Skip to content

Commit f2751f4

Browse files
committed
fix(transport): relay announces among peers of a multipoint interface
BLE Peer presents many hidden-from-each-other GATT links as one interface; without back-out relay a hub never propagates announces between its peers, breaking multi-hop discovery past two nodes.
1 parent 7d5ee21 commit f2751f4

3 files changed

Lines changed: 59 additions & 2 deletions

File tree

crates/rns-runtime/src/reticulum.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,7 @@ pub async fn init(
13371337
role,
13381338
ingress_overrides,
13391339
&reg_controls,
1340+
false,
13401341
)
13411342
.await;
13421343
}
@@ -1671,6 +1672,7 @@ async fn register_interface_handle_with_role(
16711672
role,
16721673
rns_transport::ingress::IngressOverrides::default(),
16731674
interface_controls,
1675+
false,
16741676
)
16751677
.await;
16761678
}
@@ -1681,6 +1683,7 @@ async fn register_interface_handle_with_role_and_overrides(
16811683
role: rns_transport::messages::InterfaceRole,
16821684
ingress_overrides: rns_transport::ingress::IngressOverrides,
16831685
interface_controls: &InterfaceControlMap,
1686+
multipoint: bool,
16841687
) {
16851688
let name = handle.name.clone();
16861689
let id = handle.id;
@@ -1724,6 +1727,7 @@ async fn register_interface_handle_with_role_and_overrides(
17241727
tx_drops: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0)),
17251728
ingress,
17261729
announce_queue: Vec::new(),
1730+
multipoint,
17271731
};
17281732
if let Err(e) = transport_tx
17291733
.send(TransportMessage::RegisterInterface { id, entry })
@@ -1788,6 +1792,7 @@ async fn register_interface_with_post_init(
17881792
tx_drops: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0)),
17891793
ingress,
17901794
announce_queue: Vec::new(),
1795+
multipoint: false,
17911796
};
17921797
if let Err(e) = transport_tx
17931798
.send(TransportMessage::RegisterInterface { id, entry })
@@ -3223,10 +3228,15 @@ pub async fn spawn_ble_peer_runtime(
32233228
format!("BLE Peer spawn failed: {e}")
32243229
})?;
32253230

3226-
register_interface_handle(
3231+
// multipoint = true: BLE peers can't hear each other, so the transport must
3232+
// relay announces back out this interface to reach its other peers.
3233+
register_interface_handle_with_role_and_overrides(
32273234
&handle.transport_tx,
32283235
iface_handle,
3236+
rns_transport::messages::InterfaceRole::Normal,
3237+
rns_transport::ingress::IngressOverrides::default(),
32293238
&handle.interface_controls,
3239+
true,
32303240
)
32313241
.await;
32323242
tracing::info!(name = %name, id, "runtime BLE Peer mesh interface spawned");

crates/rns-transport/src/actor/mod.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,16 @@ impl TransportActor {
10481048
let Some(entry) = self.interfaces.get(&id) else {
10491049
return false;
10501050
};
1051-
if except == Some(id) || !entry.direction.outbound {
1051+
if !entry.direction.outbound {
1052+
return false;
1053+
}
1054+
// Normally an announce is never sent back out its source interface
1055+
// (loopback / dedup churn on a shared medium). A multipoint interface
1056+
// (e.g. BLE Peer) is the exception: its peers can't hear each other, so
1057+
// relaying back out reaches the ones the source peer couldn't. The
1058+
// driver's per-peer anti-loop filter keeps it off the source peer, and
1059+
// the announce-table dedup + hop cap bound propagation.
1060+
if except == Some(id) && !entry.multipoint {
10521061
return false;
10531062
}
10541063

@@ -1544,6 +1553,7 @@ mod tests {
15441553
rxb: None,
15451554
txb: None,
15461555
tx_drops: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0)),
1556+
multipoint: false,
15471557
ingress: crate::ingress::IngressController::new(),
15481558
announce_queue: Vec::new(),
15491559
};
@@ -1596,6 +1606,30 @@ mod tests {
15961606
assert_eq!(actor.interfaces.get(&1).unwrap().name, "test_iface");
15971607
}
15981608

1609+
#[test]
1610+
fn test_multipoint_interface_relays_announce_to_source() {
1611+
let (mut actor, _tx) = TransportActor::new();
1612+
let dest = [0xAB; 16];
1613+
1614+
// Point-to-multipoint medium whose peers can't hear each other:
1615+
// an announce arriving here must be allowed back out the same
1616+
// interface. A normal interface must still exclude its source.
1617+
let (normal, _rx) = make_test_interface("normal");
1618+
actor.interfaces.insert(1, normal);
1619+
let (mut mp, _rx2) = make_test_interface("ble");
1620+
mp.multipoint = true;
1621+
actor.interfaces.insert(2, mp);
1622+
1623+
assert!(
1624+
!actor.interface_allows_announce(1, &dest, Some(1)),
1625+
"normal interface must not relay an announce back to its source"
1626+
);
1627+
assert!(
1628+
actor.interface_allows_announce(2, &dest, Some(2)),
1629+
"multipoint interface must relay an announce back to its other peers"
1630+
);
1631+
}
1632+
15991633
#[test]
16001634
fn test_deregister_interface() {
16011635
let (mut actor, _tx) = TransportActor::new();

crates/rns-transport/src/messages.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ pub struct InterfaceEntry {
9696
/// Announces awaiting bandwidth-capped retransmission. Drained in
9797
/// hop-priority order (lowest hops first, oldest among ties).
9898
pub announce_queue: Vec<QueuedAnnounce>,
99+
/// Multipoint medium whose peers cannot hear each other (e.g. BLE Peer:
100+
/// each peer is a separate point-to-point GATT link presented as one
101+
/// interface). Unlike a shared broadcast medium, an announce arriving on
102+
/// such an interface must be relayed back out the SAME interface to reach
103+
/// its other peers. Loop safety comes from the announce-table dedup + hop
104+
/// cap and the driver's own per-peer anti-loop filter.
105+
pub multipoint: bool,
99106
}
100107

101108
impl InterfaceEntry {
@@ -131,9 +138,15 @@ impl InterfaceEntry {
131138
tx_drops: Arc::new(std::sync::atomic::AtomicU64::new(0)),
132139
ingress: IngressController::new(),
133140
announce_queue: Vec::new(),
141+
multipoint: false,
134142
}
135143
}
136144

145+
pub fn with_multipoint(mut self, multipoint: bool) -> Self {
146+
self.multipoint = multipoint;
147+
self
148+
}
149+
137150
pub fn with_ifac(mut self, key: [u8; 64], size: usize) -> Self {
138151
self.ifac_key = Some(key);
139152
self.ifac_size = size;

0 commit comments

Comments
 (0)