Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,18 @@ This document provides an overview of CLI commands that can be sent to MeshCore

---

#### View or change the advert rate limiter (Repeater Only)
**Usage:**
- `get advert.ratelimit`
- `set advert.ratelimit <state>`

**Parameters:**
- `state`: `on`|`off`

**Default:** `on`

---

#### View or change this node's advert path hash size
**Usage:**
- `get path.hash.mode`
Expand Down
6 changes: 5 additions & 1 deletion examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@ void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, ui

bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
if (_prefs.disable_fwd) return false;
if (!_prefs.disable_advert_rate_limiter
&& packet->getPayloadType() == PAYLOAD_TYPE_ADVERT
&& !advert_limiter.allow(rtc_clock.getCurrentTime())) return false;
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
if (packet->isRouteFlood() && recv_pkt_region == NULL) {
MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet");
Expand Down Expand Up @@ -848,7 +851,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_cli(board, rtc, sensors, region_map, acl, &_prefs, this),
telemetry(MAX_PACKET_PAYLOAD - 4),
discover_limiter(4, 120), // max 4 every 2 minutes
anon_limiter(4, 180) // max 4 every 3 minutes
anon_limiter(4, 180), // max 4 every 3 minutes
advert_limiter(300, 3, 5)
#if defined(WITH_RS232_BRIDGE)
, bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc)
#endif
Expand Down
1 change: 1 addition & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
RegionEntry* recv_pkt_region;
TransportKey default_scope;
RateLimiter discover_limiter, anon_limiter;
AdaptiveRateLimiter advert_limiter;
uint32_t pending_discover_tag;
unsigned long pending_discover_until;
bool region_load_active;
Expand Down
101 changes: 90 additions & 11 deletions examples/simple_repeater/RateLimiter.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,100 @@
#include <stdint.h>

class RateLimiter {
uint32_t _start_timestamp;
uint32_t _secs;
uint16_t _maximum, _count;
uint32_t _start;
uint16_t _secs;
uint16_t _maximum;
uint16_t _count;

public:
RateLimiter(uint16_t maximum, uint32_t secs): _maximum(maximum), _secs(secs), _start_timestamp(0), _count(0) { }
RateLimiter(uint16_t maximum, uint16_t secs)
: _start(0), _secs(secs), _maximum(maximum), _count(0) {}

bool allow(uint32_t now) {
if (now < _start_timestamp + _secs) {
_count++;
if (_count > _maximum) return false; // deny
} else { // time window now expired
_start_timestamp = now;
_count = 1;
if (now - _start >= _secs) {
_start = now;
_count = 0;
}

if (_count >= _maximum)
return false;

_count++;

return true;
}
};

class AdaptiveRateLimiter {
enum {
EWMA_SMOOTHING = 3,
EWMA_TOTAL_WEIGHT = EWMA_SMOOTHING + 1,
EWMA_GROWTH_CAP = 4
};

static_assert(EWMA_SMOOTHING >= 1 && EWMA_SMOOTHING <= 256, "EWMA_SMOOTHING must be 1-256");
static_assert(EWMA_GROWTH_CAP >= 1, "EWMA_GROWTH_CAP must be at least 1");

uint32_t _start;
uint16_t _secs;
uint8_t _count;
uint8_t _limit;
uint8_t _ewma;
uint8_t _burst;
uint8_t _floor;

static uint8_t clampU8(uint16_t v) { return v > 255 ? 255 : (uint8_t)v; }

uint8_t nextEwma() const {
uint8_t cap = clampU8((uint16_t)_ewma + EWMA_GROWTH_CAP);
uint8_t effective = (_count > _ewma) ? cap : _count;
uint16_t next = (uint16_t)_ewma * EWMA_SMOOTHING + effective;

return (uint8_t)(next / EWMA_TOTAL_WEIGHT);
}

uint8_t computeLimit() const {
uint8_t clamped = clampU8((uint16_t)_ewma * _burst);
return clamped > _floor ? clamped : _floor;
}

void advanceWindow(uint32_t now) {
if (now - _start < _secs)
return;

uint32_t elapsed = (_secs == 0) ? 1 : (now - _start) / _secs;

if (elapsed > EWMA_TOTAL_WEIGHT * 8)
elapsed = EWMA_TOTAL_WEIGHT * 8;

_ewma = nextEwma();
_limit = computeLimit();

while (elapsed > 1 && _ewma > 0) {
_count = 0;
_ewma = nextEwma();
_limit = computeLimit();

elapsed--;
}

_start = now;
_count = 0;
}

public:
AdaptiveRateLimiter(uint16_t secs, uint8_t burst, uint8_t floor)
: _start(0), _secs(secs), _count(0), _limit(floor), _ewma(floor),
_burst(burst), _floor(floor) {}

bool allow(uint32_t now) {
advanceWindow(now);

if (_count >= _limit)
return false;

_count++;

return true;
}
};
};
12 changes: 10 additions & 2 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.read((uint8_t *)&_prefs->disable_advert_rate_limiter, sizeof(_prefs->disable_advert_rate_limiter)); // 291
// next: 292

// sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
Expand Down Expand Up @@ -180,7 +181,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.write((uint8_t *)&_prefs->disable_advert_rate_limiter, sizeof(_prefs->disable_advert_rate_limiter)); // 291
// next: 292

file.close();
}
Expand Down Expand Up @@ -547,6 +549,10 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0;
savePrefs();
strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON");
} else if (memcmp(config, "advert.ratelimit ", 17) == 0) {
_prefs->disable_advert_rate_limiter = memcmp(&config[17], "off", 3) == 0;
savePrefs();
strcpy(reply, _prefs->disable_advert_rate_limiter ? "OK - advert rate limiter OFF" : "OK - advert rate limiter ON");
#if defined(USE_SX1262) || defined(USE_SX1268)
} else if (memcmp(config, "radio.rxgain ", 13) == 0) {
_prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0;
Expand Down Expand Up @@ -765,6 +771,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", _prefs->node_name);
} else if (memcmp(config, "repeat", 6) == 0) {
sprintf(reply, "> %s", _prefs->disable_fwd ? "off" : "on");
} else if (memcmp(config, "advert.ratelimit", 16) == 0) {
sprintf(reply, "> %s", _prefs->disable_advert_rate_limiter ? "off" : "on");
} else if (memcmp(config, "lat", 3) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lat));
} else if (memcmp(config, "lon", 3) == 0) {
Expand Down
1 change: 1 addition & 0 deletions src/helpers/CommonCLI.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct NodePrefs { // persisted to file
uint8_t rx_boosted_gain; // power settings
uint8_t path_hash_mode; // which path mode to use when sending
uint8_t loop_detect;
uint8_t disable_advert_rate_limiter;
};

class CommonCLICallbacks {
Expand Down