Skip to content

Commit 72aac12

Browse files
authored
Merge branch 'develop' into agent/keyverification-allocation-safety
2 parents dbe41e4 + 6e48fac commit 72aac12

20 files changed

Lines changed: 1213 additions & 158 deletions

File tree

src/main.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,23 @@ void setup()
11111111
#endif
11121112
#endif
11131113

1114+
#if defined(SENSECAP_INDICATOR)
1115+
// The ST7701 panel shares SCK/MOSI/MISO (41/48/47) with the SX1262, and its host is SPI2_HOST,
1116+
// which on the S3 is the same peripheral as the Arduino `SPI` object (FSPI == SPI2).
1117+
// LovyanGFX bit-bangs the ST7701 init sequence on those pins, and because this variant builds
1118+
// with USE_ARDUINO_HAL_GPIO it does so via Arduino pinMode()/digitalWrite(). pinMode() calls
1119+
// perimanSetPinBus(.., ESP32_BUS_TYPE_GPIO, ..), whose deinit callback (spiDetachBus_SCK) ends
1120+
// up in spiStopBus() and gates the SPI2 clock. RadioLib then spins forever in spiTransferByte()
1121+
// waiting on cmd.update, which never clears on a stopped peripheral, and the watchdog fires.
1122+
//
1123+
// Restart the bus here, after the panel is up and before the radio is touched. Note that
1124+
// SPIClass::begin() early-returns when _spi is already non-NULL, so end() first is required.
1125+
SPI.end();
1126+
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, -1); // CS is an IO-expander pin, driven by RadioLib
1127+
SPI.setFrequency(4000000);
1128+
LOG_DEBUG("SPI2 restarted after ST7701 init (SCK=%d, MISO=%d, MOSI=%d)", LORA_SCK, LORA_MISO, LORA_MOSI);
1129+
#endif
1130+
11141131
auto rIf = initLoRa();
11151132

11161133
lateInitVariant(); // Do board specific init (see extra_variants/README.md for documentation)
@@ -1278,6 +1295,9 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
12781295

12791296
#if !(MESHTASTIC_EXCLUDE_PKI)
12801297
deviceMetadata.hasPKC = true;
1298+
#endif
1299+
#if !(MESHTASTIC_EXCLUDE_PKI) && !(MESHTASTIC_EXCLUDE_XEDDSA)
1300+
deviceMetadata.has_xeddsa = true;
12811301
#endif
12821302
return deviceMetadata;
12831303
}

