mirror of
https://github.com/ratspeak/ratdeck.git
synced 2026-05-24 16:15:19 +00:00
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:
+4
-4
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user