mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Merge branch 'refactor-transport' into refactor-next
Forward-merge the transport neighbor-beacon rename (ethernet + BLE discovery -> neighbor module/buffer rename; ethernet config discovery flag -> listen with serde alias). Hand-resolved the module files against next's identity-out-of-beacon beacon rewrite by starting from next's bodies and re-applying the rename by identifier, so next's beacon logic (no-arg build_beacon, bool parse_beacon, computed BEACON_SIZE, one-arg add_peer, deleted BLE add_peer_with_pubkey) is preserved unchanged.
This commit is contained in:
@@ -129,6 +129,13 @@ with v0.4.x or earlier peers.
|
||||
dialing, e.g. configured `auto_connect` peers — is handled by that
|
||||
late check, since those peers return earlier via the cross-connection
|
||||
paths and are not subject to the cap.
|
||||
- The Ethernet transport's per-interface `discovery` flag was renamed to
|
||||
`listen` (`transports.ethernet.*`) to match the symmetric `announce`
|
||||
(transmit) / `listen` (receive) neighbor-beacon vocabulary. The old
|
||||
`discovery:` key is still accepted via a serde alias, so deployed configs
|
||||
continue to load unchanged; `Config::to_yaml()` re-emits it under the
|
||||
canonical `listen:` name. Update your `fips.yaml` to `listen:`.
|
||||
|
||||
- The mesh-lookup control-metrics family is now emitted under the key
|
||||
`lookup` in `fipsctl stats metrics` and `show routing`. The former key
|
||||
`discovery` is still emitted as a deprecated alias carrying identical
|
||||
@@ -150,6 +157,9 @@ with v0.4.x or earlier peers.
|
||||
(mesh-lookup) and `node.rendezvous.*` (peer rendezvous). A legacy
|
||||
`node.discovery:` block still applies for now with a deprecation warning and
|
||||
will be removed; migrate to `node.lookup.*` / `node.rendezvous.*`.
|
||||
- The Ethernet `transports.ethernet.discovery` flag, renamed to
|
||||
`transports.ethernet.listen`. The old key is still accepted via a serde
|
||||
alias and will be removed at the v2 cutover; migrate to `listen`.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ UDP (1500 vs 1472 MTU).
|
||||
- **No IP dependency**: Operates below the IP layer. Nodes on the same
|
||||
Ethernet segment can communicate without IP addresses or routing
|
||||
infrastructure
|
||||
- **Broadcast discovery**: Nodes discover each other via periodic beacon
|
||||
- **Broadcast neighbor detection**: Nodes discover each other via periodic beacon
|
||||
broadcasts on the shared medium, with no static peer configuration required
|
||||
- **Higher MTU**: Standard Ethernet frames carry 1500 bytes of payload,
|
||||
yielding an effective FIPS MTU of 1499 after the frame type prefix
|
||||
@@ -290,7 +290,7 @@ that would otherwise corrupt AEAD verification. Frame types: `0x00` (data),
|
||||
| Addressing | 6-byte MAC address |
|
||||
| Platform | Linux only (`CAP_NET_RAW` required) |
|
||||
|
||||
### Beacon Discovery
|
||||
### Neighbor Beacons
|
||||
|
||||
Ethernet nodes discover peers via broadcast beacons sent to
|
||||
ff:ff:ff:ff:ff:ff. Beacons are minimal 5-byte frames (4-byte header +
|
||||
@@ -299,7 +299,7 @@ learned from the Noise XX handshake after the connection is established.
|
||||
Receiving nodes extract the MAC source address from the frame and report
|
||||
the discovered address to FMP.
|
||||
|
||||
Four configuration flags control discovery behavior — `discovery`
|
||||
Four configuration flags control neighbor behavior — `listen`
|
||||
(listen for beacons), `announce` (broadcast beacons), `auto_connect`
|
||||
(initiate handshakes to discovered peers), and `accept_connections`
|
||||
(accept inbound handshakes). The flag table and per-flag defaults
|
||||
@@ -308,13 +308,13 @@ under `transports.ethernet.*`.
|
||||
|
||||
A typical discoverable node sets `announce`, `auto_connect`, and
|
||||
`accept_connections` all true. A passive listener uses just
|
||||
`discovery: true` to observe the network without announcing itself.
|
||||
`listen: true` to observe the network without announcing itself.
|
||||
|
||||
### WiFi Compatibility
|
||||
|
||||
WiFi interfaces in infrastructure (managed) mode work transparently for
|
||||
unicast — the mac80211 subsystem handles frame translation between 802.11
|
||||
and 802.3. Broadcast beacon discovery is unreliable in managed mode because
|
||||
and 802.3. Broadcast neighbor detection is unreliable in managed mode because
|
||||
access points commonly isolate clients from each other's broadcast traffic.
|
||||
|
||||
Startup logging:
|
||||
@@ -893,7 +893,7 @@ transitions through `Starting` to `Up` (operational). `stop()` moves to
|
||||
| --------- | ------ | ----- |
|
||||
| UDP/IP | **Implemented** | Primary transport, AsyncFd/recvmsg, SO_RXQ_OVFL kernel drop detection |
|
||||
| TCP/IP | **Implemented** | FMP header-based framing, non-blocking connect, per-connection MSS MTU |
|
||||
| Ethernet | **Implemented** | AF_PACKET SOCK_DGRAM, EtherType 0x2121, beacon discovery, Linux only |
|
||||
| Ethernet | **Implemented** | AF_PACKET SOCK_DGRAM, EtherType 0x2121, neighbor beacons, Linux only |
|
||||
| WiFi | **Implemented** (via Ethernet transport, infrastructure mode) | mac80211 translates 802.11↔802.3; broadcast beacons unreliable through APs |
|
||||
| Tor | **Implemented** | Outbound SOCKS5, inbound via onion service, .onion and clearnet addressing |
|
||||
| Nym | **Implemented** | Outbound-only SOCKS5 through nym-socks5-client, mixnet anonymity, IP/hostname addressing |
|
||||
|
||||
@@ -437,7 +437,7 @@ Requires `CAP_NET_RAW` or running as root. Linux only.
|
||||
| `mtu` | u16 | *(auto)* | Override MTU. Default: interface MTU minus 3 (for frame type + length prefix) |
|
||||
| `recv_buf_size` | usize | `2097152` | Socket receive buffer size in bytes (2 MB) |
|
||||
| `send_buf_size` | usize | `2097152` | Socket send buffer size in bytes (2 MB) |
|
||||
| `discovery` | bool | `true` | Listen for discovery beacons from other nodes |
|
||||
| `listen` | bool | `true` | Listen for neighbor beacons from other nodes |
|
||||
| `announce` | bool | `false` | Broadcast announcement beacons on the LAN |
|
||||
| `auto_connect` | bool | `false` | Auto-connect to discovered peers |
|
||||
| `accept_connections` | bool | `false` | Accept incoming connection attempts from discovered peers |
|
||||
@@ -451,7 +451,7 @@ transports:
|
||||
ethernet:
|
||||
lan:
|
||||
interface: "eth0"
|
||||
discovery: true
|
||||
listen: true
|
||||
announce: true
|
||||
backbone:
|
||||
interface: "eth1"
|
||||
@@ -459,7 +459,7 @@ transports:
|
||||
```
|
||||
|
||||
Each named instance operates independently with its own socket and
|
||||
discovery state. The instance name is used in log messages and the
|
||||
neighbor state. The instance name is used in log messages and the
|
||||
`name()` method on the Transport trait.
|
||||
|
||||
### TCP (`transports.tcp.*`)
|
||||
@@ -841,7 +841,7 @@ peers:
|
||||
### Mixed UDP + Ethernet Example
|
||||
|
||||
A node bridging internet peers (UDP) and a local Ethernet segment with
|
||||
beacon discovery:
|
||||
neighbor beacons:
|
||||
|
||||
```yaml
|
||||
node:
|
||||
@@ -857,7 +857,7 @@ transports:
|
||||
mtu: 1472
|
||||
ethernet:
|
||||
interface: "eth0"
|
||||
discovery: true
|
||||
listen: true
|
||||
announce: true
|
||||
auto_connect: true
|
||||
accept_connections: true
|
||||
@@ -1001,7 +1001,7 @@ transports:
|
||||
# mtu: null # null = interface MTU - 3 (typically 1497)
|
||||
# recv_buf_size: 2097152 # 2 MB
|
||||
# send_buf_size: 2097152 # 2 MB
|
||||
# discovery: true # listen for beacons
|
||||
# listen: true # listen for beacons
|
||||
# announce: false # broadcast beacons
|
||||
# auto_connect: false # connect to discovered peers
|
||||
# accept_connections: false # accept inbound handshakes
|
||||
|
||||
@@ -209,7 +209,7 @@ for the metadata-privacy model and the rejection of onion routing.
|
||||
| --------- | --------------- | ------------ | ------ |
|
||||
| UDP | None until `bind_addr` set | `0.0.0.0:2121` typical | Operator sets `transports.udp.bind_addr` |
|
||||
| TCP | None until `bind_addr` set | None — outbound-only without bind | Operator sets `transports.tcp.bind_addr` |
|
||||
| Ethernet | Listens on configured interface (raw `AF_PACKET`) | EtherType 0x2121 on selected interface | Per-flag `discovery`, `announce`, `auto_connect`, `accept_connections` |
|
||||
| Ethernet | Listens on configured interface (raw `AF_PACKET`) | EtherType 0x2121 on selected interface | Per-flag `listen`, `announce`, `auto_connect`, `accept_connections` |
|
||||
| Tor | None until `directory_service` configured | `127.0.0.1:8443` (loopback only) | Operator sets `transports.tor.directory_service` and configures `HiddenServiceDir` in `torrc` |
|
||||
| BLE | Off by default | n/a | Operator enables `transports.ble.*` |
|
||||
| Nostr discovery | Off by default | n/a (relay client, not a listener) | Operator sets `node.discovery.nostr.enabled: true` |
|
||||
|
||||
@@ -71,7 +71,7 @@ supplies the rest:
|
||||
- **Addressing**: the `fips0` adapter takes an `fd97:...` ULA
|
||||
derived from the npub. No DHCP. No SLAAC. The address is
|
||||
cryptographically tied to the identity.
|
||||
- **Discovery**: each daemon broadcasts a small beacon on the
|
||||
- **Neighbor detection**: each daemon broadcasts a small beacon on the
|
||||
link advertising its npub; the other daemon's listener picks
|
||||
it up and dials in over the same link.
|
||||
- **Routing**: the FIPS mesh layer builds its own spanning tree
|
||||
@@ -177,7 +177,7 @@ different interface names — that is normal.
|
||||
|
||||
Edit `/etc/fips/fips.yaml` on **both** nodes. Under
|
||||
`transports:`, add an `ethernet:` block. The key settings are
|
||||
the four discovery flags — both nodes must opt in to all four,
|
||||
the four neighbor flags — both nodes must opt in to all four,
|
||||
and they default to off:
|
||||
|
||||
```yaml
|
||||
@@ -185,7 +185,7 @@ transports:
|
||||
ethernet:
|
||||
interface: "<eth>" # the name from Step 1
|
||||
announce: true # broadcast our beacon on the link
|
||||
discovery: true # listen for beacons (default; shown for clarity)
|
||||
listen: true # listen for beacons (default; shown for clarity)
|
||||
auto_connect: true # dial peers we discover
|
||||
accept_connections: true # accept dial-ins from peers we discover
|
||||
```
|
||||
@@ -194,7 +194,7 @@ Each flag does one thing:
|
||||
|
||||
- `announce: true` — emit a small beacon every
|
||||
`beacon_interval_secs` (default 30s) carrying our npub.
|
||||
- `discovery: true` — listen for incoming beacons; populate a
|
||||
- `listen: true` — listen for incoming beacons; populate a
|
||||
candidate-peer list keyed by source MAC and observed npub.
|
||||
- `auto_connect: true` — when we see a beacon from an npub
|
||||
we have not yet peered with, initiate the outbound Noise
|
||||
@@ -218,7 +218,7 @@ is "all four flags on both ends."
|
||||
> lan:
|
||||
> interface: "eth0"
|
||||
> announce: true
|
||||
> discovery: true
|
||||
> listen: true
|
||||
> auto_connect: true
|
||||
> accept_connections: true
|
||||
> dongle:
|
||||
@@ -227,7 +227,7 @@ is "all four flags on both ends."
|
||||
> # ...
|
||||
> ```
|
||||
>
|
||||
> Each named instance runs its own socket and discovery state.
|
||||
> Each named instance runs its own socket and neighbor state.
|
||||
> A single ground-up link only needs the flat form shown
|
||||
> first; named instances become useful when the same node
|
||||
> bridges multiple physical segments.
|
||||
@@ -390,7 +390,7 @@ What you do need on the AP side:
|
||||
networks and "secure" enterprise APs ship with it on.
|
||||
When client isolation is on, the AP refuses to forward
|
||||
station-to-station frames — the broadcast beacons never
|
||||
arrive at the other node, and discovery fails silently.
|
||||
arrive at the other node, and neighbor detection fails silently.
|
||||
If beacons aren't crossing, this is the first thing to
|
||||
check.
|
||||
|
||||
@@ -401,7 +401,7 @@ adapter name.
|
||||
### Bluetooth LE (experimental but works)
|
||||
|
||||
BLE is a separate transport (`transports.ble.*`) with its own
|
||||
discovery model — L2CAP advertisements rather than raw L2
|
||||
neighbor-detection model — L2CAP advertisements rather than raw L2
|
||||
broadcasts. The shape of the tutorial is the same (advertise +
|
||||
scan + auto-connect + accept), but the prerequisites are
|
||||
different: BlueZ, `bluetoothd`, an HCI adapter, and the
|
||||
@@ -424,7 +424,7 @@ Windows builds skip it.
|
||||
a radio link), `CAP_NET_RAW`, and a few config flags on each
|
||||
end are sufficient. The mesh supplies its own identity,
|
||||
addressing, discovery, and routing.
|
||||
- **Discovery is a four-flag opt-in.** `announce`, `discovery`,
|
||||
- **Neighbor detection is a four-flag opt-in.** `announce`, `listen`,
|
||||
`auto_connect`, and `accept_connections` each control one
|
||||
thing; both ends must agree before a link will form.
|
||||
- **The two modes coexist.** Overlay peers and ground-up peers
|
||||
|
||||
@@ -98,7 +98,7 @@ transports:
|
||||
# Ethernet transport — uncomment and set your interface name.
|
||||
# ethernet:
|
||||
# interface: "eth0"
|
||||
# discovery: true
|
||||
# listen: true
|
||||
# announce: true
|
||||
# auto_connect: true
|
||||
# accept_connections: true
|
||||
|
||||
@@ -83,19 +83,19 @@ transports:
|
||||
ethernet:
|
||||
wan:
|
||||
interface: "eth0"
|
||||
discovery: true
|
||||
listen: true
|
||||
announce: true
|
||||
auto_connect: true
|
||||
accept_connections: true
|
||||
wwan:
|
||||
interface: "phy0-sta0"
|
||||
discovery: true
|
||||
listen: true
|
||||
announce: true
|
||||
auto_connect: true
|
||||
accept_connections: true
|
||||
lan:
|
||||
interface: "br-lan"
|
||||
discovery: true
|
||||
listen: true
|
||||
announce: true
|
||||
auto_connect: true
|
||||
accept_connections: true
|
||||
|
||||
@@ -68,7 +68,7 @@ and set the interface name:
|
||||
transports:
|
||||
ethernet:
|
||||
interface: "eth0"
|
||||
discovery: true
|
||||
listen: true
|
||||
announce: true
|
||||
auto_connect: true
|
||||
accept_connections: true
|
||||
|
||||
+25
-6
@@ -282,9 +282,10 @@ pub struct EthernetConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub send_buf_size: Option<usize>,
|
||||
|
||||
/// Listen for discovery beacons from other nodes. Default: true.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub discovery: Option<bool>,
|
||||
/// Listen for neighbor beacons from other nodes. Default: true.
|
||||
/// (Renamed from `discovery`; the old key is still accepted.)
|
||||
#[serde(default, alias = "discovery", skip_serializing_if = "Option::is_none")]
|
||||
pub listen: Option<bool>,
|
||||
|
||||
/// Broadcast announcement beacons on the LAN. Default: false.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
@@ -319,9 +320,9 @@ impl EthernetConfig {
|
||||
self.send_buf_size.unwrap_or(DEFAULT_ETHERNET_SEND_BUF)
|
||||
}
|
||||
|
||||
/// Whether to listen for discovery beacons. Default: true.
|
||||
pub fn discovery(&self) -> bool {
|
||||
self.discovery.unwrap_or(true)
|
||||
/// Whether to listen for neighbor beacons. Default: true.
|
||||
pub fn listen(&self) -> bool {
|
||||
self.listen.unwrap_or(true)
|
||||
}
|
||||
|
||||
/// Whether to broadcast announcement beacons. Default: false.
|
||||
@@ -1046,4 +1047,22 @@ mod tests {
|
||||
assert_eq!(parse_bind_port("[::]:443"), Some(443));
|
||||
assert_eq!(parse_bind_port("not-a-socket-addr"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ethernet_listen_accepts_legacy_discovery_alias_and_rejects_unknown() {
|
||||
// (a) The legacy `discovery:` key is still accepted via serde alias.
|
||||
let legacy: EthernetConfig =
|
||||
serde_yaml::from_str("interface: eth0\ndiscovery: true\n").unwrap();
|
||||
assert_eq!(legacy.listen, Some(true));
|
||||
|
||||
// (b) The new canonical `listen:` key parses into the renamed field.
|
||||
let renamed: EthernetConfig =
|
||||
serde_yaml::from_str("interface: eth0\nlisten: true\n").unwrap();
|
||||
assert_eq!(renamed.listen, Some(true));
|
||||
|
||||
// (c) `deny_unknown_fields` still rejects an unknown ethernet key.
|
||||
let bogus: Result<EthernetConfig, _> =
|
||||
serde_yaml::from_str("interface: eth0\nbogus: true\n");
|
||||
assert!(bogus.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ async fn make_test_node_ethernet(interface: &str) -> TestNode {
|
||||
|
||||
let config = EthernetConfig {
|
||||
interface: interface.to_string(),
|
||||
discovery: Some(false),
|
||||
listen: Some(false),
|
||||
announce: Some(false),
|
||||
accept_connections: Some(true),
|
||||
..Default::default()
|
||||
|
||||
+17
-17
@@ -7,7 +7,7 @@
|
||||
//!
|
||||
//! ## Architecture
|
||||
//!
|
||||
//! Transport logic (pool, discovery, lifecycle) is separated from the
|
||||
//! Transport logic (pool, neighbor, lifecycle) is separated from the
|
||||
//! BlueZ/bluer stack via the `BleIo` trait. `BluerIo` provides the real
|
||||
//! implementation (behind `cfg(bluer_available)`); `MockBleIo` provides
|
||||
//! an in-memory test double for CI without hardware.
|
||||
@@ -19,8 +19,8 @@
|
||||
//! static (configured) peers get priority over discovered peers.
|
||||
|
||||
pub mod addr;
|
||||
pub mod discovery;
|
||||
pub mod io;
|
||||
pub mod neighbor;
|
||||
pub mod pool;
|
||||
pub mod stats;
|
||||
|
||||
@@ -30,8 +30,8 @@ use super::{
|
||||
};
|
||||
use crate::config::BleConfig;
|
||||
use addr::BleAddr;
|
||||
use discovery::DiscoveryBuffer;
|
||||
use io::{BleIo, BleScanner, BleStream};
|
||||
use neighbor::NeighborBuffer;
|
||||
use pool::{BleConnection, ConnectionPool};
|
||||
use stats::BleStats;
|
||||
|
||||
@@ -86,8 +86,8 @@ pub struct BleTransport<I: BleIo> {
|
||||
accept_task: Option<JoinHandle<()>>,
|
||||
/// Combined scan + probe loop task handle.
|
||||
scan_probe_task: Option<JoinHandle<()>>,
|
||||
/// Discovery buffer for discovered peers.
|
||||
discovery_buffer: Arc<DiscoveryBuffer>,
|
||||
/// Neighbor buffer for discovered peers.
|
||||
neighbor_buffer: Arc<NeighborBuffer>,
|
||||
/// Transport statistics.
|
||||
stats: Arc<BleStats>,
|
||||
}
|
||||
@@ -118,7 +118,7 @@ impl<I: BleIo> BleTransport<I> {
|
||||
packet_tx,
|
||||
accept_task: None,
|
||||
scan_probe_task: None,
|
||||
discovery_buffer: Arc::new(DiscoveryBuffer::new(transport_id)),
|
||||
neighbor_buffer: Arc::new(NeighborBuffer::new(transport_id)),
|
||||
stats: Arc::new(BleStats::new()),
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ impl<I: BleIo> BleTransport<I> {
|
||||
transport_id,
|
||||
stats,
|
||||
max_conns,
|
||||
Arc::clone(&self.discovery_buffer),
|
||||
Arc::clone(&self.neighbor_buffer),
|
||||
)));
|
||||
debug!(adapter = %adapter, psm = psm, "BLE accept loop started");
|
||||
}
|
||||
@@ -195,7 +195,7 @@ impl<I: BleIo> BleTransport<I> {
|
||||
scanner,
|
||||
Arc::clone(&self.io),
|
||||
Arc::clone(&self.pool),
|
||||
Arc::clone(&self.discovery_buffer),
|
||||
Arc::clone(&self.neighbor_buffer),
|
||||
Arc::clone(&self.stats),
|
||||
self.config.psm(),
|
||||
self.config.connect_timeout_ms(),
|
||||
@@ -575,7 +575,7 @@ impl<I: BleIo> Transport for BleTransport<I> {
|
||||
}
|
||||
|
||||
fn discover(&self) -> Result<Vec<DiscoveredPeer>, TransportError> {
|
||||
Ok(self.discovery_buffer.take())
|
||||
Ok(self.neighbor_buffer.take())
|
||||
}
|
||||
|
||||
fn auto_connect(&self) -> bool {
|
||||
@@ -604,7 +604,7 @@ async fn accept_loop<A>(
|
||||
transport_id: TransportId,
|
||||
stats: Arc<BleStats>,
|
||||
_max_conns: usize,
|
||||
discovery_buffer: Arc<DiscoveryBuffer>,
|
||||
neighbor_buffer: Arc<NeighborBuffer>,
|
||||
) where
|
||||
A: io::BleAcceptor,
|
||||
A::Stream: 'static,
|
||||
@@ -627,7 +627,7 @@ async fn accept_loop<A>(
|
||||
let send_mtu = stream.send_mtu();
|
||||
let recv_mtu = stream.recv_mtu();
|
||||
|
||||
discovery_buffer.add_peer(&addr);
|
||||
neighbor_buffer.add_peer(&addr);
|
||||
|
||||
let stream = Arc::new(stream);
|
||||
|
||||
@@ -721,7 +721,7 @@ async fn receive_loop<S: BleStream>(
|
||||
/// Each scan result is probed immediately unless the address is in cooldown
|
||||
/// (recently probed) or already connected. On successful probe, the
|
||||
/// connection is promoted directly into the pool (no second L2CAP connect
|
||||
/// needed) and the peer is reported to the discovery buffer for the node
|
||||
/// needed) and the peer is reported to the neighbor buffer for the node
|
||||
/// layer to auto-connect.
|
||||
///
|
||||
/// Cooldown prevents rapid re-probing of the same address: after any probe
|
||||
@@ -732,7 +732,7 @@ async fn scan_probe_loop<I: io::BleIo>(
|
||||
mut scanner: I::Scanner,
|
||||
io: Arc<I>,
|
||||
pool: Arc<Mutex<ConnectionPool<Arc<I::Stream>>>>,
|
||||
buffer: Arc<DiscoveryBuffer>,
|
||||
buffer: Arc<NeighborBuffer>,
|
||||
stats: Arc<BleStats>,
|
||||
psm: u16,
|
||||
connect_timeout_ms: u64,
|
||||
@@ -938,7 +938,7 @@ mod tests {
|
||||
#[tokio::test(start_paused = true)]
|
||||
async fn test_scan_discovers_peers() {
|
||||
let io = MockBleIo::new("hci0", test_addr(1));
|
||||
// Probe connect must succeed for peers to reach the discovery buffer
|
||||
// Probe connect must succeed for peers to reach the neighbor buffer
|
||||
let local = test_addr(1);
|
||||
io.set_connect_handler(move |addr, _psm| {
|
||||
let (stream, _peer) = io::MockBleStream::pair(local.clone(), addr.clone(), 2048);
|
||||
@@ -958,8 +958,8 @@ mod tests {
|
||||
// Let the expired entries get processed
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
// Scan results go to discovery buffer as bare addresses after probe
|
||||
let peers = transport.discovery_buffer.take();
|
||||
// Scan results go to neighbor buffer as bare addresses after probe
|
||||
let peers = transport.neighbor_buffer.take();
|
||||
assert_eq!(peers.len(), 2);
|
||||
}
|
||||
|
||||
@@ -983,7 +983,7 @@ mod tests {
|
||||
tokio::time::advance(std::time::Duration::from_secs(6)).await;
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
let peers = transport.discovery_buffer.take();
|
||||
let peers = transport.neighbor_buffer.take();
|
||||
assert_eq!(peers.len(), 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! BLE discovery via advertising and scanning.
|
||||
//! BLE neighbor detection via advertising and scanning.
|
||||
//!
|
||||
//! BLE advertisements carry a 128-bit FIPS service UUID for identification.
|
||||
//! Post-forklift, advertisements are UUID-only (no identity material);
|
||||
@@ -11,15 +11,15 @@ use super::addr::BleAddr;
|
||||
|
||||
/// Buffer for discovered BLE peers, drained by `discover()`.
|
||||
///
|
||||
/// Follows the same pattern as Ethernet's `DiscoveryBuffer`: peers are
|
||||
/// added from the scan loop and drained by the node's discovery polling.
|
||||
pub struct DiscoveryBuffer {
|
||||
/// Follows the same pattern as Ethernet's `NeighborBuffer`: peers are
|
||||
/// added from the scan loop and drained by the node's neighbor polling.
|
||||
pub struct NeighborBuffer {
|
||||
transport_id: TransportId,
|
||||
peers: Mutex<Vec<DiscoveredPeer>>,
|
||||
}
|
||||
|
||||
impl DiscoveryBuffer {
|
||||
/// Create a new empty discovery buffer.
|
||||
impl NeighborBuffer {
|
||||
/// Create a new empty neighbor buffer.
|
||||
pub fn new(transport_id: TransportId) -> Self {
|
||||
Self {
|
||||
transport_id,
|
||||
@@ -64,8 +64,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_discovery_buffer_add_take() {
|
||||
let buffer = DiscoveryBuffer::new(TransportId::new(1));
|
||||
fn test_neighbor_buffer_add_take() {
|
||||
let buffer = NeighborBuffer::new(TransportId::new(1));
|
||||
buffer.add_peer(&test_addr(1));
|
||||
|
||||
let peers = buffer.take();
|
||||
@@ -77,8 +77,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_discovery_buffer_dedup() {
|
||||
let buffer = DiscoveryBuffer::new(TransportId::new(1));
|
||||
fn test_neighbor_buffer_dedup() {
|
||||
let buffer = NeighborBuffer::new(TransportId::new(1));
|
||||
buffer.add_peer(&test_addr(1));
|
||||
buffer.add_peer(&test_addr(1)); // same address again
|
||||
|
||||
@@ -87,8 +87,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_discovery_buffer_multiple_peers() {
|
||||
let buffer = DiscoveryBuffer::new(TransportId::new(1));
|
||||
fn test_neighbor_buffer_multiple_peers() {
|
||||
let buffer = NeighborBuffer::new(TransportId::new(1));
|
||||
buffer.add_peer(&test_addr(1));
|
||||
buffer.add_peer(&test_addr(2));
|
||||
buffer.add_peer(&test_addr(3));
|
||||
@@ -98,8 +98,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_discovery_buffer_transport_addr_format() {
|
||||
let buffer = DiscoveryBuffer::new(TransportId::new(1));
|
||||
fn test_neighbor_buffer_transport_addr_format() {
|
||||
let buffer = NeighborBuffer::new(TransportId::new(1));
|
||||
buffer.add_peer(&test_addr(0x42));
|
||||
|
||||
let peers = buffer.take();
|
||||
@@ -6,8 +6,8 @@
|
||||
//! 802.11 transparently on Linux).
|
||||
|
||||
pub mod addr;
|
||||
pub mod discovery;
|
||||
pub mod io;
|
||||
pub mod neighbor;
|
||||
pub mod stats;
|
||||
|
||||
pub use addr::parse_mac_string;
|
||||
@@ -17,8 +17,8 @@ use super::{
|
||||
TransportId, TransportState, TransportType,
|
||||
};
|
||||
use crate::config::EthernetConfig;
|
||||
use discovery::{DiscoveryBuffer, FRAME_TYPE_BEACON, FRAME_TYPE_DATA, build_beacon, parse_beacon};
|
||||
use io::{AsyncPacketSocket, ETHERNET_BROADCAST, PacketSocket};
|
||||
use neighbor::{FRAME_TYPE_BEACON, FRAME_TYPE_DATA, NeighborBuffer, build_beacon, parse_beacon};
|
||||
use stats::EthernetStats;
|
||||
|
||||
use std::sync::Arc;
|
||||
@@ -53,8 +53,8 @@ pub struct EthernetTransport {
|
||||
interface: String,
|
||||
/// Effective MTU (interface MTU - 4 for frame header).
|
||||
effective_mtu: u16,
|
||||
/// Discovery buffer for discovered peers.
|
||||
discovery_buffer: Arc<DiscoveryBuffer>,
|
||||
/// Neighbor buffer for discovered peers.
|
||||
neighbor_buffer: Arc<NeighborBuffer>,
|
||||
/// Transport-level statistics.
|
||||
stats: Arc<EthernetStats>,
|
||||
}
|
||||
@@ -68,7 +68,7 @@ impl EthernetTransport {
|
||||
packet_tx: PacketTx,
|
||||
) -> Self {
|
||||
let interface = config.interface.clone();
|
||||
let discovery_buffer = Arc::new(DiscoveryBuffer::new(transport_id));
|
||||
let neighbor_buffer = Arc::new(NeighborBuffer::new(transport_id));
|
||||
let stats = Arc::new(EthernetStats::new());
|
||||
|
||||
Self {
|
||||
@@ -83,7 +83,7 @@ impl EthernetTransport {
|
||||
local_mac: None,
|
||||
interface,
|
||||
effective_mtu: 1496, // default, updated on start
|
||||
discovery_buffer,
|
||||
neighbor_buffer,
|
||||
stats,
|
||||
}
|
||||
}
|
||||
@@ -150,8 +150,8 @@ impl EthernetTransport {
|
||||
let transport_id = self.transport_id;
|
||||
let packet_tx = self.packet_tx.clone();
|
||||
let mtu = self.effective_mtu;
|
||||
let discovery_enabled = self.config.discovery();
|
||||
let discovery_buffer = self.discovery_buffer.clone();
|
||||
let listen_enabled = self.config.listen();
|
||||
let neighbor_buffer = self.neighbor_buffer.clone();
|
||||
let stats = self.stats.clone();
|
||||
let recv_socket = socket.clone();
|
||||
|
||||
@@ -161,8 +161,8 @@ impl EthernetTransport {
|
||||
transport_id,
|
||||
packet_tx,
|
||||
mtu,
|
||||
discovery_enabled,
|
||||
discovery_buffer,
|
||||
listen_enabled,
|
||||
neighbor_buffer,
|
||||
stats,
|
||||
)
|
||||
.await;
|
||||
@@ -346,7 +346,7 @@ impl Transport for EthernetTransport {
|
||||
}
|
||||
|
||||
fn discover(&self) -> Result<Vec<DiscoveredPeer>, TransportError> {
|
||||
Ok(self.discovery_buffer.take())
|
||||
Ok(self.neighbor_buffer.take())
|
||||
}
|
||||
|
||||
fn auto_connect(&self) -> bool {
|
||||
@@ -368,8 +368,8 @@ async fn ethernet_receive_loop(
|
||||
transport_id: TransportId,
|
||||
packet_tx: PacketTx,
|
||||
mtu: u16,
|
||||
discovery_enabled: bool,
|
||||
discovery_buffer: Arc<DiscoveryBuffer>,
|
||||
listen_enabled: bool,
|
||||
neighbor_buffer: Arc<NeighborBuffer>,
|
||||
stats: Arc<EthernetStats>,
|
||||
) {
|
||||
// Buffer with headroom: frame type prefix + MTU + some extra
|
||||
@@ -426,12 +426,12 @@ async fn ethernet_receive_loop(
|
||||
FRAME_TYPE_BEACON => {
|
||||
stats.record_beacon_recv();
|
||||
|
||||
if discovery_enabled && parse_beacon(&buf[..len]) {
|
||||
discovery_buffer.add_peer(src_mac);
|
||||
if listen_enabled && parse_beacon(&buf[..len]) {
|
||||
neighbor_buffer.add_peer(src_mac);
|
||||
trace!(
|
||||
transport_id = %transport_id,
|
||||
remote_mac = %format_mac(&src_mac),
|
||||
"Discovery beacon received"
|
||||
"Neighbor beacon received"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -715,7 +715,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_beacon_size() {
|
||||
assert_eq!(discovery::BEACON_SIZE, 5);
|
||||
assert_eq!(neighbor::BEACON_SIZE, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
//! Ethernet LAN discovery via broadcast beacons.
|
||||
//! Ethernet LAN neighbor detection via broadcast beacons.
|
||||
//!
|
||||
//! Beacon format (5 bytes total):
|
||||
//! - Unified header (4 bytes): `[type:1][flags:1][length:2 LE]`
|
||||
//! - Version (1 byte): discovery protocol version
|
||||
//! - Version (1 byte): beacon protocol version
|
||||
|
||||
use crate::transport::{DiscoveredPeer, TransportAddr, TransportId};
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// Discovery protocol version.
|
||||
pub const DISCOVERY_VERSION: u8 = 0x01;
|
||||
/// Beacon protocol version.
|
||||
pub const BEACON_VERSION: u8 = 0x01;
|
||||
|
||||
/// Frame type prefix for discovery announcement beacons.
|
||||
/// Frame type prefix for neighbor beacon frames.
|
||||
pub const FRAME_TYPE_BEACON: u8 = 0x01;
|
||||
|
||||
/// Frame type prefix for FIPS data frames.
|
||||
@@ -25,17 +25,17 @@ pub const BEACON_PAYLOAD_SIZE: usize = 1;
|
||||
/// Total beacon size: header(4) + payload(1).
|
||||
pub const BEACON_SIZE: usize = ETHERNET_HEADER_SIZE + BEACON_PAYLOAD_SIZE;
|
||||
|
||||
/// Build a discovery announcement beacon payload.
|
||||
/// Build a neighbor beacon payload.
|
||||
pub fn build_beacon() -> [u8; BEACON_SIZE] {
|
||||
let mut buf = [0u8; BEACON_SIZE];
|
||||
buf[0] = FRAME_TYPE_BEACON;
|
||||
buf[1] = 0x00; // flags (reserved)
|
||||
buf[2..4].copy_from_slice(&(BEACON_PAYLOAD_SIZE as u16).to_le_bytes());
|
||||
buf[4] = DISCOVERY_VERSION;
|
||||
buf[4] = BEACON_VERSION;
|
||||
buf
|
||||
}
|
||||
|
||||
/// Parse a discovery announcement beacon payload.
|
||||
/// Parse a neighbor beacon payload.
|
||||
///
|
||||
/// Returns true if the payload is a valid beacon, false otherwise.
|
||||
pub fn parse_beacon(data: &[u8]) -> bool {
|
||||
@@ -50,17 +50,17 @@ pub fn parse_beacon(data: &[u8]) -> bool {
|
||||
if length < 1 {
|
||||
return false;
|
||||
}
|
||||
data[4] == DISCOVERY_VERSION
|
||||
data[4] == BEACON_VERSION
|
||||
}
|
||||
|
||||
/// Buffer for discovered peers, drained by `discover()`.
|
||||
pub struct DiscoveryBuffer {
|
||||
pub struct NeighborBuffer {
|
||||
transport_id: TransportId,
|
||||
peers: Mutex<Vec<DiscoveredPeer>>,
|
||||
}
|
||||
|
||||
impl DiscoveryBuffer {
|
||||
/// Create a new empty discovery buffer.
|
||||
impl NeighborBuffer {
|
||||
/// Create a new empty neighbor buffer.
|
||||
pub fn new(transport_id: TransportId) -> Self {
|
||||
Self {
|
||||
transport_id,
|
||||
@@ -100,7 +100,7 @@ mod tests {
|
||||
assert_eq!(beacon[0], FRAME_TYPE_BEACON);
|
||||
assert_eq!(beacon[1], 0x00); // flags
|
||||
assert_eq!(u16::from_le_bytes([beacon[2], beacon[3]]), 1); // length
|
||||
assert_eq!(beacon[4], DISCOVERY_VERSION);
|
||||
assert_eq!(beacon[4], BEACON_VERSION);
|
||||
|
||||
assert!(parse_beacon(&beacon));
|
||||
}
|
||||
@@ -139,8 +139,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_discovery_buffer() {
|
||||
let buffer = DiscoveryBuffer::new(TransportId::new(1));
|
||||
fn test_neighbor_buffer() {
|
||||
let buffer = NeighborBuffer::new(TransportId::new(1));
|
||||
let mac = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
|
||||
|
||||
buffer.add_peer(mac);
|
||||
@@ -156,8 +156,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_discovery_buffer_dedup() {
|
||||
let buffer = DiscoveryBuffer::new(TransportId::new(1));
|
||||
fn test_neighbor_buffer_dedup() {
|
||||
let buffer = NeighborBuffer::new(TransportId::new(1));
|
||||
let mac = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
|
||||
|
||||
buffer.add_peer(mac);
|
||||
@@ -67,7 +67,7 @@ def _build_ethernet_config(iface: str) -> dict:
|
||||
"""Build an Ethernet transport config dict for a single interface."""
|
||||
return {
|
||||
"interface": iface,
|
||||
"discovery": True,
|
||||
"listen": True,
|
||||
"announce": True,
|
||||
"auto_connect": True,
|
||||
"accept_connections": True,
|
||||
|
||||
Reference in New Issue
Block a user