src/mesh/FloodingRouter.cpp

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
5151
LOG_DEBUG("Repeated reliable tx");
5252
// Check if it's still in the Tx queue, if not, we have to relay it again
5353
if (!findInTxQueue(p->from, p->id)) {
54-
reprocessPacket(p);
55-
perhapsRebroadcast(p);
54+
if (reprocessPacket(p))
55+
perhapsRebroadcast(p);
5656
}
5757
} else {
5858
perhapsCancelDupe(p);
@@ -68,14 +68,21 @@ bool FloodingRouter::perhapsHandleUpgradedPacket(const meshtastic_MeshPacket *p)
6868
{
6969
// isRebroadcaster() is duplicated in perhapsRebroadcast(), but this avoids confusing log messages
7070
if (isRebroadcaster() && iface && p->hop_limit > 0) {
71+
// Verify the replacement before deleting the valid lower-hop copy waiting in the TX queue.
72+
// This is intentionally redundant with ReliableRouter's ingress gate: it keeps this helper
73+
// safe if another caller is introduced later.
74+
if (passesRoutingAuthGate(const_cast<meshtastic_MeshPacket *>(p)) != RoutingAuthVerdict::ACCEPT)
75+
return true;
76+
7177
// If we overhear a duplicate copy of the packet with more hops left than the one we are waiting to
7278
// rebroadcast, then remove the packet currently sitting in the TX queue and use this one instead.
7379
uint8_t dropThreshold = p->hop_limit; // remove queued packets that have fewer hops remaining
7480
if (iface->removePendingTXPacket(getFrom(p), p->id, dropThreshold)) {
7581
LOG_DEBUG("Processing upgraded packet 0x%08x for rebroadcast with hop limit %d (dropping queued < %d)", p->id,
7682
p->hop_limit, dropThreshold);
7783

78-
reprocessPacket(p);
84+
if (!reprocessPacket(p))
85+
return true;
7986
perhapsRebroadcast(p);
8087

8188
rxDupe++;
@@ -87,32 +94,24 @@ bool FloodingRouter::perhapsHandleUpgradedPacket(const meshtastic_MeshPacket *p)
8794
return false;
8895
}
8996

90-
void FloodingRouter::reprocessPacket(const meshtastic_MeshPacket *p)
97+
bool FloodingRouter::reprocessPacket(const meshtastic_MeshPacket *p)
9198
{
99+
if (p->which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
100+
auto decodedState = perhapsDecode(const_cast<meshtastic_MeshPacket *>(p));
101+
if (decodedState != DecodeState::DECODE_SUCCESS && decodedState != DecodeState::DECODE_OPAQUE)
102+
return false;
103+
}
104+
92105
if (nodeDB)
93106
nodeDB->updateFrom(*p);
94107

95108
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
96-
if (traceRouteModule && p->which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
97-
// If we got a packet that is not decoded, try to decode it so we can check for traceroute.
98-
auto decodedState = perhapsDecode(const_cast<meshtastic_MeshPacket *>(p));
99-
if (decodedState == DecodeState::DECODE_SUCCESS) {
100-
// parsing was successful, print for debugging
101-
printPacket("reprocessPacket(DUP)", p);
102-
} else {
103-
// Fatal decoding error, we can't do anything with this packet
104-
LOG_WARN(
105-
"FloodingRouter::reprocessPacket: Fatal decode error (state=%d, id=0x%08x, from=%u), can't check for traceroute",
106-
static_cast<int>(decodedState), p->id, getFrom(p));
107-
return;
108-
}
109-
}
110-
111109
if (traceRouteModule && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
112110
p->decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP) {
113111
traceRouteModule->processUpgradedPacket(*p);
114112
}
115113
#endif
114+
return true;
116115
}
117116

118117
bool FloodingRouter::roleAllowsCancelingDupe(const meshtastic_MeshPacket *p)

src/mesh/FloodingRouter.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class FloodingRouter : public Router
6464
bool perhapsHandleUpgradedPacket(const meshtastic_MeshPacket *p);
6565

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

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

7676
// Return true if we are a rebroadcaster
7777
bool isRebroadcaster();
78-
};
78+
};

src/mesh/NextHopRouter.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@
1111

1212
NextHopRouter::NextHopRouter() {}
1313

14+
bool NextHopRouter::relayOpaquePacket(const meshtastic_MeshPacket *p)
15+
{
16+
// Opaque traffic is never admitted to PacketHistory, NodeDB, modules, phone, MQTT, or ACK
17+
// handling. Relay only from the immutable outer routing header and let hop exhaustion bound it.
18+
const auto mode = config.device.rebroadcast_mode;
19+
if (!iface || isToUs(p) || isFromUs(p) || p->id == 0 || p->hop_limit == 0 || !isRebroadcaster() || owner.is_licensed ||
20+
!IS_ONE_OF(mode, meshtastic_Config_DeviceConfig_RebroadcastMode_ALL,
21+
meshtastic_Config_DeviceConfig_RebroadcastMode_ALL_SKIP_DECODING) ||
22+
(p->next_hop != NO_NEXT_HOP_PREFERENCE && p->next_hop != nodeDB->getLastByteOfNodeNum(getNodeNum())))
23+
return false;
24+
25+
meshtastic_MeshPacket *relay = packetPool.allocCopy(*p);
26+
if (!relay)
27+
return false;
28+
relay->hop_limit--;
29+
relay->relay_node = nodeDB->getLastByteOfNodeNum(getNodeNum());
30+
return Router::send(relay) == ERRNO_OK;
31+
}
32+
1433
PendingPacket::PendingPacket(meshtastic_MeshPacket *p, uint8_t numRetransmissions)
1534
{
1635
packet = p;
@@ -65,16 +84,15 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
6584
LOG_INFO("Fallback to flooding from relay_node=0x%x", p->relay_node);
6685
// Check if it's still in the Tx queue, if not, we have to relay it again
6786
if (!findInTxQueue(p->from, p->id)) {
68-
reprocessPacket(p);
69-
perhapsRebroadcast(p);
87+
if (reprocessPacket(p))
88+
perhapsRebroadcast(p);
7089
}
7190
} else {
7291
bool isRepeated = getHopsAway(*p) == 0;
7392
// If repeated and not in Tx queue anymore, try relaying again, or if we are the destination, send the ACK again
7493
if (isRepeated) {
7594
if (!findInTxQueue(p->from, p->id)) {
76-
reprocessPacket(p);
77-
if (!perhapsRebroadcast(p) && isToUs(p) && p->want_ack) {
95+
if (reprocessPacket(p) && !perhapsRebroadcast(p) && isToUs(p) && p->want_ack) {
7896
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0);
7997
}
8098
}

src/mesh/NextHopRouter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class NextHopRouter : public FloodingRouter
137137
* @return true to abandon the packet
138138
*/
139139
virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) override;
140+
bool relayOpaquePacket(const meshtastic_MeshPacket *p) override;
140141

141142
/**
142143
* Look for packets we need to relay
@@ -213,4 +214,4 @@ class NextHopRouter : public FloodingRouter
213214
/** Check if we should be rebroadcasting this packet if so, do so.
214215
* @return true if we did rebroadcast */
215216
bool perhapsRebroadcast(const meshtastic_MeshPacket *p) override;
216-
};
217+
};

src/mesh/NodeDB.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,8 @@ bool NodeDB::enforceSatelliteCaps()
18611861
// them if they do); otherwise tracker/sensor/tak_tracker are role-protected.
18621862
static uint8_t warmProtectedCategory(const meshtastic_NodeInfoLite &n)
18631863
{
1864+
if (nodeInfoLiteHasXeddsaSigned(&n))
1865+
return static_cast<uint8_t>(WarmProtected::XeddsaSigner);
18641866
if (n.bitfield & (NODEINFO_BITFIELD_IS_FAVORITE_MASK | NODEINFO_BITFIELD_IS_IGNORED_MASK |
18651867
NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK))
18661868
return static_cast<uint8_t>(WarmProtected::Flag);
@@ -3934,6 +3936,8 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
39343936
lite->public_key.size = 32;
39353937
memcpy(lite->public_key.bytes, warm.public_key, 32);
39363938
}
3939+
if (warmProtOf(warm) == static_cast<uint8_t>(WarmProtected::XeddsaSigner))
3940+
nodeInfoLiteSetBit(lite, NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_MASK, true);
39373941
LOG_MIGRATION("Rehydrated node 0x%08x from warm tier (key=%d)", n, lite->public_key.size == 32);
39383942
}
39393943
#endif

0 commit comments

Comments
 (0)