mirror of
https://github.com/meshtastic/firmware.git
synced 2026-11-06 06:14:45 -05:00
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>
98 lines
4.2 KiB
C++
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;
|
|
}
|