From 94cd96cfdea1cb2387c807e0fb8a4089b0ccabb0 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 22 Feb 2021 12:57:26 +0800 Subject: [PATCH] begin multichannel hash impl --- proto | 2 +- src/mesh/Channels.cpp | 49 +++++++++++++---- src/mesh/Channels.h | 20 +++++-- src/mesh/MeshTypes.h | 2 + src/mesh/RadioInterface.cpp | 4 +- src/mesh/RadioInterface.h | 3 ++ src/mesh/RadioLibInterface.cpp | 1 + src/mesh/Router.cpp | 84 ++++++++++++++++++------------ src/mesh/Router.h | 3 ++ src/mesh/generated/deviceonly.pb.h | 2 +- src/mesh/generated/mesh.pb.h | 18 ++++--- 11 files changed, 129 insertions(+), 59 deletions(-) diff --git a/proto b/proto index 6421b29ef..f6ff4cc0c 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 6421b29ef45dbd34d7c6c43f4be48a7906e66ad9 +Subproject commit f6ff4cc0c98b201342c32776eeeb9ace83b450dd diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 8b5d5cb6b..696f78f4f 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -10,12 +10,20 @@ static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0 Channels channels; +uint8_t xorHash(uint8_t *p, size_t len) +{ + uint8_t code = 0; + for (int i = 0; i < len; i++) + code ^= p[i]; + return code; +} + /** * Validate a channel, fixing any errors as needed */ Channel &Channels::fixupChannel(ChannelIndex chIndex) { - auto ch = getByIndex(chIndex); + Channel &ch = getByIndex(chIndex); ch.index = chIndex; // Preinit the index so it be ready to share with the phone (we'll never change it later) @@ -31,14 +39,16 @@ Channel &Channels::fixupChannel(ChannelIndex chIndex) if (strcmp(channelSettings.name, "Default") == 0) *channelSettings.name = '\0'; - // Convert any old usage of the defaultpsk into our new short representation. + /* Convert any old usage of the defaultpsk into our new short representation. if (channelSettings.psk.size == sizeof(defaultpsk) && memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) { *channelSettings.psk.bytes = 1; channelSettings.psk.size = 1; - } + } */ } + hashes[chIndex] = generateHash(chIndex); + return ch; } @@ -47,7 +57,7 @@ Channel &Channels::fixupChannel(ChannelIndex chIndex) */ void Channels::initDefaultChannel(ChannelIndex chIndex) { - auto ch = getByIndex(chIndex); + Channel &ch = getByIndex(chIndex); ChannelSettings &channelSettings = ch.settings; // radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast @@ -69,7 +79,7 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) */ void Channels::setCrypto(ChannelIndex chIndex) { - auto ch = getByIndex(chIndex); + Channel &ch = getByIndex(chIndex); ChannelSettings &channelSettings = ch.settings; assert(ch.has_settings); @@ -124,7 +134,7 @@ void Channels::onConfigChanged() { // Make sure the phone hasn't mucked anything up for (int i = 0; i < devicestate.channels_count; i++) { - auto ch = fixupChannel(i); + Channel &ch = fixupChannel(i); if (ch.role == Channel_Role_PRIMARY) primaryIndex = i; @@ -211,9 +221,7 @@ const char *Channels::getPrimaryName() auto channelSettings = getPrimary(); if (channelSettings.psk.size != 1) { // We have a standard PSK, so generate a letter based hash. - uint8_t code = 0; - for (int i = 0; i < activePSKSize; i++) - code ^= activePSK[i]; + uint8_t code = xorHash(activePSK, activePSKSize); suffix = 'A' + (code % 26); } else { @@ -222,4 +230,25 @@ const char *Channels::getPrimaryName() snprintf(buf, sizeof(buf), "#%s-%c", channelSettings.name, suffix); return buf; -} \ No newline at end of file +} + +/** Given a channel hash setup crypto for decoding that channel (or the primary channel if that channel is unsecured) + * + * This method is called before decoding inbound packets + * + * @return -1 if no suitable channel could be found, otherwise returns the channel index + */ +int16_t Channels::setActiveByHash(ChannelHash channelHash) {} + +/** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured) + * + * This method is called before encoding outbound packets + * + * @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1 + */ +int16_t Channels::setActiveByIndex(ChannelIndex channelIndex) {} + +/** Given a channel number, return the (0 to 255) hash for that channel + * If no suitable channel could be found, return -1 + */ +ChannelHash Channels::generateHash(ChannelIndex channelNum) {} \ No newline at end of file diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h index 913c316dc..a86b7ff68 100644 --- a/src/mesh/Channels.h +++ b/src/mesh/Channels.h @@ -3,7 +3,13 @@ #include "mesh-pb-constants.h" #include +/** A channel number (index into the channel table) + */ typedef uint8_t ChannelIndex; + +/** A low quality hash of the channel PSK and the channel name. created by generateHash(chIndex) + * Used as a hint to limit which PSKs are considered for packet decoding. +*/ typedef uint8_t ChannelHash; /** The container/on device API for working with channels */ @@ -21,6 +27,9 @@ class Channels uint8_t activePSK[32]; uint8_t activePSKSize = 0; + /// the precomputed hashes for each of our channels + ChannelHash hashes[MAX_NUM_CHANNELS]; + public: const ChannelSettings &getPrimary() { return getByIndex(getPrimaryIndex()).settings; } @@ -66,18 +75,21 @@ class Channels * * This method is called before decoding inbound packets * - * @return false if no suitable channel could be found. + * @return -1 if no suitable channel could be found, otherwise returns the channel index */ - bool setActiveByHash(ChannelHash channelHash); + int16_t setActiveByHash(ChannelHash channelHash); /** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured) * - * This method is called before encoding inbound packets + * This method is called before encoding outbound packets * * @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1 */ int16_t setActiveByIndex(ChannelIndex channelIndex); + /** return the channel hash we are currently using for sending */ + ChannelHash getActiveHash(); + private: /** Given a channel index, change to use the crypto key specified by that index */ @@ -89,7 +101,7 @@ class Channels /** Given a channel number, return the (0 to 255) hash for that channel * If no suitable channel could be found, return -1 */ - int16_t getHash(ChannelIndex channelNum); + ChannelHash generateHash(ChannelIndex channelNum); /** * Validate a channel, fixing any errors as needed diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index 69e782b12..06f2bf480 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -14,6 +14,8 @@ typedef uint32_t PacketId; // A packet sequence number #define ERRNO_NO_INTERFACES 33 #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER #define ERRNO_DISABLED 34 // the itnerface is disabled +#define ERRNO_TOO_LARGE 35 +#define ERRNO_NO_CHANNEL 36 /** * the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket. diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 08ac40d5d..93dd55e80 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -10,6 +10,7 @@ #include #include #include +#include "Channels.h" #define RDEF(name, freq, spacing, num_ch, power_limit) \ { \ @@ -157,7 +158,7 @@ void printPacket(const char *prefix, const MeshPacket *p) RadioInterface::RadioInterface() { - assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected + assert(sizeof(PacketHeader) == 16); // make sure the compiler did what we expected // Can't print strings this early - serial not setup yet // DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name); @@ -350,6 +351,7 @@ size_t RadioInterface::beginSending(MeshPacket *p) h->from = p->from; h->to = p->to; h->id = p->id; + h->channel = p->channel; assert(p->hop_limit <= HOP_MAX); h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0); diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 5efad0ae4..9d19fa590 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -29,6 +29,9 @@ typedef struct { * The bottom three bits of flags are use to store hop_limit when sent over the wire. **/ uint8_t flags; + + /** The channel hash - used as a hint for the decoder to limit which channels we consider */ + uint8_t channel; } PacketHeader; /** diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 63d7095c5..ab1728126 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -274,6 +274,7 @@ void RadioLibInterface::handleReceiveInterrupt() mp->from = h->from; mp->to = h->to; mp->id = h->id; + mp->channel = h->id; assert(HOP_MAX <= PACKET_FLAGS_HOP_MASK); // If hopmax changes, carefully check this code mp->hop_limit = h->flags & PACKET_FLAGS_HOP_MASK; mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK); diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 673fafefd..434bdde5d 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -1,9 +1,10 @@ #include "Router.h" +#include "Channels.h" #include "CryptoEngine.h" +#include "NodeDB.h" #include "RTC.h" #include "configuration.h" #include "mesh-pb-constants.h" -#include #include "plugins/RoutingPlugin.h" /** @@ -107,7 +108,12 @@ void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom) routingPlugin->sendAckNak(err, to, idFrom); } - +void Router::abortSendAndNak(Routing_Error err, MeshPacket *p) +{ + DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err); + sendAckNak(Routing_Error_NO_INTERFACE, p->from, p->id); + packetPool.release(p); +} ErrorCode Router::sendLocal(MeshPacket *p) { @@ -118,11 +124,7 @@ ErrorCode Router::sendLocal(MeshPacket *p) return ERRNO_OK; } else if (!iface) { // We must be sending to remote nodes also, fail if no interface found - - // ERROR! no radio found, report failure back to the client and drop the packet - DEBUG_MSG("Error: No interface, returning NAK and dropping packet.\n"); - sendAckNak(Routing_Error_NO_INTERFACE, p->from, p->id); - packetPool.release(p); + abortSendAndNak(Routing_Error_NO_INTERFACE, p); return ERRNO_NO_INTERFACES; } else { @@ -146,7 +148,8 @@ ErrorCode Router::send(MeshPacket *p) assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal // PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0; - // assert(!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with assert + // assert(!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with + // assert // Never set the want_ack flag on broadcast packets sent over the air. if (p->to == NODENUM_BROADCAST) @@ -163,7 +166,20 @@ ErrorCode Router::send(MeshPacket *p) size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded); - assert(numbytes <= MAX_RHPACKETLEN); + if (numbytes > MAX_RHPACKETLEN) { + abortSendAndNak(Routing_Error_TOO_LARGE, p); + return ERRNO_TOO_LARGE; + } + + auto hash = channels.setActiveByIndex(p->channel); + if (hash < 0) { + // No suitable channel could be found for sending + abortSendAndNak(Routing_Error_NO_CHANNEL, p); + return ERRNO_NO_CHANNEL; + } + + // Now that we are encrypting the packet channel should be the hash (no longer the index) + p->channel = hash; crypto->encrypt(p->from, p->id, numbytes, bytes); // Copy back into the packet and set the variant type @@ -173,23 +189,15 @@ ErrorCode Router::send(MeshPacket *p) } assert(iface); // This should have been detected already in sendLocal (or we just received a packet from outside) - // if (iface) { - // DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); return iface->send(p); - /* } else { - DEBUG_MSG("Dropping packet - no interfaces - fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); - packetPool.release(p); - return ERRNO_NO_INTERFACES; - } */ } /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ -bool Router::cancelSending(NodeNum from, PacketId id) { +bool Router::cancelSending(NodeNum from, PacketId id) +{ return iface ? iface->cancelSending(from, id) : false; } - - /** * Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to * update routing tables etc... based on what we overhear (even for messages not destined to our node) @@ -207,22 +215,30 @@ bool Router::perhapsDecode(MeshPacket *p) assert(p->which_payloadVariant == MeshPacket_encrypted_tag); - // FIXME - someday don't send routing packets encrypted. That would allow us to route for other channels without - // being able to decrypt their data. - // Try to decrypt the packet if we can - static uint8_t bytes[MAX_RHPACKETLEN]; - memcpy(bytes, p->encrypted.bytes, - p->encrypted.size); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf - crypto->decrypt(p->from, p->id, p->encrypted.size, bytes); - - // Take those raw bytes and convert them back into a well structured protobuf we can understand - if (!pb_decode_from_bytes(bytes, p->encrypted.size, Data_fields, &p->decoded)) { - DEBUG_MSG("Invalid protobufs in received mesh packet!\n"); + ChannelHash chHash = p->channel; + int16_t chIndex = channels.setActiveByHash(chHash); + if (chIndex < 0) { + DEBUG_MSG("No suitable channel found for decoding, hash was 0x%x!\n", chHash); return false; } else { - // parsing was successful - p->which_payloadVariant = MeshPacket_decoded_tag; - return true; + p->channel = chIndex; + + // Try to decrypt the packet if we can + static uint8_t bytes[MAX_RHPACKETLEN]; + memcpy(bytes, p->encrypted.bytes, + p->encrypted + .size); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf + crypto->decrypt(p->from, p->id, p->encrypted.size, bytes); + + // Take those raw bytes and convert them back into a well structured protobuf we can understand + if (!pb_decode_from_bytes(bytes, p->encrypted.size, Data_fields, &p->decoded)) { + DEBUG_MSG("Invalid protobufs in received mesh packet!\n"); + return false; + } else { + // parsing was successful + p->which_payloadVariant = MeshPacket_decoded_tag; + return true; + } } } @@ -244,7 +260,7 @@ void Router::handleReceived(MeshPacket *p) if (perhapsDecode(p)) { // parsing was successful, queue for our recipient - // call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api + // call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api // sniffReceived(p); MeshPlugin::callPlugins(*p); } diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 4b1656936..6e6bf2c2a 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -123,6 +123,9 @@ class Router : protected concurrency::OSThread * Note: this method will free the provided packet. */ void handleReceived(MeshPacket *p); + + /** Frees the provided packet, and generates a NAK indicating the speicifed error while sending */ + void abortSendAndNak(Routing_Error err, MeshPacket *p); }; extern Router *router; diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index b8c372664..9ec3fa5a0 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -80,7 +80,7 @@ extern const pb_msgdesc_t DeviceState_msg; #define DeviceState_fields &DeviceState_msg /* Maximum encoded size of messages (where known) */ -#define DeviceState_size 6119 +#define DeviceState_size 6125 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 873e0aa2f..6858cdcd9 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -80,7 +80,9 @@ typedef enum _Routing_Error { Routing_Error_GOT_NAK = 2, Routing_Error_TIMEOUT = 3, Routing_Error_NO_INTERFACE = 4, - Routing_Error_MAX_RETRANSMIT = 5 + Routing_Error_MAX_RETRANSMIT = 5, + Routing_Error_NO_CHANNEL = 6, + Routing_Error_TOO_LARGE = 7 } Routing_Error; typedef enum _MeshPacket_Priority { @@ -240,7 +242,7 @@ typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t; typedef struct _MeshPacket { uint32_t from; uint32_t to; - uint8_t channel_index; + uint32_t channel; pb_size_t which_payloadVariant; union { Data decoded; @@ -340,8 +342,8 @@ typedef struct _ToRadio { #define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_TransmitFailed+1)) #define _Routing_Error_MIN Routing_Error_NONE -#define _Routing_Error_MAX Routing_Error_MAX_RETRANSMIT -#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_MAX_RETRANSMIT+1)) +#define _Routing_Error_MAX Routing_Error_TOO_LARGE +#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_TOO_LARGE+1)) #define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET #define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX @@ -488,7 +490,7 @@ extern "C" { #define Channel_role_tag 3 #define MeshPacket_from_tag 1 #define MeshPacket_to_tag 2 -#define MeshPacket_channel_index_tag 3 +#define MeshPacket_channel_tag 3 #define MeshPacket_decoded_tag 4 #define MeshPacket_encrypted_tag 5 #define MeshPacket_id_tag 6 @@ -571,7 +573,7 @@ X(a, STATIC, SINGULAR, FIXED32, source, 5) #define MeshPacket_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, FIXED32, from, 1) \ X(a, STATIC, SINGULAR, FIXED32, to, 2) \ -X(a, STATIC, SINGULAR, UINT32, channel_index, 3) \ +X(a, STATIC, SINGULAR, UINT32, channel, 3) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,decoded,decoded), 4) \ X(a, STATIC, ONEOF, BYTES, (payloadVariant,encrypted,encrypted), 5) \ X(a, STATIC, SINGULAR, FIXED32, id, 6) \ @@ -771,7 +773,7 @@ extern const pb_msgdesc_t AdminMessage_msg; #define RouteDiscovery_size 40 #define Routing_size 47 #define Data_size 255 -#define MeshPacket_size 294 +#define MeshPacket_size 297 #define ChannelSettings_size 87 #define Channel_size 94 #define RadioConfig_size 308 @@ -780,7 +782,7 @@ extern const pb_msgdesc_t AdminMessage_msg; #define MyNodeInfo_size 89 #define LogRecord_size 81 #define FromRadio_size 317 -#define ToRadio_size 297 +#define ToRadio_size 300 #define AdminMessage_size 311 #ifdef __cplusplus