From 71812d220b0968737d401566ddd5038a8aa4adbc Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Tue, 26 Aug 2025 16:57:01 +0200 Subject: [PATCH 1/4] Added compatibility between nodes on different Presets through `Mesh via UDP` --- src/mesh/Channels.cpp | 23 +++++++++++++++++++++++ src/mesh/Channels.h | 2 ++ src/mesh/FloodingRouter.cpp | 7 +++++++ src/mesh/Router.cpp | 21 +++++++++++++++++++++ src/mesh/udp/UdpMulticastHandler.h | 1 + 5 files changed, 54 insertions(+) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 70e4127d8..433727301 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -422,6 +422,29 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) } } +bool Channels::setDefaultPresetCryptoForHash(ChannelHash channelHash) +{ + // Iterate all known presets + for (int preset = _meshtastic_Config_LoRaConfig_ModemPreset_MIN; preset <= _meshtastic_Config_LoRaConfig_ModemPreset_MAX; ++preset) { + const char *name = DisplayFormatters::getModemPresetDisplayName((meshtastic_Config_LoRaConfig_ModemPreset)preset, false); + if (!name) continue; + if (strcmp(name, "Invalid") == 0) continue; // skip invalid placeholder + uint8_t h = xorHash((const uint8_t *)name, strlen(name)); + // Expand default PSK alias 1 to actual bytes and xor into hash + uint8_t tmp = h ^ xorHash(defaultpsk, sizeof(defaultpsk)); + if (tmp == channelHash) { + // Set crypto to defaultpsk and report success + CryptoKey k; + memcpy(k.bytes, defaultpsk, sizeof(defaultpsk)); + k.length = sizeof(defaultpsk); + crypto->setKey(k); + LOG_INFO("Matched default preset '%s' for hash 0x%x; set default PSK", name, channelHash); + return true; + } + } + return false; +} + /** 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 diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h index 7873a306a..b53f552fa 100644 --- a/src/mesh/Channels.h +++ b/src/mesh/Channels.h @@ -94,6 +94,8 @@ class Channels bool ensureLicensedOperation(); + bool setDefaultPresetCryptoForHash(ChannelHash channelHash); + private: /** Given a channel index, change to use the crypto key specified by that index * diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index dbd458b61..b19966e6b 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -68,6 +68,13 @@ bool FloodingRouter::isRebroadcaster() void FloodingRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p) { if (!isToUs(p) && (p->hop_limit > 0) && !isFromUs(p)) { + // If this packet arrived via UDP and is still encrypted, skip rebroadcast. + // We expect the router to decode and then re-encode to our local preset before sending. + if (p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP && + p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag) { + LOG_DEBUG("Skip rebroadcast of UDP-encrypted packet; awaiting local decode/re-encode"); + return; + } if (p->id != 0) { if (isRebroadcaster()) { meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 1f835bca7..bf6a66a1e 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -416,6 +416,27 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p) } } } + + // Fallback: for UDP multicast, try default preset names with default PSK if normal channel match failed + if (!decrypted && p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP) { + if (channels.setDefaultPresetCryptoForHash(p->channel)) { + memcpy(bytes, p->encrypted.bytes, rawSize); + crypto->decrypt(p->from, p->id, rawSize, bytes); + + meshtastic_Data decodedtmp; + memset(&decodedtmp, 0, sizeof(decodedtmp)); + if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &decodedtmp) && + decodedtmp.portnum != meshtastic_PortNum_UNKNOWN_APP) { + p->decoded = decodedtmp; + p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; + // Map to our primary channel index so subsequent processing and re-encoding use local preset + chIndex = channels.getPrimaryIndex(); + decrypted = true; + } else { + LOG_WARN("UDP fallback decode attempted but failed for hash 0x%x", p->channel); + } + } + } if (decrypted) { // parsing was successful p->channel = chIndex; // change to store the index instead of the hash diff --git a/src/mesh/udp/UdpMulticastHandler.h b/src/mesh/udp/UdpMulticastHandler.h index 9650668a8..104e1a711 100644 --- a/src/mesh/udp/UdpMulticastHandler.h +++ b/src/mesh/udp/UdpMulticastHandler.h @@ -54,6 +54,7 @@ class UdpMulticastHandler final LOG_DEBUG("Decoding MeshPacket from UDP len=%u", packetLength); bool isPacketDecoded = pb_decode_from_bytes(packet.data(), packetLength, &meshtastic_MeshPacket_msg, &mp); if (isPacketDecoded && router && mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag) { + mp.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP; mp.pki_encrypted = false; mp.public_key.size = 0; memset(mp.public_key.bytes, 0, sizeof(mp.public_key.bytes)); From 81a4c84c2e67af25290759847e5856ca7ce20afc Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Tue, 26 Aug 2025 20:57:43 +0200 Subject: [PATCH 2/4] Optimize multicast handling and channel mapping - FloodingRouter: remove redundant UDP-encrypted rebroadcast suppression. - Router: guard multicast fallback with HAS_UDP_MULTICAST and map fallback-decoded packets to the local default channel via isDefaultChannel() - UdpMulticastHandler: set transport_mechanism only after successful decode --- src/mesh/FloodingRouter.cpp | 7 ------- src/mesh/Router.cpp | 13 +++++++++++-- src/mesh/udp/UdpMulticastHandler.h | 5 +++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index b19966e6b..dbd458b61 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -68,13 +68,6 @@ bool FloodingRouter::isRebroadcaster() void FloodingRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p) { if (!isToUs(p) && (p->hop_limit > 0) && !isFromUs(p)) { - // If this packet arrived via UDP and is still encrypted, skip rebroadcast. - // We expect the router to decode and then re-encode to our local preset before sending. - if (p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP && - p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag) { - LOG_DEBUG("Skip rebroadcast of UDP-encrypted packet; awaiting local decode/re-encode"); - return; - } if (p->id != 0) { if (isRebroadcaster()) { meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index bf6a66a1e..4a65fd7c4 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -417,6 +417,7 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p) } } +#if HAS_UDP_MULTICAST // Fallback: for UDP multicast, try default preset names with default PSK if normal channel match failed if (!decrypted && p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP) { if (channels.setDefaultPresetCryptoForHash(p->channel)) { @@ -429,14 +430,22 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p) decodedtmp.portnum != meshtastic_PortNum_UNKNOWN_APP) { p->decoded = decodedtmp; p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; - // Map to our primary channel index so subsequent processing and re-encoding use local preset - chIndex = channels.getPrimaryIndex(); + // Map to our local default channel index (name+PSK default), not necessarily primary + ChannelIndex defaultIndex = channels.getPrimaryIndex(); + for (ChannelIndex i = 0; i < channels.getNumChannels(); ++i) { + if (channels.isDefaultChannel(i)) { + defaultIndex = i; + break; + } + } + chIndex = defaultIndex; decrypted = true; } else { LOG_WARN("UDP fallback decode attempted but failed for hash 0x%x", p->channel); } } } +#endif if (decrypted) { // parsing was successful p->channel = chIndex; // change to store the index instead of the hash diff --git a/src/mesh/udp/UdpMulticastHandler.h b/src/mesh/udp/UdpMulticastHandler.h index 104e1a711..b475e171c 100644 --- a/src/mesh/udp/UdpMulticastHandler.h +++ b/src/mesh/udp/UdpMulticastHandler.h @@ -50,11 +50,12 @@ class UdpMulticastHandler final LOG_DEBUG("UDP broadcast from: %s, len=%u", packet.remoteIP().toString().c_str(), packetLength); #endif meshtastic_MeshPacket mp; - mp.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP; LOG_DEBUG("Decoding MeshPacket from UDP len=%u", packetLength); bool isPacketDecoded = pb_decode_from_bytes(packet.data(), packetLength, &meshtastic_MeshPacket_msg, &mp); - if (isPacketDecoded && router && mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag) { + if (isPacketDecoded) { mp.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP; + } + if (isPacketDecoded && router && mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag) { mp.pki_encrypted = false; mp.public_key.size = 0; memset(mp.public_key.bytes, 0, sizeof(mp.public_key.bytes)); From 6ea3642919335f93a930e9ad32e8bf93717e8265 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Tue, 26 Aug 2025 22:12:10 +0200 Subject: [PATCH 3/4] trunk fmt --- src/mesh/Channels.cpp | 9 ++++++--- variants/esp32s3/t-deck-pro/variant.h | 15 +++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 433727301..68778f71e 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -425,10 +425,13 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) bool Channels::setDefaultPresetCryptoForHash(ChannelHash channelHash) { // Iterate all known presets - for (int preset = _meshtastic_Config_LoRaConfig_ModemPreset_MIN; preset <= _meshtastic_Config_LoRaConfig_ModemPreset_MAX; ++preset) { + for (int preset = _meshtastic_Config_LoRaConfig_ModemPreset_MIN; preset <= _meshtastic_Config_LoRaConfig_ModemPreset_MAX; + ++preset) { const char *name = DisplayFormatters::getModemPresetDisplayName((meshtastic_Config_LoRaConfig_ModemPreset)preset, false); - if (!name) continue; - if (strcmp(name, "Invalid") == 0) continue; // skip invalid placeholder + if (!name) + continue; + if (strcmp(name, "Invalid") == 0) + continue; // skip invalid placeholder uint8_t h = xorHash((const uint8_t *)name, strlen(name)); // Expand default PSK alias 1 to actual bytes and xor into hash uint8_t tmp = h ^ xorHash(defaultpsk, sizeof(defaultpsk)); diff --git a/variants/esp32s3/t-deck-pro/variant.h b/variants/esp32s3/t-deck-pro/variant.h index abe0a772a..35cb99435 100644 --- a/variants/esp32s3/t-deck-pro/variant.h +++ b/variants/esp32s3/t-deck-pro/variant.h @@ -93,11 +93,10 @@ // Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface // code) -#define MODEM_POWER_EN 41 -#define MODEM_PWRKEY 40 -#define MODEM_RST 9 -#define MODEM_RI 7 -#define MODEM_DTR 8 -#define MODEM_RX 10 -#define MODEM_TX 11 - +#define MODEM_POWER_EN 41 +#define MODEM_PWRKEY 40 +#define MODEM_RST 9 +#define MODEM_RI 7 +#define MODEM_DTR 8 +#define MODEM_RX 10 +#define MODEM_TX 11 From 264e574f449d95ea73db1c6ab411a406e888c9b6 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Fri, 29 Aug 2025 19:34:43 +0200 Subject: [PATCH 4/4] Move setting transport mechanism. --- src/mesh/udp/UdpMulticastHandler.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mesh/udp/UdpMulticastHandler.h b/src/mesh/udp/UdpMulticastHandler.h index b475e171c..2df8686a3 100644 --- a/src/mesh/udp/UdpMulticastHandler.h +++ b/src/mesh/udp/UdpMulticastHandler.h @@ -52,10 +52,8 @@ class UdpMulticastHandler final meshtastic_MeshPacket mp; LOG_DEBUG("Decoding MeshPacket from UDP len=%u", packetLength); bool isPacketDecoded = pb_decode_from_bytes(packet.data(), packetLength, &meshtastic_MeshPacket_msg, &mp); - if (isPacketDecoded) { - mp.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP; - } if (isPacketDecoded && router && mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag) { + mp.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP; mp.pki_encrypted = false; mp.public_key.size = 0; memset(mp.public_key.bytes, 0, sizeof(mp.public_key.bytes));