From d93746e257a9391f3239659f03006c05872d4cdb Mon Sep 17 00:00:00 2001 From: Dale Ruane Date: Tue, 14 Apr 2026 10:26:18 +0100 Subject: [PATCH] feat: make region auto-tagging opt-in with configurable hop limit Adds two new repeater prefs to give admins explicit control over VLAN-style region tagging of untagged flood packets: region.autotag on|off (default: off) region.autotag.max.hops 0-8 (default: 1) Auto-tagging now only occurs when the setting is enabled AND the received packet's path hash count is <= the configured max hops. This prevents mis-tagging distant-origin floods that arrived via untagged / older-firmware repeaters, and ensures home-region selection is a deliberate admin decision rather than an implicit side-effect of configuring a home region. --- examples/simple_repeater/MyMesh.cpp | 7 +++++-- src/helpers/CommonCLI.cpp | 27 +++++++++++++++++++++++++-- src/helpers/CommonCLI.h | 2 ++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 0c8318f7..0fb486b5 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -543,9 +543,10 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD); } } else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) { - // untagged packet: tag with home region if one is configured + // untagged packet: tag with home region if auto-tagging is enabled and a home region is configured RegionEntry* home = region_map.getHomeRegion(); - if (home && home->id != 0) { + if (_prefs.region_autotag && home && home->id != 0 + && pkt->getPathHashCount() <= _prefs.region_autotag_max_hops) { // calculate transport code for home region and stamp onto packet TransportKey key; if (home->name[0] == '$') { @@ -901,6 +902,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.advert_interval = 1; // default to 2 minutes for NEW installs _prefs.flood_advert_interval = 12; // 12 hours _prefs.flood_max = 64; + _prefs.region_autotag = 0; // opt-in, default off + _prefs.region_autotag_max_hops = 1; // only tag zero-hop / 1-hop packets by default _prefs.interference_threshold = 0; // disabled // bridge defaults diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 2f7a0fff..3c91af21 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -87,7 +87,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 - // next: 290 + file.read((uint8_t *)&_prefs->region_autotag, sizeof(_prefs->region_autotag)); // 290 + file.read((uint8_t *)&_prefs->region_autotag_max_hops, sizeof(_prefs->region_autotag_max_hops)); // 291 + // next: 292 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -117,6 +119,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean + _prefs->region_autotag = constrain(_prefs->region_autotag, 0, 1); // boolean + _prefs->region_autotag_max_hops = constrain(_prefs->region_autotag_max_hops, 0, 8); file.close(); } @@ -177,7 +181,9 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 - // next: 290 + file.write((uint8_t *)&_prefs->region_autotag, sizeof(_prefs->region_autotag)); // 290 + file.write((uint8_t *)&_prefs->region_autotag_max_hops, sizeof(_prefs->region_autotag_max_hops)); // 291 + // next: 292 file.close(); } @@ -349,6 +355,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch *reply = 0; // set null terminator } else if (memcmp(config, "path.hash.mode", 14) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->path_hash_mode); + } else if (memcmp(config, "region.autotag.max.hops", 23) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->region_autotag_max_hops); + } else if (memcmp(config, "region.autotag", 14) == 0) { + sprintf(reply, "> %s", _prefs->region_autotag ? "on" : "off"); } else if (memcmp(config, "loop.detect", 11) == 0) { if (_prefs->loop_detect == LOOP_DETECT_OFF) { strcpy(reply, "> off"); @@ -597,6 +607,19 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch *dp = 0; savePrefs(); strcpy(reply, "OK"); + } else if (memcmp(config, "region.autotag.max.hops ", 24) == 0) { + int h = atoi(&config[24]); + if (h >= 0 && h <= 8) { + _prefs->region_autotag_max_hops = (uint8_t)h; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, range is 0-8"); + } + } else if (memcmp(config, "region.autotag ", 15) == 0) { + _prefs->region_autotag = memcmp(&config[15], "on", 2) == 0; + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "path.hash.mode ", 15) == 0) { config += 15; uint8_t mode = atoi(config); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 3a4332d1..f0bd2aba 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -60,6 +60,8 @@ 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 region_autotag; // boolean: auto-tag untagged flood packets with home region's transport code + uint8_t region_autotag_max_hops; // only auto-tag packets received with pathHashCount <= this value (0 = zero-hop only) }; class CommonCLICallbacks {