mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-03-30 19:15:49 +00:00
* Mesh.cpp: optimisation to not retransmit packets handled by this node
* simple_repeater: now supports a remote CLI
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
#include <helpers/StaticPoolPacketManager.h>
|
||||
#include <helpers/SimpleMeshTables.h>
|
||||
#include <helpers/IdentityStore.h>
|
||||
#include <RTClib.h>
|
||||
|
||||
/* ------------------------------ Config -------------------------------- */
|
||||
|
||||
@@ -262,39 +263,81 @@ protected:
|
||||
}
|
||||
|
||||
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override {
|
||||
if (type == PAYLOAD_TYPE_REQ) { // request (from a Known admin client!)
|
||||
int i = matching_peer_indexes[sender_idx];
|
||||
int i = matching_peer_indexes[sender_idx];
|
||||
if (i < 0 || i >= num_clients) { // get from our known_clients table (sender SHOULD already be known in this context)
|
||||
MESH_DEBUG_PRINTLN("onPeerDataRecv: invalid peer idx: %d", i);
|
||||
return;
|
||||
}
|
||||
auto client = &known_clients[i];
|
||||
if (type == PAYLOAD_TYPE_REQ) { // request (from a Known admin client!)
|
||||
uint32_t timestamp;
|
||||
memcpy(×tamp, data, 4);
|
||||
|
||||
if (i >= 0 && i < num_clients) { // get from our known_clients table (sender SHOULD already be known in this context)
|
||||
auto client = &known_clients[i];
|
||||
if (timestamp > client->last_timestamp) { // prevent replay attacks
|
||||
int reply_len = handleRequest(client, &data[4], len - 4);
|
||||
if (reply_len == 0) return; // invalid command
|
||||
|
||||
uint32_t timestamp;
|
||||
memcpy(×tamp, data, 4);
|
||||
client->last_timestamp = timestamp;
|
||||
|
||||
if (timestamp > client->last_timestamp) { // prevent replay attacks
|
||||
int reply_len = handleRequest(client, &data[4], len - 4);
|
||||
if (reply_len == 0) return; // invalid command
|
||||
|
||||
client->last_timestamp = timestamp;
|
||||
|
||||
if (packet->isRouteFlood()) {
|
||||
// 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);
|
||||
} else {
|
||||
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
|
||||
if (reply) {
|
||||
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
|
||||
sendDirect(reply, client->out_path, client->out_path_len);
|
||||
} else {
|
||||
sendFlood(reply);
|
||||
}
|
||||
if (packet->isRouteFlood()) {
|
||||
// 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);
|
||||
} else {
|
||||
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
|
||||
if (reply) {
|
||||
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
|
||||
sendDirect(reply, client->out_path, client->out_path_len);
|
||||
} else {
|
||||
sendFlood(reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type == PAYLOAD_TYPE_TXT_MSG && len > 5) { // a CLI command
|
||||
uint32_t timestamp;
|
||||
memcpy(×tamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong)
|
||||
uint flags = data[4]; // message attempt number, and other flags
|
||||
|
||||
if (flags == 0 && timestamp > client->last_timestamp) { // prevent replay attacks
|
||||
client->last_timestamp = timestamp;
|
||||
|
||||
// len can be > original length, but 'text' will be padded with zeroes
|
||||
data[len] = 0; // need to make a C string again, with null terminator
|
||||
|
||||
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
|
||||
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), client->id.pub_key, PUB_KEY_SIZE);
|
||||
|
||||
mesh::Packet* ack = createAck(ack_hash);
|
||||
if (ack) {
|
||||
if (client->out_path_len < 0) {
|
||||
sendFlood(ack);
|
||||
} else {
|
||||
sendDirect(ack, client->out_path, client->out_path_len);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t temp[166];
|
||||
handleCommand(timestamp, (const char *) &data[5], (char *) &temp[5]);
|
||||
int text_len = strlen((char *) &temp[5]);
|
||||
if (text_len > 0) {
|
||||
uint32_t timestamp = getRTCClock()->getCurrentTime();
|
||||
memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique
|
||||
temp[4] = 0;
|
||||
|
||||
// calc expected ACK reply
|
||||
//mesh::Utils::sha256((uint8_t *)&expected_ack_crc, 4, temp, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
|
||||
|
||||
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
|
||||
if (reply) {
|
||||
if (client->out_path_len < 0) {
|
||||
sendFlood(reply);
|
||||
} else {
|
||||
sendDirect(reply, client->out_path, client->out_path_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("onPeerDataRecv: invalid peer idx: %d", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,6 +402,31 @@ public:
|
||||
MESH_DEBUG_PRINTLN("ERROR: unable to create advertisement packet!");
|
||||
}
|
||||
}
|
||||
|
||||
void handleCommand(uint32_t sender_timestamp, const char* command, char reply[]) {
|
||||
while (*command == ' ') command++; // skip leading spaces
|
||||
|
||||
if (memcmp(command, "reboot", 6) == 0) {
|
||||
board.reboot(); // doesn't return
|
||||
} else if (memcmp(command, "advert", 6) == 0) {
|
||||
sendSelfAdvertisement();
|
||||
strcpy(reply, "OK - Advert sent");
|
||||
} else if (memcmp(command, "clock sync", 10) == 0) {
|
||||
uint32_t curr = getRTCClock()->getCurrentTime();
|
||||
if (sender_timestamp > curr) {
|
||||
getRTCClock()->setCurrentTime(sender_timestamp);
|
||||
strcpy(reply, "OK - clock set");
|
||||
} else {
|
||||
strcpy(reply, "ERR: clock cannot go backwards");
|
||||
}
|
||||
} else if (memcmp(command, "clock", 5) == 0) {
|
||||
uint32_t now = getRTCClock()->getCurrentTime();
|
||||
DateTime dt = DateTime(now);
|
||||
sprintf(reply, "%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
|
||||
} else {
|
||||
sprintf(reply, "Unknown: %s (commands: reboot, advert, clock)", command);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
@@ -456,13 +524,10 @@ void loop() {
|
||||
|
||||
if (len > 0 && command[len - 1] == '\r') { // received complete line
|
||||
command[len - 1] = 0; // replace newline with C string null terminator
|
||||
if (strcmp(command, "reboot") == 0) {
|
||||
board.reboot(); // doesn't return
|
||||
} else if (strcmp(command, "advert") == 0) {
|
||||
the_mesh.sendSelfAdvertisement();
|
||||
} else {
|
||||
Serial.print(" ERROR: unknown command: "); Serial.println(command);
|
||||
Serial.println(" (commands: reboot, advert)");
|
||||
char reply[160];
|
||||
the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial!
|
||||
if (reply[0]) {
|
||||
Serial.print(" -> "); Serial.println(reply);
|
||||
}
|
||||
|
||||
command[0] = 0; // reset command buffer
|
||||
|
||||
@@ -220,20 +220,24 @@ protected:
|
||||
}
|
||||
|
||||
void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override {
|
||||
processAck((uint8_t *)&ack_crc);
|
||||
if (processAck((uint8_t *)&ack_crc)) {
|
||||
packet->markDoNotRetransmit(); // ACK was for this node, so don't retransmit
|
||||
}
|
||||
}
|
||||
|
||||
void processAck(const uint8_t *data) {
|
||||
bool processAck(const uint8_t *data) {
|
||||
if (memcmp(data, &expected_ack_crc, 4) == 0) { // got an ACK from recipient
|
||||
Serial.printf(" Got ACK! (round trip: %d millis)\n", _ms->getMillis() - last_msg_sent);
|
||||
// NOTE: the same ACK can be received multiple times!
|
||||
expected_ack_crc = 0; // reset our expected hash, now that we have received ACK
|
||||
txt_send_timeout = 0;
|
||||
} else {
|
||||
uint32_t crc;
|
||||
memcpy(&crc, data, 4);
|
||||
MESH_DEBUG_PRINTLN(" unknown ACK received: %08X (expected: %08X)", crc, expected_ack_crc);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t crc;
|
||||
memcpy(&crc, data, 4);
|
||||
MESH_DEBUG_PRINTLN(" unknown ACK received: %08X (expected: %08X)", crc, expected_ack_crc);
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -70,6 +70,9 @@ build_flags =
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_repeater/main.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
adafruit/RTClib @ ^2.1.3
|
||||
|
||||
[env:Heltec_v3_chat_alice]
|
||||
extends = Heltec_lora32_v3
|
||||
@@ -126,6 +129,9 @@ build_flags =
|
||||
-D ADMIN_PASSWORD="\"password\""
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
lib_deps =
|
||||
${Xiao_esp32_C3.lib_deps}
|
||||
adafruit/RTClib @ ^2.1.3
|
||||
|
||||
[env:Xiao_C3_Repeater_sx1268]
|
||||
extends = Xiao_esp32_C3
|
||||
@@ -141,6 +147,9 @@ build_flags =
|
||||
-D ADMIN_PASSWORD="\"password\""
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
lib_deps =
|
||||
${Xiao_esp32_C3.lib_deps}
|
||||
adafruit/RTClib @ ^2.1.3
|
||||
|
||||
; =============
|
||||
[Xiao_S3_WIO]
|
||||
@@ -175,6 +184,9 @@ build_flags =
|
||||
-D ADMIN_PASSWORD="\"password\""
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
lib_deps =
|
||||
${Xiao_S3_WIO.lib_deps}
|
||||
adafruit/RTClib @ ^2.1.3
|
||||
|
||||
; ----------------- NRF52 ---------------------
|
||||
[nrf52_base]
|
||||
@@ -213,6 +225,9 @@ build_flags =
|
||||
-D ADMIN_PASSWORD="\"password\""
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
lib_deps =
|
||||
${rak4631.lib_deps}
|
||||
adafruit/RTClib @ ^2.1.3
|
||||
|
||||
[env:RAK_4631_chat_alice]
|
||||
extends = rak4631
|
||||
|
||||
@@ -111,7 +111,9 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
if (found) {
|
||||
pkt->markDoNotRetransmit(); // packet was for this node, so don't retransmit
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("recv matches no peers, src_hash=%02X", (uint32_t)src_hash);
|
||||
}
|
||||
}
|
||||
@@ -139,6 +141,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
|
||||
int len = Utils::MACThenDecrypt(secret, data, macAndData, pkt->payload_len - i);
|
||||
if (len > 0) { // success!
|
||||
onAnonDataRecv(pkt, pkt->getPayloadType(), sender, data, len);
|
||||
pkt->markDoNotRetransmit();
|
||||
}
|
||||
}
|
||||
action = routeRecvPacket(pkt);
|
||||
@@ -217,7 +220,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
|
||||
}
|
||||
|
||||
DispatcherAction Mesh::routeRecvPacket(Packet* packet) {
|
||||
if (packet->isRouteFlood() && packet->path_len + PATH_HASH_SIZE <= MAX_PATH_SIZE && allowPacketForward(packet)) {
|
||||
if (packet->isRouteFlood() && !packet->isMarkedDoNotRetransmit()
|
||||
&& packet->path_len + PATH_HASH_SIZE <= MAX_PATH_SIZE && allowPacketForward(packet)) {
|
||||
// append this node's hash to 'path'
|
||||
packet->path_len += self_id.copyHashTo(&packet->path[packet->path_len]);
|
||||
|
||||
|
||||
@@ -68,6 +68,9 @@ public:
|
||||
* \returns one of PAYLOAD_VER_ values
|
||||
*/
|
||||
uint8_t getPayloadVer() const { return (header >> PH_VER_SHIFT) & PH_VER_MASK; }
|
||||
|
||||
void markDoNotRetransmit() { header = 0xFF; }
|
||||
bool isMarkedDoNotRetransmit() const { return header == 0xFF; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user