Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,7 @@ bool runASAP;
// TODO find better home than main.cpp
extern meshtastic_DeviceMetadata getDeviceMetadata()
{
meshtastic_DeviceMetadata deviceMetadata;
meshtastic_DeviceMetadata deviceMetadata = meshtastic_DeviceMetadata_init_zero;
strncpy(deviceMetadata.firmware_version, optstr(APP_VERSION), sizeof(deviceMetadata.firmware_version));
deviceMetadata.device_state_version = DEVICESTATE_CUR_VER;
deviceMetadata.canShutdown = pmu_found || HAS_CPU_SHUTDOWN;
Expand Down Expand Up @@ -1262,6 +1262,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 @@ -209,4 +210,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;
};
};
16 changes: 16 additions & 0 deletions src/mesh/NodeDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1855,6 +1855,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 @@ -3719,6 +3721,18 @@ bool NodeDB::copyPublicKey(NodeNum n, meshtastic_NodeInfoLite_public_key_t &out)
return false;
}

bool NodeDB::hasSeenXeddsaSigner(NodeNum n)
{
if (nodeInfoLiteHasXeddsaSigned(getMeshNode(n)))
return true;
#if WARM_NODE_COUNT > 0
uint8_t role = 0, prot = 0;
return warmStore.lookupMeta(n, role, prot) && prot == static_cast<uint8_t>(WarmProtected::XeddsaSigner);
#else
return false;
#endif
}

meshtastic_Config_DeviceConfig_Role NodeDB::getNodeRole(NodeNum n)
{
const meshtastic_NodeInfoLite *info = getMeshNode(n);
Expand Down Expand Up @@ -3811,6 +3825,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
4 changes: 4 additions & 0 deletions src/mesh/NodeDB.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ class NodeDB
/// tier. Returns false if we don't know a key for n.
bool copyPublicKey(NodeNum n, meshtastic_NodeInfoLite_public_key_t &out);

/// Whether this node has produced a verified XEdDSA signature, including while its
/// identity is resident only in the warm tier.
bool hasSeenXeddsaSigner(NodeNum n);

/// Resolve a node's device role - hot store (with user) first, then the role
/// cached in the warm tier, else CLIENT. Lets role-aware policy keep firing for
/// nodes that have aged out of the hot store.
Expand Down
Loading
Loading