From 60429c9b4b7bf3c84e07a161386aec00dd06fca3 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 18 May 2026 16:37:45 -0700 Subject: [PATCH] Add repeater outpath flood mode --- docs/cli_commands.md | 3 +++ examples/simple_repeater/MyMesh.cpp | 32 +++++++++++++++++++---------- src/helpers/ClientACL.h | 3 ++- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index bab45c11f..38fbebf6f 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -750,6 +750,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `get outpath` - `set outpath ` - `set outpath clear` +- `set outpath flood` **Parameters:** - `hopN_hex`: Hop hash, `2`, `4`, or `6` hex characters. All hops must use the same width. @@ -758,6 +759,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore - These commands require remote client context (they target the caller's ACL entry). - The path hash size is inferred from the hop hash width. - `outpath` overrides the primary direct route used for replies to the caller. +- `clear` forgets the current direct path and allows normal path discovery to repopulate it. +- `flood` forces replies to use flood packets and ignores later discovered direct paths. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index fb03764fe..4c25d9a44 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -128,7 +128,7 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr } } - if (is_flood) { + if (is_flood && client->out_path_len != OUT_PATH_FORCE_FLOOD) { client->out_path_len = OUT_PATH_UNKNOWN; // need to rediscover out_path } @@ -672,7 +672,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); if (reply) { - if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT + if (mesh::Packet::isValidPathLen(client->out_path_len)) { // we have an out_path, so send DIRECT sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); @@ -705,10 +705,10 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, mesh::Packet *ack = createAck(ack_hash); if (ack) { - if (client->out_path_len == OUT_PATH_UNKNOWN) { - sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); - } else { + if (mesh::Packet::isValidPathLen(client->out_path_len)) { sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); + } else { + sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); } } } @@ -733,10 +733,10 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len); if (reply) { - if (client->out_path_len == OUT_PATH_UNKNOWN) { - sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); - } else { + if (mesh::Packet::isValidPathLen(client->out_path_len)) { sendDirect(reply, client->out_path, client->out_path_len, CLI_REPLY_DELAY_MILLIS); + } else { + sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); } } } @@ -756,7 +756,9 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t auto client = acl.getClientByIdx(i); // store a copy of path, for sendDirect() - client->out_path_len = mesh::Packet::copyPath(client->out_path, path, path_len); + if (client->out_path_len != OUT_PATH_FORCE_FLOOD) { + client->out_path_len = mesh::Packet::copyPath(client->out_path, path, path_len); + } client->last_activity = getRTCClock()->getCurrentTime(); } else { MESH_DEBUG_PRINTLN("onPeerPathRecv: invalid peer idx: %d", i); @@ -1188,6 +1190,10 @@ static bool parsePathCommand(char* raw, uint8_t* out_path, uint8_t& out_path_len out_path_len = OUT_PATH_UNKNOWN; return true; } + if (strcmp(spec, "flood") == 0) { + out_path_len = OUT_PATH_FORCE_FLOOD; + return true; + } uint8_t hash_size = 0; uint8_t hop_count = 0; @@ -1233,6 +1239,10 @@ static bool parsePathCommand(char* raw, uint8_t* out_path, uint8_t& out_path_len } static void formatPathReply(const uint8_t* path, uint8_t path_len, char* out, size_t out_len) { + if (path_len == OUT_PATH_FORCE_FLOOD) { + snprintf(out, out_len, "> flood"); + return; + } if (path_len == OUT_PATH_UNKNOWN) { snprintf(out, out_len, "> unknown"); return; @@ -1345,9 +1355,9 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * if (!parsePathCommand(spec, path, path_len, err)) { strcpy(reply, err ? err : "Err - invalid path"); } else { - if (path_len == OUT_PATH_UNKNOWN) { - sender->out_path_len = OUT_PATH_UNKNOWN; + if (path_len == OUT_PATH_UNKNOWN || path_len == OUT_PATH_FORCE_FLOOD) { memset(sender->out_path, 0, sizeof(sender->out_path)); + sender->out_path_len = path_len; } else { sender->out_path_len = mesh::Packet::copyPath(sender->out_path, path, path_len); } diff --git a/src/helpers/ClientACL.h b/src/helpers/ClientACL.h index b758f7068..e0b8c5428 100644 --- a/src/helpers/ClientACL.h +++ b/src/helpers/ClientACL.h @@ -10,7 +10,8 @@ #define PERM_ACL_READ_WRITE 2 #define PERM_ACL_ADMIN 3 -#define OUT_PATH_UNKNOWN 0xFF +#define OUT_PATH_FORCE_FLOOD 0xFE +#define OUT_PATH_UNKNOWN 0xFF struct ClientInfo { mesh::Identity id;