diff --git a/zephcore/app/CompanionMesh.cpp b/zephcore/app/CompanionMesh.cpp index b134a41..a4d0150 100644 --- a/zephcore/app/CompanionMesh.cpp +++ b/zephcore/app/CompanionMesh.cpp @@ -83,6 +83,7 @@ LOG_MODULE_REGISTER(zephcore_companion, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL); #define CMD_SEND_CHANNEL_DATA 0x3E #define CMD_SET_DEFAULT_FLOOD_SCOPE 0x3F /* v11+ */ #define CMD_GET_DEFAULT_FLOOD_SCOPE 0x40 /* v11+ */ +#define CMD_SEND_RAW_PACKET 0x41 /* v12+ */ /* Response packet types */ #define PACKET_OK 0x00 @@ -3132,6 +3133,26 @@ bool CompanionMesh::handleProtocolFrame(const uint8_t *data, size_t len) } return true; + case CMD_SEND_RAW_PACKET: + if (len >= 4) { + mesh::Packet *pkt = obtainNewPacket(); + if (pkt) { + uint8_t priority = data[1]; + if (tryParsePacket(pkt, &data[2], len - 2)) { + sendPacket(pkt, priority, 0); + sendPacketOk(); + } else { + releasePacket(pkt); + sendPacketError(ERR_ILLEGAL_ARG); + } + } else { + sendPacketError(ERR_TABLE_FULL); + } + } else { + sendPacketError(ERR_ILLEGAL_ARG); + } + return true; + default: break; } diff --git a/zephcore/helpers/BaseChatMesh.cpp b/zephcore/helpers/BaseChatMesh.cpp index d397102..70e6ca5 100644 --- a/zephcore/helpers/BaseChatMesh.cpp +++ b/zephcore/helpers/BaseChatMesh.cpp @@ -52,19 +52,19 @@ mesh::Packet *BaseChatMesh::createSelfAdvert(const char *name, double lat, doubl return createAdvert(self_id, app_data, app_data_len); } -void BaseChatMesh::sendAckTo(const ContactInfo &dest, uint32_t ack_hash) +void BaseChatMesh::sendAckTo(const ContactInfo &dest, const uint8_t *ack_hash, uint8_t ack_len) { if (dest.out_path_len == OUT_PATH_UNKNOWN) { - mesh::Packet *ack = createAck(ack_hash); + mesh::Packet *ack = createAck(ack_hash, ack_len); if (ack) sendFloodScoped(dest, ack, TXT_ACK_DELAY); } else { uint32_t d = TXT_ACK_DELAY; if (getExtraAckTransmitCount() > 0) { - mesh::Packet *a1 = createMultiAck(ack_hash, 1); + mesh::Packet *a1 = createMultiAck(ack_hash, ack_len, 1); if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, d); d += 300; } - mesh::Packet *a2 = createAck(ack_hash); + mesh::Packet *a2 = createAck(ack_hash, ack_len); if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, d); } } @@ -254,16 +254,18 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender from.lastmod = getRTCClock()->getCurrentTime(); onMessageRecv(from, packet, timestamp, (const char *)&data[5]); - uint32_t ack_hash; - mesh::Utils::sha256((uint8_t *)&ack_hash, 4, data, 5 + strlen((char *)&data[5]), - from.id.pub_key, PUB_KEY_SIZE); + int text_len = strlen((char *)&data[5]); + uint8_t ack_hash[6]; + mesh::Utils::sha256(ack_hash, 4, data, 5 + text_len, from.id.pub_key, PUB_KEY_SIZE); + ack_hash[4] = data[5 + text_len + 1]; // attempt byte (makes hash unique across retries) + getRNG()->random(&ack_hash[5], 1); // random byte (makes hash unique per delivery node) if (packet->isRouteFlood()) { mesh::Packet *path = createPathReturn(from.id, secret, packet->path, packet->path_len, - PAYLOAD_TYPE_ACK, (uint8_t *)&ack_hash, 4); + PAYLOAD_TYPE_ACK, ack_hash, 6); if (path) sendFloodScoped(from, path, TXT_ACK_DELAY); } else { - sendAckTo(from, ack_hash); + sendAckTo(from, ack_hash, 6); } } else if (flags == TXT_TYPE_CLI_DATA) { onCommandDataRecv(from, packet, timestamp, (const char *)&data[5]); @@ -288,7 +290,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender PAYLOAD_TYPE_ACK, (uint8_t *)&ack_hash, 4); if (path) sendFloodScoped(from, path, TXT_ACK_DELAY); } else { - sendAckTo(from, ack_hash); + sendAckTo(from, (uint8_t *)&ack_hash, 4); } } } else if (type == PAYLOAD_TYPE_REQ && len > 4) { diff --git a/zephcore/helpers/BaseChatMesh.h b/zephcore/helpers/BaseChatMesh.h index 22a3ee7..22f1ad0 100644 --- a/zephcore/helpers/BaseChatMesh.h +++ b/zephcore/helpers/BaseChatMesh.h @@ -94,7 +94,7 @@ class BaseChatMesh : public mesh::Mesh { mesh::Packet *composeMsgPacket(const ContactInfo &recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t &expected_ack); - void sendAckTo(const ContactInfo &dest, uint32_t ack_hash); + void sendAckTo(const ContactInfo &dest, const uint8_t *ack_hash, uint8_t ack_len = 4); protected: BaseChatMesh(mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, mesh::RTCClock &rtc, diff --git a/zephcore/include/mesh/Dispatcher.h b/zephcore/include/mesh/Dispatcher.h index f57bc43..8f53aa4 100644 --- a/zephcore/include/mesh/Dispatcher.h +++ b/zephcore/include/mesh/Dispatcher.h @@ -118,10 +118,11 @@ public: bool millisHasNowPassed(uint32_t timestamp) const; uint32_t futureMillis(int millis_from_now) const; + bool tryParsePacket(Packet *pkt, const uint8_t *raw, int len); + private: void updateTxBudget(); uint32_t getMaxTxBudgetMs() const; - bool tryParsePacket(Packet *pkt, const uint8_t *raw, int len); void checkRecv(); void checkSend(); }; diff --git a/zephcore/include/mesh/Mesh.h b/zephcore/include/mesh/Mesh.h index eb1f190..ee2f093 100644 --- a/zephcore/include/mesh/Mesh.h +++ b/zephcore/include/mesh/Mesh.h @@ -90,8 +90,10 @@ public: MeshTables *getTables() const { return _tables; } Packet *createAdvert(const LocalIdentity &id, const uint8_t *app_data = nullptr, size_t app_data_len = 0); - Packet *createAck(uint32_t ack_crc); - Packet *createMultiAck(uint32_t ack_crc, uint8_t remaining); + Packet *createAck(const uint8_t *ack, uint8_t len); + Packet *createAck(uint32_t ack_crc) { return createAck((const uint8_t *)&ack_crc, 4); } + Packet *createMultiAck(const uint8_t *ack, uint8_t len, uint8_t remaining); + Packet *createMultiAck(uint32_t ack_crc, uint8_t remaining) { return createMultiAck((const uint8_t *)&ack_crc, 4, remaining); } Packet *createControlData(const uint8_t *data, size_t len); Packet *createDatagram(uint8_t type, const Identity &dest, const uint8_t *secret, const uint8_t *data, size_t len); Packet *createAnonDatagram(uint8_t type, const LocalIdentity &sender, const Identity &dest, const uint8_t *secret, const uint8_t *data, size_t data_len); diff --git a/zephcore/include/mesh/SimpleMeshTables.h b/zephcore/include/mesh/SimpleMeshTables.h index c276b39..e31b773 100644 --- a/zephcore/include/mesh/SimpleMeshTables.h +++ b/zephcore/include/mesh/SimpleMeshTables.h @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * SimpleMeshTables - hash-based deduplication + * SimpleMeshTables - hash-based packet deduplication */ #pragma once @@ -10,44 +10,21 @@ namespace mesh { -#define MAX_PACKET_HASHES 128 -#define MAX_PACKET_ACKS 64 +#define MAX_PACKET_HASHES (128+32) class SimpleMeshTables : public MeshTables { uint8_t _hashes[MAX_PACKET_HASHES * MAX_HASH_SIZE]; int _next_idx; - uint32_t _acks[MAX_PACKET_ACKS]; - int _next_ack_idx; uint32_t _direct_dups, _flood_dups; public: SimpleMeshTables() { memset(_hashes, 0, sizeof(_hashes)); _next_idx = 0; - memset(_acks, 0, sizeof(_acks)); - _next_ack_idx = 0; _direct_dups = _flood_dups = 0; } bool hasSeen(const Packet *packet) override { - if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { - uint32_t ack; - memcpy(&ack, packet->payload, 4); - for (int i = 0; i < MAX_PACKET_ACKS; i++) { - if (ack == _acks[i]) { - if (packet->isRouteDirect()) { - _direct_dups++; - } else { - _flood_dups++; - } - return true; - } - } - _acks[_next_ack_idx] = ack; - _next_ack_idx = (_next_ack_idx + 1) % MAX_PACKET_ACKS; - return false; - } - uint8_t hash[MAX_HASH_SIZE]; packet->calculatePacketHash(hash); const uint8_t *sp = _hashes; @@ -67,24 +44,13 @@ public: } void clear(const Packet *packet) override { - if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { - uint32_t ack; - memcpy(&ack, packet->payload, 4); - for (int i = 0; i < MAX_PACKET_ACKS; i++) { - if (ack == _acks[i]) { - _acks[i] = 0; - break; - } - } - } else { - uint8_t hash[MAX_HASH_SIZE]; - packet->calculatePacketHash(hash); - uint8_t *sp = _hashes; - for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { - if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { - memset(sp, 0, MAX_HASH_SIZE); - break; - } + uint8_t hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(hash); + uint8_t *sp = _hashes; + for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { + if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { + memset(sp, 0, MAX_HASH_SIZE); + break; } } } diff --git a/zephcore/src/Mesh.cpp b/zephcore/src/Mesh.cpp index 2fda297..ef31cbc 100644 --- a/zephcore/src/Mesh.cpp +++ b/zephcore/src/Mesh.cpp @@ -127,9 +127,21 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet *pkt) void Mesh::routeDirectRecvAcks(Packet *packet, uint32_t delay_millis) { if (!packet->isMarkedDoNotRetransmit()) { - uint32_t crc; - memcpy(&crc, packet->payload, 4); - Packet *a2 = createAck(crc); + uint8_t extra = getExtraAckTransmitCount(); + while (extra > 0) { + delay_millis += getDirectRetransmitDelay(packet) + 300; + Packet *a1 = createMultiAck(packet->payload, packet->payload_len, extra); + if (a1) { + /* Trusted source: packet->path is MAX_PATH_SIZE-sized. */ + a1->path_len = Packet::copyPath(a1->path, packet->path, MAX_PATH_SIZE, packet->path_len); + a1->header &= ~PH_ROUTE_MASK; + a1->header |= ROUTE_TYPE_DIRECT; + sendPacket(a1, 0, delay_millis); + } + extra--; + } + + Packet *a2 = createAck(packet->payload, packet->payload_len); if (a2) { /* Trusted source: packet->path is MAX_PATH_SIZE-sized. */ a2->path_len = Packet::copyPath(a2->path, packet->path, MAX_PATH_SIZE, packet->path_len); @@ -439,24 +451,24 @@ Packet *Mesh::createAdvert(const LocalIdentity &id, const uint8_t *app_data, siz return packet; } -Packet *Mesh::createAck(uint32_t ack_crc) +Packet *Mesh::createAck(const uint8_t *ack, uint8_t len) { Packet *packet = obtainNewPacket(); if (packet == nullptr) return nullptr; packet->header = (PAYLOAD_TYPE_ACK << PH_TYPE_SHIFT); - memcpy(packet->payload, &ack_crc, 4); - packet->payload_len = 4; + memcpy(packet->payload, ack, len); + packet->payload_len = len; return packet; } -Packet *Mesh::createMultiAck(uint32_t ack_crc, uint8_t remaining) +Packet *Mesh::createMultiAck(const uint8_t *ack, uint8_t len, uint8_t remaining) { Packet *packet = obtainNewPacket(); if (packet == nullptr) return nullptr; packet->header = (PAYLOAD_TYPE_MULTIPART << PH_TYPE_SHIFT); packet->payload[0] = (remaining << 4) | PAYLOAD_TYPE_ACK; - memcpy(&packet->payload[1], &ack_crc, 4); - packet->payload_len = 5; + memcpy(&packet->payload[1], ack, len); + packet->payload_len = 1 + len; return packet; }