From 906f6fa7fa5e2bf418610cefa437aa621f277083 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 20 Oct 2026 18:23:05 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BB=D0=B8=D0=BC=D0=B8=D1=82=20=D1=84?= =?UTF-8?q?=D0=BB=D1=83=D0=B4=D0=B0=20=D0=B0=D0=BD=D0=BE=D0=BD=D1=81=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8=20=D0=B8=20=D0=B1=D1=8E=D0=B4=D0=B6=D0=B5=D1=82=20?= =?UTF-8?q?=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB=D0=B0;=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D0=BE=D0=B4=20=D0=B2=D0=B5=D0=B1-=D0=BF=D0=B0?= =?UTF-8?q?=D0=BD=D0=B5=D0=BB=D0=B8=20=D0=BD=D0=B0=20=D1=80=D1=83=D1=81?= =?UTF-8?q?=D1=81=D0=BA=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/simple_repeater/MyMesh.cpp | 25 ++ examples/simple_repeater/MyMesh.h | 12 + src/helpers/CommonCLI.cpp | 15 + src/helpers/CommonCLI.h | 6 + src/helpers/web/WebPanelServer.cpp | 530 ++++++++++++++-------------- 5 files changed, 323 insertions(+), 265 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index c04d95c2..e562d48e 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1216,6 +1216,31 @@ mesh::DispatcherAction MyMesh::onRecvPacket(mesh::Packet* pkt) { MESH_DEBUG_PRINTLN("onRecvPacket: channel hash blacklisted, dropping!"); return ACTION_RELEASE; } +#if defined(ESP32) + // Лимит флуда анонсами: слишком частые анонсы от одного отправителя не пересылаем + if (_prefs.advert_jail && pkt->getPayloadType() == PAYLOAD_TYPE_ADVERT && pkt->payload_len >= 36) { + uint32_t advert_time; + memcpy(&advert_time, &pkt->payload[32], 4); + const uint32_t interval_ms = _prefs.advert_interval + ? (uint32_t)_prefs.advert_interval * 2UL * 60UL * 1000UL // pref хранит минуты / 2 + : 60UL * 60UL * 1000UL; + if (mesh::checkAdvertJail(_advert_jail, ADVERT_JAIL_TABLE_SIZE, pkt->payload, advert_time, + (uint32_t)millis(), interval_ms)) { + MESH_DEBUG_PRINTLN("onRecvPacket: advert jail, dropping!"); + return ACTION_RELEASE; + } + } + // Бюджет эфира на канал для групповых flood-пакетов + if (_prefs.channel_budget + && (pkt->getPayloadType() == PAYLOAD_TYPE_GRP_TXT || pkt->getPayloadType() == PAYLOAD_TYPE_GRP_DATA) + && pkt->payload_len > 0) { + if (mesh::checkChannelBudget(_chan_budget, pkt->payload[0], (uint32_t)millis(), + _prefs.channel_budget, CHAN_BUDGET_WINDOW_MS)) { + MESH_DEBUG_PRINTLN("onRecvPacket: channel budget exceeded, dropping!"); + return ACTION_RELEASE; + } + } +#endif } return Mesh::onRecvPacket(pkt); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 81dd353d..52b13c1e 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -38,6 +38,13 @@ #include #include +#include +#include + +// Сколько отправителей помним в таблице лимита анонсов (24 байта на запись) +#ifndef ADVERT_JAIL_TABLE_SIZE + #define ADVERT_JAIL_TABLE_SIZE 24 +#endif #include #include #include @@ -180,6 +187,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public WebPanelComm unsigned long dirty_contacts_expiry; BlacklistEntry _path_blacklist[MAX_BLACKLIST_ENTRIES]; BlacklistEntry _chan_blacklist[MAX_BLACKLIST_ENTRIES]; + // Защита сети: таблица лимита анонсов и бюджет эфира на канал (около 1,6 КБ на ESP32) +#if defined(ESP32) + AdvertJailEntry _advert_jail[ADVERT_JAIL_TABLE_SIZE]; + uint32_t _chan_budget[CHAN_BUDGET_CHANNELS]; +#endif ChanNameFilter _chan_name_filters[MAX_CHAN_NAME_FILTERS]; int _num_chan_name_filters; #if MAX_NEIGHBOURS diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index a7893009..6a17e84b 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -605,6 +605,21 @@ 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.jail ", 12) == 0) { + _prefs->advert_jail = memcmp(&config[12], "on", 2) == 0 ? 1 : 0; + savePrefs(); + strcpy(reply, _prefs->advert_jail ? "OK - advert jail is now ON" : "OK - advert jail is now OFF"); + } else if (memcmp(config, "channel.budget ", 15) == 0) { + int budget = atoi(&config[15]); + if (budget < 0) budget = 0; + if (budget > 1000) budget = 1000; + _prefs->channel_budget = (uint16_t)budget; + savePrefs(); + if (budget == 0) { + strcpy(reply, "OK - channel budget is now OFF"); + } else { + sprintf(reply, "OK - channel budget is now %d packets per 10 min", budget); + } } else if (memcmp(config, "lat ", 4) == 0) { _prefs->node_lat = atof(&config[4]); savePrefs(); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 6fba1844..ae2a252f 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -32,6 +32,10 @@ public: float freq = 0; int8_t tx_power_dbm = 0; uint8_t disable_fwd = 0; + // Защита сети: лимит флуда анонсами (1 = включён) и бюджет эфира на канал + // в пакетах за 10 минут (0 = выключен) + uint8_t advert_jail = 1; + uint16_t channel_budget = 0; uint8_t advert_interval = 0; // minutes / 2 uint8_t flood_advert_interval = 0; // hours float rx_delay_base = 0; @@ -194,6 +198,8 @@ private: protected: void structure() override { def("disable", _parent->disable_fwd); + def("advert_jail", _parent->advert_jail); + def("channel_budget", _parent->channel_budget); def("f_max", _parent->flood_max); def("f_max_uns", _parent->flood_max_unscoped); def("f_max_adv", _parent->flood_max_advert); diff --git a/src/helpers/web/WebPanelServer.cpp b/src/helpers/web/WebPanelServer.cpp index 83198865..72c70dc4 100644 --- a/src/helpers/web/WebPanelServer.cpp +++ b/src/helpers/web/WebPanelServer.cpp @@ -280,7 +280,7 @@ const char kWebPanelLoginHtml[] PROGMEM = R"HTML( - Repeater Login + Вход в панель репитера