Fix TCP announce flood: adaptive filter, TCP backpressure, aligned table caps

- Enhanced announce filter: adaptive rate (3/sec boot, 5/sec normal),
  skip re-validation of known paths unless hop count improved or 5min
  revalidation window expired
- Skip TCP reads when RNS loop exceeds 200ms to prevent UI starvation
- Lower MAX_ANNOUNCES_PER_SEC from 8 to 5
- Align build flag table caps (256/128) with runtime values
This commit is contained in:
DeFiDude
2026-03-19 15:24:31 -06:00
parent 00d04284c1
commit 45df807424
4 changed files with 50 additions and 19 deletions
+4 -4
View File
@@ -24,10 +24,10 @@ build_flags =
-DRNS_CONTAINER_ALLOCATOR=RNS_PSRAM_POOL_ALLOCATOR
-DRNS_PSRAM_POOL_BUFFER_SIZE=2048000
; Raise microReticulum table caps — all containers go to 2MB PSRAM pool
-DRNS_KNOWN_DESTINATIONS_MAX=500
-DRNS_HASHLIST_MAX=500
-DRNS_PATH_TABLE_MAX=500
-DRNS_ANNOUNCE_TABLE_MAX=500
-DRNS_KNOWN_DESTINATIONS_MAX=256
-DRNS_HASHLIST_MAX=256
-DRNS_PATH_TABLE_MAX=256
-DRNS_ANNOUNCE_TABLE_MAX=128
-DRNS_RECEIPTS_MAX=64
-DRNS_QUEUED_ANNOUNCES_MAX=64
-DRNS_RANDOM_BLOBS_MAX=128
+1 -1
View File
@@ -47,7 +47,7 @@
#define TCP_CONNECT_TIMEOUT_MS 5000
// --- Announce Flood Defense ---
#define RATDECK_MAX_ANNOUNCES_PER_SEC 8 // Transport-level rate limit (before Ed25519 verify)
#define RATDECK_MAX_ANNOUNCES_PER_SEC 5 // Transport-level rate limit (before Ed25519 verify)
// --- Limits ---
#define RATDECK_MAX_NODES 200 // PSRAM allows more
+13 -7
View File
@@ -895,12 +895,15 @@ void loop() {
}
// 4. Reticulum loop (radio RX via LoRaInterface) — throttle to ~100Hz
unsigned long rnsDuration = 0;
{
static unsigned long lastRNS = 0;
unsigned long now = millis();
if (now - lastRNS >= 10) {
lastRNS = now;
unsigned long rnsStart = millis();
rns.loop();
rnsDuration = millis() - rnsStart;
}
}
@@ -973,14 +976,17 @@ void loop() {
}
}
// 8. WiFi + TCP loops (with global budget)
if (wifiImpl) wifiImpl->loop();
// 8. WiFi + TCP loops (with global budget) — skip if RNS was overloaded
{
unsigned long tcpBudgetStart = millis();
for (auto* tcp : tcpClients) {
if (millis() - tcpBudgetStart >= TCP_GLOBAL_BUDGET_MS) break;
tcp->loop();
yield();
bool skipTcp = (rnsDuration > 200);
if (!skipTcp && wifiImpl) wifiImpl->loop();
if (!skipTcp) {
unsigned long tcpBudgetStart = millis();
for (auto* tcp : tcpClients) {
if (millis() - tcpBudgetStart >= TCP_GLOBAL_BUDGET_MS) break;
tcp->loop();
yield();
}
}
}
+32 -7
View File
@@ -3,6 +3,8 @@
#include "config/Config.h"
#include <LittleFS.h>
#include <Preferences.h>
#include <map>
#include <string>
bool LittleFSFileSystem::init() { return true; }
bool LittleFSFileSystem::file_exists(const char* p) { return LittleFS.exists(p); }
@@ -98,15 +100,38 @@ bool ReticulumManager::begin(SX1262* radio, FlashStore* flash) {
_reticulum.start();
Serial.println("[RNS] Reticulum started (Endpoint)");
// Layer 1: Transport-level announce rate limiter — filters BEFORE Ed25519 verify
// Layer 1: Transport-level announce filter — runs BEFORE Ed25519 verify
RNS::Transport::set_filter_packet_callback([](const RNS::Packet& packet) -> bool {
if (packet.packet_type() == RNS::Type::Packet::ANNOUNCE) {
static unsigned long windowStart = 0;
static unsigned int count = 0;
unsigned long now = millis();
if (now - windowStart >= 1000) { windowStart = now; count = 0; }
if (++count > RATDECK_MAX_ANNOUNCES_PER_SEC) return false;
if (packet.packet_type() != RNS::Type::Packet::ANNOUNCE) return true;
unsigned long now = millis();
// Rate limit window (per-second)
static unsigned long windowStart = 0;
static unsigned int count = 0;
if (now - windowStart >= 1000) { windowStart = now; count = 0; }
// Adaptive rate: tighter during first 60s boot flood, then normal
unsigned int maxRate = (now < 60000) ? 3 : RATDECK_MAX_ANNOUNCES_PER_SEC;
if (++count > maxRate) return false;
// Skip re-validation of known paths (saves ~100ms Ed25519 per announce)
// Allow through if hop count improved, or once per 5 min for name/ratchet updates
if (RNS::Transport::has_path(packet.destination_hash())) {
static std::map<std::string, unsigned long> lastRevalidate;
std::string destHex = packet.destination_hash().toHex();
uint8_t existingHops = RNS::Transport::hops_to(packet.destination_hash());
if (packet.hops() < existingHops) return true; // Better path, allow
auto it = lastRevalidate.find(destHex);
if (it != lastRevalidate.end() && (now - it->second) < 300000) return false;
lastRevalidate[destHex] = now;
// Cap map size to prevent unbounded growth
if (lastRevalidate.size() > 300) lastRevalidate.clear();
}
return true;
});