Files
firmware/src/modules/RoutingModule.cpp
T
9461670f49 Add relaying node and RSSI/SNR to the implicit-ack routing packet (#10767)
When we overhear another node rebroadcast one of our own packets, we
generate an implicit-ack ROUTING packet for the local sending process.
That ack is delivered locally to the phone, so pass the overheard
rebroadcast as a relay source and copy its relay_node and the rx_rssi /
rx_snr we heard it at onto the ack. This lets the connected client see
which node relayed our packet and the link quality, instead of only
learning that the packet was repeated.

allocAckNak / sendAckNak gain an optional relaySource parameter. The
ack is sent to ourselves (to == us), so Router::send() is bypassed and
does not overwrite these fields with our own.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-08-26 22:01:57 +00:00

98 lines
4.2 KiB
C++

#include "RoutingModule.h"
#include "Default.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
RoutingModule *routingModule;
bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Routing *r)
{
bool maybePKI = mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && mp.channel == 0 && !isBroadcast(mp.to);
// Beginning of logic whether to drop the packet based on Rebroadcast mode
if (mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag &&
(config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY ||
config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY)) {
if (!maybePKI)
return false;
if (!nodeInfoLiteHasUser(nodeDB->getMeshNode(mp.from)) && !nodeInfoLiteHasUser(nodeDB->getMeshNode(mp.to)))
return false;
} else if (owner.is_licensed && ((nodeDB->getLicenseStatus(mp.from) == UserLicenseStatus::NotLicensed) ||
(nodeDB->getLicenseStatus(mp.to) == UserLicenseStatus::NotLicensed))) {
// Don't let licensed users to rebroadcast packets to or from unlicensed users
// If we know they are in-fact unlicensed
LOG_DEBUG("Packet to or from unlicensed user, ignoring packet");
return false;
}
printPacket("Routing sniffing", &mp);
router->sniffReceived(&mp, r);
// FIXME - move this to a non promsicious PhoneAPI module?
// Note: we are careful not to send back packets that started with the phone back to the phone
if ((isBroadcast(mp.to) || isToUs(&mp)) && (mp.from != 0)) {
printPacket("Delivering rx packet", &mp);
service->handleFromRadio(&mp);
}
return false; // Let others look at this message also if they want
}
meshtastic_MeshPacket *RoutingModule::allocReply()
{
assert(currentRequest);
return NULL;
}
void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit,
bool ackWantsAck, const meshtastic_MeshPacket *relaySource)
{
auto p = allocAckNak(err, to, idFrom, chIndex, hopLimit, relaySource);
if (!p)
return;
// Allow the caller to set want_ack on this ACK packet if it's important that the ACK be delivered reliably
p->want_ack = ackWantsAck;
if (router->sendLocal(p) == ERRNO_SHOULD_RELEASE) // we sometimes send directly to the local node
packetPool.release(p);
}
uint8_t RoutingModule::getHopLimitForResponse(const meshtastic_MeshPacket &mp)
{
const int8_t hopsUsed = getHopsAway(mp);
const uint8_t responseHopLimit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
if (hopsUsed >= 0) {
if (hopsUsed > static_cast<int32_t>(responseHopLimit)) {
// In event mode, never exceed the configured event hop limit.
#if !USERPREFS_EVENT_MODE // This falls through to the default.
return hopsUsed; // If the request used more hops than the limit, use the same amount of hops
#endif
} else if (mp.hop_start == 0) {
return 0; // The requesting node wanted 0 hops, so the response also uses a direct/local path.
} else if (static_cast<uint8_t>(hopsUsed + 2) < responseHopLimit) {
return hopsUsed + 2; // Use only the amount of hops needed with some margin as the way back may be different
}
}
return responseHopLimit;
}
meshtastic_MeshPacket *RoutingModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,
uint8_t hopLimit, const meshtastic_MeshPacket *relaySource)
{
return MeshModule::allocAckNak(err, to, idFrom, chIndex, hopLimit, relaySource);
}
RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
{
isPromiscuous = true;
// moved the RebroadcastMode logic into handleReceivedProtobuf
// LocalOnly requires either the from or to to be a known node
// knownOnly specifically requires the from to be a known node.
encryptedOk = true;
}