Browse Source

Merge c602e4989a into e8d3c53ba1

pull/2586/merge
mikecarper 19 hours ago
committed by GitHub
parent
commit
28e3645597
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 21
      docs/cli_commands.md
  2. 151
      examples/simple_repeater/MyMesh.cpp
  3. 2
      examples/simple_repeater/MyMesh.h
  4. 2
      examples/simple_repeater/main.cpp
  5. 3
      src/helpers/ClientACL.h

21
docs/cli_commands.md

@ -776,6 +776,27 @@ This document provides an overview of CLI commands that can be sent to MeshCore
---
#### View or set the direct path override for the current remote client
**Usage:**
- `get outpath`
- `set outpath <hop1_hex,hop2_hex,...>`
- `set outpath direct`
- `set outpath clear`
- `set outpath flood`
**Parameters:**
- `hopN_hex`: Hop hash, `2`, `4`, or `6` hex characters. All hops must use the same width.
**Notes:**
- 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.
- `direct` sets a zero-hop direct route for a caller reachable without repeaters.
- `clear` forgets the current direct path and allows normal path discovery to repopulate it.
- `flood` forces replies to use flood packets until the client logs in again.
---
#### Create a new region
**Usage:**
- `region put <name> [parent_name]`

151
examples/simple_repeater/MyMesh.cpp

@ -676,7 +676,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());
@ -709,10 +709,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());
}
}
}
@ -723,7 +723,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
if (is_retry) {
*reply = 0;
} else {
handleCommand(sender_timestamp, command, reply);
handleCommand(sender_timestamp, client, command, reply);
}
int text_len = strlen(reply);
if (text_len > 0) {
@ -737,10 +737,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());
}
}
}
@ -760,7 +760,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);
@ -1171,7 +1173,108 @@ void MyMesh::clearStats() {
((SimpleMeshTables *)getTables())->resetStats();
}
void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) {
static char* trimSpaces(char* s) {
while (*s == ' ') s++;
char* end = s + strlen(s);
while (end > s && end[-1] == ' ') end--;
*end = 0;
return s;
}
static bool parsePathCommand(char* raw, uint8_t* out_path, uint8_t& out_path_len, const char*& err) {
if (raw == NULL || out_path == NULL) {
err = "Err - bad params";
return false;
}
char* spec = trimSpaces(raw);
if (*spec == 0) {
err = "Err - missing path";
return false;
}
if (strcmp(spec, "clear") == 0 || strcmp(spec, "-") == 0 || strcmp(spec, "none") == 0) {
out_path_len = OUT_PATH_UNKNOWN;
return true;
}
if (strcmp(spec, "flood") == 0) {
out_path_len = OUT_PATH_FORCE_FLOOD;
return true;
}
if (strcmp(spec, "direct") == 0) {
out_path_len = 0;
return true;
}
uint8_t hash_size = 0;
uint8_t hop_count = 0;
char* token = spec;
while (token && *token) {
char* comma = strchr(token, ',');
if (comma) *comma = 0;
token = trimSpaces(token);
int hex_len = strlen(token);
if (!(hex_len == 2 || hex_len == 4 || hex_len == 6)) {
err = "Err - bad params";
return false;
}
uint8_t hop_hash_size = (uint8_t)(hex_len / 2);
if (hash_size == 0) {
hash_size = hop_hash_size;
} else if (hash_size != hop_hash_size) {
err = "Err - bad params";
return false;
}
if (hop_count >= 63 || (hop_count + 1) * hash_size > MAX_PATH_SIZE) {
err = "Err - bad params";
return false;
}
if (!mesh::Utils::fromHex(&out_path[hop_count * hash_size], hash_size, token)) {
err = "Err - bad hex";
return false;
}
hop_count++;
token = comma ? comma + 1 : NULL;
}
if (hash_size == 0 || hop_count == 0) {
err = "Err - missing path";
return false;
}
out_path_len = ((hash_size - 1) << 6) | (hop_count & 63);
return true;
}
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;
}
if (!mesh::Packet::isValidPathLen(path_len)) {
snprintf(out, out_len, "> invalid");
return;
}
if ((path_len & 63) == 0) {
snprintf(out, out_len, "> direct");
return;
}
uint8_t hash_size = (path_len >> 6) + 1;
uint8_t hop_count = path_len & 63;
uint8_t byte_len = hop_count * hash_size;
char hex[(MAX_PATH_SIZE * 2) + 1];
mesh::Utils::toHex(hex, path, byte_len);
snprintf(out, out_len, "> hs=%u hops=%u hex=%s", (uint32_t)hash_size, (uint32_t)hop_count, hex);
}
void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *command, char *reply) {
if (region_load_active) {
if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation
region_map = temp_map; // copy over the temp instance as new current map
@ -1248,6 +1351,34 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
Serial.printf("\n");
}
reply[0] = 0;
} else if (strcmp(command, "get outpath") == 0
|| strcmp(command, "set outpath") == 0
|| strncmp(command, "set outpath ", 12) == 0) {
bool is_get = strncmp(command, "get ", 4) == 0;
if (sender == NULL) {
strcpy(reply, "Err - command needs remote client context");
} else if (is_get) {
formatPathReply(sender->out_path, sender->out_path_len, reply, 160);
} else {
char* spec = command + 11; // length of "set outpath"
if (*spec == ' ') spec++;
uint8_t path[MAX_PATH_SIZE];
uint8_t path_len = OUT_PATH_UNKNOWN;
const char* err = NULL;
if (!parsePathCommand(spec, path, path_len, err)) {
strcpy(reply, err ? err : "Err - invalid path");
} else {
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);
}
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
formatPathReply(sender->out_path, sender->out_path_len, reply, 160);
}
}
} else if (memcmp(command, "discover.neighbors", 18) == 0) {
const char* sub = command + 18;
while (*sub == ' ') sub++;

2
examples/simple_repeater/MyMesh.h

@ -223,7 +223,7 @@ public:
void saveIdentity(const mesh::LocalIdentity& new_id) override;
void clearStats() override;
void handleCommand(uint32_t sender_timestamp, char* command, char* reply);
void handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char* command, char* reply);
void loop();
#if defined(WITH_BRIDGE)

2
examples/simple_repeater/main.cpp

@ -122,7 +122,7 @@ void loop() {
Serial.print('\n');
command[len - 1] = 0; // replace newline with C string null terminator
char reply[160];
the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial!
the_mesh.handleCommand(0, NULL, command, reply); // NOTE: there is no sender_timestamp via serial!
if (reply[0]) {
Serial.print(" -> "); Serial.println(reply);
}

3
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;

Loading…
Cancel
Save