From 683539220d8297729d5134bd87b9108575d801fc Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Sep 2025 05:55:04 -0500 Subject: [PATCH 1/6] Re-use Uplink / downlink controls for UDP --- src/mesh/Router.cpp | 14 +++++++++++--- src/mesh/udp/UdpMulticastHandler.h | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 171c383ba3..a92cc502ab 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -355,13 +355,21 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) if (moduleConfig.mqtt.enabled && isFromUs(p) && mqtt) { mqtt->onSend(*p, *p_decoded, chIndex); } +#endif +#if HAS_UDP_MULTICAST + // Only publish to UDP if we're the original transmitter of the packet + if (udpHandler && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST && + isFromUs(p)) { + udpHandler->onSend(const_cast(p), chIndex); + } #endif packetPool.release(p_decoded); } - #if HAS_UDP_MULTICAST - if (udpHandler && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST) { - udpHandler->onSend(const_cast(p)); + // For packets that are already encrypted, we need to determine the channel from packet info + else if (udpHandler && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST && + isFromUs(p)) { + udpHandler->onSend(const_cast(p), p->channel); } #endif diff --git a/src/mesh/udp/UdpMulticastHandler.h b/src/mesh/udp/UdpMulticastHandler.h index 2df8686a31..0a27a316ee 100644 --- a/src/mesh/udp/UdpMulticastHandler.h +++ b/src/mesh/udp/UdpMulticastHandler.h @@ -2,6 +2,7 @@ #if HAS_UDP_MULTICAST #include "configuration.h" #include "main.h" +#include "mesh/Channels.h" #include "mesh/Router.h" #if HAS_ETHERNET && defined(ARCH_NRF52) @@ -53,6 +54,13 @@ 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) { + // Check if downlink is enabled for this channel + auto &ch = channels.getByIndex(mp.channel); + if (!ch.settings.downlink_enabled) { + LOG_DEBUG("UDP downlink disabled for channel %d", mp.channel); + return; + } + mp.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP; mp.pki_encrypted = false; mp.public_key.size = 0; @@ -65,11 +73,17 @@ class UdpMulticastHandler final } } - bool onSend(const meshtastic_MeshPacket *mp) + bool onSend(const meshtastic_MeshPacket *mp, ChannelIndex chIndex) { if (!mp || !udp) { return false; } + // Check if uplink is enabled for this specific channel + auto &ch = channels.getByIndex(chIndex); + if (!ch.settings.uplink_enabled) { + LOG_DEBUG("UDP uplink disabled for channel %d", chIndex); + return false; + } #if defined(ARCH_NRF52) if (!isEthernetAvailable()) { return false; From 42532d5b7ca9cdb3e1cc9b5ad4473d58f03ef8fa Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Sep 2025 05:57:08 -0500 Subject: [PATCH 2/6] Size --- src/mesh/Router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index a92cc502ab..453c06ddaa 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -90,7 +90,7 @@ bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p) // For subsequent hops, check if previous relay is a favorite router // Optimized search for favorite routers with matching last byte // Check ordering optimized for IoT devices (cheapest checks first) - for (int i = 0; i < nodeDB->getNumMeshNodes(); i++) { + for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); if (!node) continue; From 2b590d23004ee26dd8283d4109656353187552df Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Sep 2025 06:06:05 -0500 Subject: [PATCH 3/6] Update src/mesh/udp/UdpMulticastHandler.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/mesh/udp/UdpMulticastHandler.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mesh/udp/UdpMulticastHandler.h b/src/mesh/udp/UdpMulticastHandler.h index 0a27a316ee..897c2eb473 100644 --- a/src/mesh/udp/UdpMulticastHandler.h +++ b/src/mesh/udp/UdpMulticastHandler.h @@ -78,6 +78,11 @@ class UdpMulticastHandler final if (!mp || !udp) { return false; } + // Validate channel index before accessing + if (chIndex < 0 || chIndex >= channels.size()) { + LOG_ERROR("Invalid channel index %d in UDP onSend", chIndex); + return false; + } // Check if uplink is enabled for this specific channel auto &ch = channels.getByIndex(chIndex); if (!ch.settings.uplink_enabled) { From 888115e0b908f307ded8889438f41bf1650c598b Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Sep 2025 06:10:04 -0500 Subject: [PATCH 4/6] Revert "Update src/mesh/udp/UdpMulticastHandler.h" This reverts commit 2b590d23004ee26dd8283d4109656353187552df. --- src/mesh/udp/UdpMulticastHandler.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/mesh/udp/UdpMulticastHandler.h b/src/mesh/udp/UdpMulticastHandler.h index 897c2eb473..0a27a316ee 100644 --- a/src/mesh/udp/UdpMulticastHandler.h +++ b/src/mesh/udp/UdpMulticastHandler.h @@ -78,11 +78,6 @@ class UdpMulticastHandler final if (!mp || !udp) { return false; } - // Validate channel index before accessing - if (chIndex < 0 || chIndex >= channels.size()) { - LOG_ERROR("Invalid channel index %d in UDP onSend", chIndex); - return false; - } // Check if uplink is enabled for this specific channel auto &ch = channels.getByIndex(chIndex); if (!ch.settings.uplink_enabled) { From c1e8c281288a19ce2b621fcdb4ebdac02edec736 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Sep 2025 16:25:40 -0500 Subject: [PATCH 5/6] Revert "Revert cross-preset UDP bridging" This reverts commit 359a4dbd680e28c39dd0944a9b3f5f0fb83bd91e. --- src/mesh/Router.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index ad33660023..828f700183 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -491,6 +491,35 @@ 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)) { + 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 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 From 87a4a6ab731b9133dfba0d82ec44f558340f3586 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 28 Sep 2025 06:39:15 -0500 Subject: [PATCH 6/6] Fix isFromUs and account for channel hashes instead of indexes --- src/mesh/Channels.cpp | 11 ++++++++ src/mesh/Channels.h | 6 ++-- src/mesh/Router.cpp | 44 ++++++++++++++++++++++++++---- src/mesh/udp/UdpMulticastHandler.h | 11 ++++++-- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 4dcd94e3b9..f3dfae88ed 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -459,4 +459,15 @@ bool Channels::setDefaultPresetCryptoForHash(ChannelHash channelHash) int16_t Channels::setActiveByIndex(ChannelIndex channelIndex) { return setCrypto(channelIndex); +} + +int8_t Channels::getIndexByHash(ChannelHash channelHash) +{ + // Iterate through all channels to find the one with matching hash + for (ChannelIndex i = 0; i < getNumChannels(); i++) { + if (getHash(i) == channelHash) { + return i; + } + } + return -1; // Hash not found } \ No newline at end of file diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h index b53f552fa5..499401c5ca 100644 --- a/src/mesh/Channels.h +++ b/src/mesh/Channels.h @@ -96,6 +96,9 @@ class Channels bool setDefaultPresetCryptoForHash(ChannelHash channelHash); + /** Return the channel index for the specified channel hash, or -1 for not found */ + int8_t getIndexByHash(ChannelHash channelHash); + private: /** Given a channel index, change to use the crypto key specified by that index * @@ -103,9 +106,6 @@ class Channels */ int16_t setCrypto(ChannelIndex chIndex); - /** Return the channel index for the specified channel hash, or -1 for not found */ - int8_t getIndexByHash(ChannelHash channelHash); - /** Given a channel number, return the (0 to 255) hash for that channel * If no suitable channel could be found, return -1 * diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 828f700183..fa15a7c1f8 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -357,10 +357,16 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) } #endif #if HAS_UDP_MULTICAST - // Only publish to UDP if we're the original transmitter of the packet - if (udpHandler && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST && - isFromUs(p)) { - udpHandler->onSend(const_cast(p), chIndex); + if (udpHandler && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST) { + // We must have at least one channel setup for with uplink_enabled + bool uplinkEnabled = false; + for (int i = 0; i <= 7; i++) { + if (channels.getByIndex(i).settings.uplink_enabled) + uplinkEnabled = true; + } + if (uplinkEnabled) { + udpHandler->onSend(const_cast(p), chIndex); + } } #endif packetPool.release(p_decoded); @@ -369,7 +375,35 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) // For packets that are already encrypted, we need to determine the channel from packet info else if (udpHandler && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST && isFromUs(p)) { - udpHandler->onSend(const_cast(p), p->channel); + // Check if any channel has uplink enabled (for PKI support) + bool uplinkEnabled = false; + for (int i = 0; i <= 7; i++) { + if (channels.getByIndex(i).settings.uplink_enabled) { + uplinkEnabled = true; + break; + } + } + if (uplinkEnabled) { + ChannelIndex chIndex = 0; // Default to primary channel + + // For PKI encrypted packets, use default channel if at least one channel has uplink + if (p->pki_encrypted) { + for (int i = 0; i <= 7; i++) { + if (channels.getByIndex(i).settings.uplink_enabled) { + chIndex = i; + break; + } + } + udpHandler->onSend(const_cast(p), chIndex); + } else { + // For regular channel PSK encrypted packets, try to find the channel by hash + int8_t foundIndex = channels.getIndexByHash(p->channel); + if (foundIndex >= 0) { + chIndex = foundIndex; + udpHandler->onSend(const_cast(p), chIndex); + } + } + } } #endif diff --git a/src/mesh/udp/UdpMulticastHandler.h b/src/mesh/udp/UdpMulticastHandler.h index 0a27a316ee..b04785f302 100644 --- a/src/mesh/udp/UdpMulticastHandler.h +++ b/src/mesh/udp/UdpMulticastHandler.h @@ -54,10 +54,17 @@ 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) { + // Convert channel hash to index for downlink check + int8_t chIndex = channels.getIndexByHash(mp.channel); + if (chIndex < 0) { + LOG_DEBUG("UDP received packet with unknown channel hash 0x%x", mp.channel); + return; + } + // Check if downlink is enabled for this channel - auto &ch = channels.getByIndex(mp.channel); + auto &ch = channels.getByIndex(chIndex); if (!ch.settings.downlink_enabled) { - LOG_DEBUG("UDP downlink disabled for channel %d", mp.channel); + LOG_DEBUG("UDP downlink disabled for channel %d", chIndex); return; }