Skip to content
3 changes: 3 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,9 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()

#if !(MESHTASTIC_EXCLUDE_PKI)
deviceMetadata.hasPKC = true;
#endif
#if !(MESHTASTIC_EXCLUDE_PKI) && !(MESHTASTIC_EXCLUDE_XEDDSA)
deviceMetadata.has_xeddsa = true;
#endif
return deviceMetadata;
}
Expand Down
37 changes: 18 additions & 19 deletions src/mesh/FloodingRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
LOG_DEBUG("Repeated reliable tx");
// Check if it's still in the Tx queue, if not, we have to relay it again
if (!findInTxQueue(p->from, p->id)) {
reprocessPacket(p);
perhapsRebroadcast(p);
if (reprocessPacket(p))
perhapsRebroadcast(p);
}
} else {
perhapsCancelDupe(p);
Expand All @@ -68,14 +68,21 @@ bool FloodingRouter::perhapsHandleUpgradedPacket(const meshtastic_MeshPacket *p)
{
// isRebroadcaster() is duplicated in perhapsRebroadcast(), but this avoids confusing log messages
if (isRebroadcaster() && iface && p->hop_limit > 0) {
// Verify the replacement before deleting the valid lower-hop copy waiting in the TX queue.
// This is intentionally redundant with ReliableRouter's ingress gate: it keeps this helper
// safe if another caller is introduced later.
if (passesRoutingAuthGate(const_cast<meshtastic_MeshPacket *>(p)) != RoutingAuthVerdict::ACCEPT)
return true;

// If we overhear a duplicate copy of the packet with more hops left than the one we are waiting to
// rebroadcast, then remove the packet currently sitting in the TX queue and use this one instead.
uint8_t dropThreshold = p->hop_limit; // remove queued packets that have fewer hops remaining
if (iface->removePendingTXPacket(getFrom(p), p->id, dropThreshold)) {
LOG_DEBUG("Processing upgraded packet 0x%08x for rebroadcast with hop limit %d (dropping queued < %d)", p->id,
p->hop_limit, dropThreshold);

reprocessPacket(p);
if (!reprocessPacket(p))
return true;
perhapsRebroadcast(p);

rxDupe++;
Expand All @@ -87,32 +94,24 @@ bool FloodingRouter::perhapsHandleUpgradedPacket(const meshtastic_MeshPacket *p)
return false;
}

void FloodingRouter::reprocessPacket(const meshtastic_MeshPacket *p)
bool FloodingRouter::reprocessPacket(const meshtastic_MeshPacket *p)
{
if (p->which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
auto decodedState = perhapsDecode(const_cast<meshtastic_MeshPacket *>(p));
if (decodedState != DecodeState::DECODE_SUCCESS && decodedState != DecodeState::DECODE_OPAQUE)
return false;
}

if (nodeDB)
nodeDB->updateFrom(*p);

#if !MESHTASTIC_EXCLUDE_TRACEROUTE
if (traceRouteModule && p->which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
// If we got a packet that is not decoded, try to decode it so we can check for traceroute.
auto decodedState = perhapsDecode(const_cast<meshtastic_MeshPacket *>(p));
if (decodedState == DecodeState::DECODE_SUCCESS) {
// parsing was successful, print for debugging
printPacket("reprocessPacket(DUP)", p);
} else {
// Fatal decoding error, we can't do anything with this packet
LOG_WARN(
"FloodingRouter::reprocessPacket: Fatal decode error (state=%d, id=0x%08x, from=%u), can't check for traceroute",
static_cast<int>(decodedState), p->id, getFrom(p));
return;
}
}

if (traceRouteModule && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
p->decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP) {
traceRouteModule->processUpgradedPacket(*p);
}
#endif
return true;
}

bool FloodingRouter::roleAllowsCancelingDupe(const meshtastic_MeshPacket *p)
Expand Down
4 changes: 2 additions & 2 deletions src/mesh/FloodingRouter.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class FloodingRouter : public Router
bool perhapsHandleUpgradedPacket(const meshtastic_MeshPacket *p);

/* Call when we receive a packet that needs some reprocessing, but afterwards should be filtered */
void reprocessPacket(const meshtastic_MeshPacket *p);
bool reprocessPacket(const meshtastic_MeshPacket *p);

// Return false for roles like ROUTER which should always rebroadcast even when we've heard another rebroadcast of
// the same packet
Expand All @@ -75,4 +75,4 @@ class FloodingRouter : public Router

// Return true if we are a rebroadcaster
bool isRebroadcaster();
};
};
26 changes: 22 additions & 4 deletions src/mesh/NextHopRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@

NextHopRouter::NextHopRouter() {}

bool NextHopRouter::relayOpaquePacket(const meshtastic_MeshPacket *p)
{
// Opaque traffic is never admitted to PacketHistory, NodeDB, modules, phone, MQTT, or ACK
// handling. Relay only from the immutable outer routing header and let hop exhaustion bound it.
const auto mode = config.device.rebroadcast_mode;
if (!iface || isToUs(p) || isFromUs(p) || p->id == 0 || p->hop_limit == 0 || !isRebroadcaster() || owner.is_licensed ||
!IS_ONE_OF(mode, meshtastic_Config_DeviceConfig_RebroadcastMode_ALL,
meshtastic_Config_DeviceConfig_RebroadcastMode_ALL_SKIP_DECODING) ||
(p->next_hop != NO_NEXT_HOP_PREFERENCE && p->next_hop != nodeDB->getLastByteOfNodeNum(getNodeNum())))
return false;

meshtastic_MeshPacket *relay = packetPool.allocCopy(*p);
if (!relay)
return false;
relay->hop_limit--;
relay->relay_node = nodeDB->getLastByteOfNodeNum(getNodeNum());
return Router::send(relay) == ERRNO_OK;
}

PendingPacket::PendingPacket(meshtastic_MeshPacket *p, uint8_t numRetransmissions)
{
packet = p;
Expand Down Expand Up @@ -65,16 +84,15 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
LOG_INFO("Fallback to flooding from relay_node=0x%x", p->relay_node);
// Check if it's still in the Tx queue, if not, we have to relay it again
if (!findInTxQueue(p->from, p->id)) {
reprocessPacket(p);
perhapsRebroadcast(p);
if (reprocessPacket(p))
perhapsRebroadcast(p);
}
} else {
bool isRepeated = getHopsAway(*p) == 0;
// If repeated and not in Tx queue anymore, try relaying again, or if we are the destination, send the ACK again
if (isRepeated) {
if (!findInTxQueue(p->from, p->id)) {
reprocessPacket(p);
if (!perhapsRebroadcast(p) && isToUs(p) && p->want_ack) {
if (reprocessPacket(p) && !perhapsRebroadcast(p) && isToUs(p) && p->want_ack) {
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/mesh/NextHopRouter.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class NextHopRouter : public FloodingRouter
* @return true to abandon the packet
*/
virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) override;
bool relayOpaquePacket(const meshtastic_MeshPacket *p) override;

/**
* Look for packets we need to relay
Expand Down Expand Up @@ -213,4 +214,4 @@ class NextHopRouter : public FloodingRouter
/** Check if we should be rebroadcasting this packet if so, do so.
* @return true if we did rebroadcast */
bool perhapsRebroadcast(const meshtastic_MeshPacket *p) override;
};
};
4 changes: 4 additions & 0 deletions src/mesh/NodeDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1861,6 +1861,8 @@ bool NodeDB::enforceSatelliteCaps()
// them if they do); otherwise tracker/sensor/tak_tracker are role-protected.
static uint8_t warmProtectedCategory(const meshtastic_NodeInfoLite &n)
{
if (nodeInfoLiteHasXeddsaSigned(&n))
return static_cast<uint8_t>(WarmProtected::XeddsaSigner);
if (n.bitfield & (NODEINFO_BITFIELD_IS_FAVORITE_MASK | NODEINFO_BITFIELD_IS_IGNORED_MASK |
NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK))
return static_cast<uint8_t>(WarmProtected::Flag);
Expand Down Expand Up @@ -3934,6 +3936,8 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
lite->public_key.size = 32;
memcpy(lite->public_key.bytes, warm.public_key, 32);
}
if (warmProtOf(warm) == static_cast<uint8_t>(WarmProtected::XeddsaSigner))
nodeInfoLiteSetBit(lite, NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_MASK, true);
LOG_MIGRATION("Rehydrated node 0x%08x from warm tier (key=%d)", n, lite->public_key.size == 32);
}
#endif
Expand Down
Loading
Loading