|
|
|
@ -413,6 +413,19 @@ bool MyMesh::isLooped(const mesh::Packet* packet, const uint8_t max_counters[]) |
|
|
|
return n >= max_counters[hash_size]; |
|
|
|
} |
|
|
|
|
|
|
|
void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) { |
|
|
|
if (recv_pkt_region && !recv_pkt_region->isWildcard()) { // if _request_ packet scope is known, send reply with same scope
|
|
|
|
TransportKey scope; |
|
|
|
if (region_map.getTransportKeysFor(*recv_pkt_region, &scope, 1) > 0) { |
|
|
|
sendFloodScoped(scope, packet, delay_millis, path_hash_size); |
|
|
|
} else { |
|
|
|
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
|
|
|
|
} |
|
|
|
} else { |
|
|
|
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool MyMesh::allowPacketForward(const mesh::Packet *packet) { |
|
|
|
if (_prefs.disable_fwd) return false; |
|
|
|
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false; |
|
|
|
@ -578,10 +591,10 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m |
|
|
|
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
|
|
|
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len, |
|
|
|
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); |
|
|
|
if (path) sendFlood(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); |
|
|
|
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); |
|
|
|
} else if (reply_path_len < 0) { |
|
|
|
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len); |
|
|
|
if (reply) sendFlood(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); |
|
|
|
if (reply) sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); |
|
|
|
} else { |
|
|
|
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len); |
|
|
|
uint8_t path_len = ((reply_path_hash_size - 1) << 6) | (reply_path_len & 63); |
|
|
|
@ -654,7 +667,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, |
|
|
|
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
|
|
|
mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len, |
|
|
|
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); |
|
|
|
if (path) sendFlood(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); |
|
|
|
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); |
|
|
|
} else { |
|
|
|
mesh::Packet *reply = |
|
|
|
createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); |
|
|
|
@ -662,7 +675,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, |
|
|
|
if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT
|
|
|
|
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); |
|
|
|
} else { |
|
|
|
sendFlood(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); |
|
|
|
sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -693,7 +706,7 @@ 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) { |
|
|
|
sendFlood(ack, TXT_ACK_DELAY, packet->getPathHashSize()); |
|
|
|
sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); |
|
|
|
} else { |
|
|
|
sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); |
|
|
|
} |
|
|
|
@ -721,7 +734,7 @@ 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) { |
|
|
|
sendFlood(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); |
|
|
|
sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); |
|
|
|
} else { |
|
|
|
sendDirect(reply, client->out_path, client->out_path_len, CLI_REPLY_DELAY_MILLIS); |
|
|
|
} |
|
|
|
@ -831,7 +844,9 @@ void MyMesh::sendNodeDiscoverReq() { |
|
|
|
MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, |
|
|
|
mesh::RTCClock &rtc, mesh::MeshTables &tables) |
|
|
|
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), |
|
|
|
_cli(board, rtc, sensors, acl, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store), temp_map(key_store), |
|
|
|
region_map(key_store), temp_map(key_store), |
|
|
|
_cli(board, rtc, sensors, region_map, acl, &_prefs, this), |
|
|
|
telemetry(MAX_PACKET_PAYLOAD - 4), |
|
|
|
discover_limiter(4, 120), // max 4 every 2 minutes
|
|
|
|
anon_limiter(4, 180) // max 4 every 3 minutes
|
|
|
|
#if defined(WITH_RS232_BRIDGE) |
|
|
|
@ -899,6 +914,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc |
|
|
|
|
|
|
|
pending_discover_tag = 0; |
|
|
|
pending_discover_until = 0; |
|
|
|
|
|
|
|
memset(default_scope.key, 0, sizeof(default_scope.key)); |
|
|
|
} |
|
|
|
|
|
|
|
void MyMesh::begin(FILESYSTEM *fs) { |
|
|
|
@ -910,6 +927,26 @@ void MyMesh::begin(FILESYSTEM *fs) { |
|
|
|
// TODO: key_store.begin();
|
|
|
|
region_map.load(_fs); |
|
|
|
|
|
|
|
// establish default-scope
|
|
|
|
{ |
|
|
|
RegionEntry* r = region_map.getDefaultRegion(); |
|
|
|
if (r) { |
|
|
|
region_map.getTransportKeysFor(*r, &default_scope, 1); |
|
|
|
} else { |
|
|
|
#ifdef DEFAULT_FLOOD_SCOPE_NAME |
|
|
|
r = region_map.findByName(DEFAULT_FLOOD_SCOPE_NAME); |
|
|
|
if (r == NULL) { |
|
|
|
r = region_map.putRegion(DEFAULT_FLOOD_SCOPE_NAME, 0); // auto-create the default scope region
|
|
|
|
if (r) { r->flags = 0; } // Allow-flood
|
|
|
|
} |
|
|
|
if (r) { |
|
|
|
region_map.setDefaultRegion(r); |
|
|
|
region_map.getTransportKeysFor(*r, &default_scope, 1); |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#if defined(WITH_BRIDGE) |
|
|
|
if (_prefs.bridge_enabled) { |
|
|
|
bridge.begin(); |
|
|
|
@ -933,6 +970,17 @@ void MyMesh::begin(FILESYSTEM *fs) { |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis, uint8_t path_hash_size) { |
|
|
|
if (scope.isNull()) { |
|
|
|
sendFlood(pkt, delay_millis, path_hash_size); |
|
|
|
} else { |
|
|
|
uint16_t codes[2]; |
|
|
|
codes[0] = scope.calcTransportCode(pkt); |
|
|
|
codes[1] = 0; // REVISIT: set to 'home' Region, for sender/return region?
|
|
|
|
sendFlood(pkt, codes, delay_millis, path_hash_size); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { |
|
|
|
set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params
|
|
|
|
pending_freq = freq; |
|
|
|
@ -960,7 +1008,7 @@ void MyMesh::sendSelfAdvertisement(int delay_millis, bool flood) { |
|
|
|
mesh::Packet *pkt = createSelfAdvert(); |
|
|
|
if (pkt) { |
|
|
|
if (flood) { |
|
|
|
sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1); |
|
|
|
sendFloodScoped(default_scope, pkt, delay_millis, _prefs.path_hash_mode + 1); |
|
|
|
} else { |
|
|
|
sendZeroHop(pkt, delay_millis); |
|
|
|
} |
|
|
|
@ -1066,6 +1114,25 @@ void MyMesh::removeNeighbor(const uint8_t *pubkey, int key_len) { |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void MyMesh::startRegionsLoad() { |
|
|
|
temp_map.resetFrom(region_map); // rebuild regions in a temp instance
|
|
|
|
memset(load_stack, 0, sizeof(load_stack)); |
|
|
|
load_stack[0] = &temp_map.getWildcard(); |
|
|
|
region_load_active = true; |
|
|
|
} |
|
|
|
|
|
|
|
bool MyMesh::saveRegions() { |
|
|
|
return region_map.save(_fs); |
|
|
|
} |
|
|
|
|
|
|
|
void MyMesh::onDefaultRegionChanged(const RegionEntry* r) { |
|
|
|
if (r) { |
|
|
|
region_map.getTransportKeysFor(*r, &default_scope, 1); |
|
|
|
} else { |
|
|
|
memset(default_scope.key, 0, sizeof(default_scope.key)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void MyMesh::formatStatsReply(char *reply) { |
|
|
|
StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr); |
|
|
|
} |
|
|
|
@ -1175,107 +1242,6 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply |
|
|
|
Serial.printf("\n"); |
|
|
|
} |
|
|
|
reply[0] = 0; |
|
|
|
} else if (memcmp(command, "region", 6) == 0) { |
|
|
|
reply[0] = 0; |
|
|
|
|
|
|
|
const char* parts[4]; |
|
|
|
int n = mesh::Utils::parseTextParts(command, parts, 4, ' '); |
|
|
|
if (n == 1) { |
|
|
|
region_map.exportTo(reply, 160); |
|
|
|
} else if (n >= 2 && strcmp(parts[1], "load") == 0) { |
|
|
|
temp_map.resetFrom(region_map); // rebuild regions in a temp instance
|
|
|
|
memset(load_stack, 0, sizeof(load_stack)); |
|
|
|
load_stack[0] = &temp_map.getWildcard(); |
|
|
|
region_load_active = true; |
|
|
|
} else if (n >= 2 && strcmp(parts[1], "save") == 0) { |
|
|
|
_prefs.discovery_mod_timestamp = rtc_clock.getCurrentTime(); // this node is now 'modified' (for discovery info)
|
|
|
|
savePrefs(); |
|
|
|
bool success = region_map.save(_fs); |
|
|
|
strcpy(reply, success ? "OK" : "Err - save failed"); |
|
|
|
} else if (n >= 3 && strcmp(parts[1], "allowf") == 0) { |
|
|
|
auto region = region_map.findByNamePrefix(parts[2]); |
|
|
|
if (region) { |
|
|
|
region->flags &= ~REGION_DENY_FLOOD; |
|
|
|
strcpy(reply, "OK"); |
|
|
|
} else { |
|
|
|
strcpy(reply, "Err - unknown region"); |
|
|
|
} |
|
|
|
} else if (n >= 3 && strcmp(parts[1], "denyf") == 0) { |
|
|
|
auto region = region_map.findByNamePrefix(parts[2]); |
|
|
|
if (region) { |
|
|
|
region->flags |= REGION_DENY_FLOOD; |
|
|
|
strcpy(reply, "OK"); |
|
|
|
} else { |
|
|
|
strcpy(reply, "Err - unknown region"); |
|
|
|
} |
|
|
|
} else if (n >= 3 && strcmp(parts[1], "get") == 0) { |
|
|
|
auto region = region_map.findByNamePrefix(parts[2]); |
|
|
|
if (region) { |
|
|
|
auto parent = region_map.findById(region->parent); |
|
|
|
if (parent && parent->id != 0) { |
|
|
|
sprintf(reply, " %s (%s) %s", region->name, parent->name, (region->flags & REGION_DENY_FLOOD) ? "" : "F"); |
|
|
|
} else { |
|
|
|
sprintf(reply, " %s %s", region->name, (region->flags & REGION_DENY_FLOOD) ? "" : "F"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
strcpy(reply, "Err - unknown region"); |
|
|
|
} |
|
|
|
} else if (n >= 3 && strcmp(parts[1], "home") == 0) { |
|
|
|
auto home = region_map.findByNamePrefix(parts[2]); |
|
|
|
if (home) { |
|
|
|
region_map.setHomeRegion(home); |
|
|
|
sprintf(reply, " home is now %s", home->name); |
|
|
|
} else { |
|
|
|
strcpy(reply, "Err - unknown region"); |
|
|
|
} |
|
|
|
} else if (n == 2 && strcmp(parts[1], "home") == 0) { |
|
|
|
auto home = region_map.getHomeRegion(); |
|
|
|
sprintf(reply, " home is %s", home ? home->name : "*"); |
|
|
|
} else if (n >= 3 && strcmp(parts[1], "put") == 0) { |
|
|
|
auto parent = n >= 4 ? region_map.findByNamePrefix(parts[3]) : ®ion_map.getWildcard(); |
|
|
|
if (parent == NULL) { |
|
|
|
strcpy(reply, "Err - unknown parent"); |
|
|
|
} else { |
|
|
|
auto region = region_map.putRegion(parts[2], parent->id); |
|
|
|
if (region == NULL) { |
|
|
|
strcpy(reply, "Err - unable to put"); |
|
|
|
} else { |
|
|
|
strcpy(reply, "OK"); |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (n >= 3 && strcmp(parts[1], "remove") == 0) { |
|
|
|
auto region = region_map.findByName(parts[2]); |
|
|
|
if (region) { |
|
|
|
if (region_map.removeRegion(*region)) { |
|
|
|
strcpy(reply, "OK"); |
|
|
|
} else { |
|
|
|
strcpy(reply, "Err - not empty"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
strcpy(reply, "Err - not found"); |
|
|
|
} |
|
|
|
} else if (n >= 3 && strcmp(parts[1], "list") == 0) { |
|
|
|
uint8_t mask = 0; |
|
|
|
bool invert = false; |
|
|
|
|
|
|
|
if (strcmp(parts[2], "allowed") == 0) { |
|
|
|
mask = REGION_DENY_FLOOD; |
|
|
|
invert = false; // list regions that DON'T have DENY flag
|
|
|
|
} else if (strcmp(parts[2], "denied") == 0) { |
|
|
|
mask = REGION_DENY_FLOOD; |
|
|
|
invert = true; // list regions that DO have DENY flag
|
|
|
|
} else { |
|
|
|
strcpy(reply, "Err - use 'allowed' or 'denied'"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
int len = region_map.exportNamesTo(reply, 160, mask, invert); |
|
|
|
if (len == 0) { |
|
|
|
strcpy(reply, "-none-"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
strcpy(reply, "Err - ??"); |
|
|
|
} |
|
|
|
} else if (memcmp(command, "discover.neighbors", 18) == 0) { |
|
|
|
const char* sub = command + 18; |
|
|
|
while (*sub == ' ') sub++; |
|
|
|
@ -1300,7 +1266,7 @@ void MyMesh::loop() { |
|
|
|
if (next_flood_advert && millisHasNowPassed(next_flood_advert)) { |
|
|
|
mesh::Packet *pkt = createSelfAdvert(); |
|
|
|
uint32_t delay_millis = 0; |
|
|
|
if (pkt) sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1); |
|
|
|
if (pkt) sendFloodScoped(default_scope, pkt, delay_millis, _prefs.path_hash_mode + 1); |
|
|
|
|
|
|
|
updateFloodAdvertTimer(); // schedule next flood advert
|
|
|
|
updateAdvertTimer(); // also schedule local advert (so they don't overlap)
|
|
|
|
|