mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Add BLE L2CAP transport with scan-based auto-connect
BLE transport implementation using L2CAP Connection-Oriented Channels (SeqPacket mode) via the bluer crate, behind cfg(feature = "ble"). Core transport: - BleTransport<I> generic over BleIo trait (BluerIo prod, MockBleIo test) - Connection pool with priority eviction (static > discovered, max 7) - Connect-on-send via connect_inline() matching TCP behavior - Per-connection receive loops with pool cleanup on disconnect Discovery and probing: - Combined scan_probe_loop using select! over scanner events and a BinaryHeap delay queue with per-entry random jitter (0-5s) to prevent herd effects when multiple nodes see the same beacon simultaneously - Pre-handshake pubkey exchange ([0x00][pubkey:32]) for IK identity - Cross-probe tie-breaker: smaller NodeAddr's outbound wins (same convention as FMP/FSP rekey dual-initiation) - Probed peers reported to DiscoveryBuffer; pool fills through normal node-layer auto-connect -> send_async -> connect_inline path Beacon management: - Periodic advertising: 1s burst every 30s (configurable via beacon_interval_secs / beacon_duration_secs) - FIPS service UUID for scan filtering Configuration (all fields optional with defaults): - adapter, psm, mtu, max_connections, connect_timeout_ms - advertise, scan, auto_connect, accept_connections - beacon_interval_secs (30), beacon_duration_secs (1) Hardware validated with two BLE nodes: - 2048-byte MTU, ~60-160ms RTT, zero-config auto-connect - BLE spike tool at testing/ble/ for standalone adapter validation 42 unit tests + 4 node-level integration tests, all CI-compatible via MockBleIo (no hardware required). tokio test-util added for time-dependent scan/probe tests.
This commit is contained in:
@@ -82,48 +82,44 @@
|
||||
<!-- === Transport layer === -->
|
||||
|
||||
<!-- Overlay transports -->
|
||||
<rect x="80" y="336" width="150" height="60" class="cat"/>
|
||||
<text x="155" y="349" text-anchor="middle" class="cat">Overlay</text>
|
||||
<rect x="80" y="336" width="216" height="60" class="cat"/>
|
||||
<text x="188" y="349" text-anchor="middle" class="cat">Overlay</text>
|
||||
|
||||
<rect x="92" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="122" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">UDP</text>
|
||||
<text x="122" y="381" text-anchor="middle" class="sub">IP</text>
|
||||
|
||||
<rect x="158" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="188" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">Tor</text>
|
||||
<text x="188" y="381" text-anchor="middle" class="sub">.onion</text>
|
||||
<text x="188" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">TCP</text>
|
||||
<text x="188" y="381" text-anchor="middle" class="sub">IP</text>
|
||||
|
||||
<rect x="224" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="254" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">Tor</text>
|
||||
<text x="254" y="381" text-anchor="middle" class="sub">.onion</text>
|
||||
|
||||
<!-- Shared medium transports -->
|
||||
<rect x="240" y="336" width="300" height="60" class="cat"/>
|
||||
<text x="390" y="349" text-anchor="middle" class="cat">Shared Medium</text>
|
||||
<rect x="306" y="336" width="234" height="60" class="cat"/>
|
||||
<text x="423" y="349" text-anchor="middle" class="cat">Shared Medium</text>
|
||||
|
||||
<rect x="254" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="284" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">Ether</text>
|
||||
<text x="284" y="381" text-anchor="middle" class="sub">802.3</text>
|
||||
<rect x="318" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="348" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">Ether</text>
|
||||
<text x="348" y="381" text-anchor="middle" class="sub">802.3</text>
|
||||
|
||||
<rect x="320" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="350" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">WiFi</text>
|
||||
<text x="350" y="381" text-anchor="middle" class="sub">802.11</text>
|
||||
<rect x="384" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="414" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">BLE</text>
|
||||
<text x="414" y="381" text-anchor="middle" class="sub">L2CAP</text>
|
||||
|
||||
<rect x="386" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="416" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">BT</text>
|
||||
<text x="416" y="381" text-anchor="middle" class="sub">RFCOMM</text>
|
||||
|
||||
<rect x="452" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="482" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">Radio</text>
|
||||
<text x="482" y="381" text-anchor="middle" class="sub">Sat, ...</text>
|
||||
<rect x="450" y="356" width="80" height="30" class="layer xport"/>
|
||||
<text x="490" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">Radio ...</text>
|
||||
<text x="490" y="381" text-anchor="middle" class="sub">future</text>
|
||||
|
||||
<!-- Point-to-point transports -->
|
||||
<rect x="550" y="336" width="150" height="60" class="cat"/>
|
||||
<text x="625" y="349" text-anchor="middle" class="cat">Point-to-Point</text>
|
||||
|
||||
<rect x="562" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="592" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">Serial</text>
|
||||
<text x="592" y="381" text-anchor="middle" class="sub">UART</text>
|
||||
|
||||
<rect x="628" y="356" width="60" height="30" class="layer xport"/>
|
||||
<text x="658" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">...</text>
|
||||
<text x="658" y="381" text-anchor="middle" class="sub"></text>
|
||||
<rect x="562" y="356" width="126" height="30" class="layer xport"/>
|
||||
<text x="625" y="371" text-anchor="middle" font-size="10" font-weight="bold" fill="#e0e0e0">Serial ...</text>
|
||||
<text x="625" y="381" text-anchor="middle" class="sub">future</text>
|
||||
|
||||
<!-- === Peer networks below node box === -->
|
||||
<text x="155" y="436" text-anchor="middle" class="sub">Internet / Overlay Peers</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 7.8 KiB |
@@ -485,6 +485,78 @@ HiddenServiceDir /var/lib/tor/fips
|
||||
HiddenServicePort 8443 127.0.0.1:8444
|
||||
```
|
||||
|
||||
### BLE (`transports.ble.*`)
|
||||
|
||||
Bluetooth Low Energy transport using L2CAP Connection-Oriented Channels.
|
||||
Requires BlueZ and the `ble` Cargo feature flag (default-on). Linux only;
|
||||
guarded by `#[cfg(target_os = "linux")]`. Communicates with BlueZ via D-Bus
|
||||
using the `bluer` crate.
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `transports.ble.adapter` | string | `"hci0"` | HCI adapter name |
|
||||
| `transports.ble.psm` | u16 | `0x0085` (133) | L2CAP Protocol/Service Multiplexer |
|
||||
| `transports.ble.mtu` | u16 | `2048` | Default MTU. Actual MTU is negotiated per-link during L2CAP connection setup. |
|
||||
| `transports.ble.max_connections` | usize | `7` | Maximum concurrent BLE connections |
|
||||
| `transports.ble.connect_timeout_ms` | u64 | `10000` | Outbound connect timeout in milliseconds |
|
||||
| `transports.ble.advertise` | bool | `true` | Broadcast BLE beacon advertisements for peer discovery |
|
||||
| `transports.ble.scan` | bool | `true` | Listen for BLE beacon advertisements from other nodes |
|
||||
| `transports.ble.auto_connect` | bool | `false` | Automatically connect to discovered peers |
|
||||
| `transports.ble.accept_connections` | bool | `true` | Accept incoming L2CAP connections |
|
||||
| `transports.ble.scan_interval_secs` | u64 | `10` | Interval between BLE scan cycles |
|
||||
| `transports.ble.beacon_interval_secs` | u64 | `10` | Interval between beacon advertising bursts |
|
||||
| `transports.ble.beacon_duration_secs` | u64 | `3` | Duration of each beacon advertising burst |
|
||||
|
||||
**Address format.** BLE peer addresses use the form
|
||||
`"adapter/device_address"` — for example, `"hci0/AA:BB:CC:DD:EE:FF"`.
|
||||
|
||||
**Scan/probe and tie-breaking.** When `scan` is enabled, the transport
|
||||
periodically scans for BLE beacons from other FIPS nodes. Discovered
|
||||
peers are probed with per-entry random jitter to prevent herd effects
|
||||
when multiple nodes see the same beacon simultaneously. If two nodes
|
||||
probe each other at the same time (cross-probe), a deterministic
|
||||
tie-breaker based on NodeAddr comparison ensures only one connection
|
||||
is established.
|
||||
|
||||
**Connection pool.** The `max_connections` parameter limits the number of
|
||||
concurrent BLE connections. When the pool is full, the least-recently-used
|
||||
connection is evicted to make room for new connections.
|
||||
|
||||
### BLE Example
|
||||
|
||||
A node using BLE for local mesh discovery alongside UDP for internet peers:
|
||||
|
||||
```yaml
|
||||
node:
|
||||
identity:
|
||||
persistent: true
|
||||
|
||||
tun:
|
||||
enabled: true
|
||||
|
||||
transports:
|
||||
udp:
|
||||
bind_addr: "0.0.0.0:2121"
|
||||
ble:
|
||||
adapter: "hci0"
|
||||
advertise: true
|
||||
scan: true
|
||||
auto_connect: true
|
||||
accept_connections: true
|
||||
|
||||
peers:
|
||||
- npub: "npub1abc..."
|
||||
alias: "internet-peer"
|
||||
addresses:
|
||||
- transport: udp
|
||||
addr: "203.0.113.5:2121"
|
||||
connect_policy: auto_connect
|
||||
```
|
||||
|
||||
BLE peers on the local radio range are discovered automatically via
|
||||
beacons — no static peer entries needed. Internet peers still require
|
||||
explicit configuration.
|
||||
|
||||
## Peers (`peers[]`)
|
||||
|
||||
Static peer list. Each entry defines a peer to connect to.
|
||||
@@ -493,8 +565,8 @@ Static peer list. Each entry defines a peer to connect to.
|
||||
|-----------|------|---------|-------------|
|
||||
| `peers[].npub` | string | *(required)* | Peer's Nostr public key (npub-encoded) |
|
||||
| `peers[].alias` | string | *(none)* | Human-readable name for logging |
|
||||
| `peers[].addresses[].transport` | string | *(required)* | Transport type: `udp`, `tcp`, `ethernet`, or `tor` |
|
||||
| `peers[].addresses[].addr` | string | *(required)* | Transport address. UDP/TCP: `"host:port"` (IP or DNS hostname). Ethernet: `"interface/mac"` (e.g., `"eth0/aa:bb:cc:dd:ee:ff"`). Tor: `".onion:port"` or `"host:port"` |
|
||||
| `peers[].addresses[].transport` | string | *(required)* | Transport type: `udp`, `tcp`, `ethernet`, `tor`, or `ble` |
|
||||
| `peers[].addresses[].addr` | string | *(required)* | Transport address. UDP/TCP: `"host:port"` (IP or DNS hostname). Ethernet: `"interface/mac"` (e.g., `"eth0/aa:bb:cc:dd:ee:ff"`). BLE: `"adapter/device_address"` (e.g., `"hci0/AA:BB:CC:DD:EE:FF"`). Tor: `".onion:port"` or `"host:port"` |
|
||||
| `peers[].addresses[].priority` | u8 | `100` | Address priority (lower = preferred) |
|
||||
| `peers[].connect_policy` | string | `"auto_connect"` | Connection policy: `auto_connect`, `on_demand`, or `manual` |
|
||||
| `peers[].auto_reconnect` | bool | `true` | Automatically reconnect after MMP link-dead removal (exponential backoff, unlimited retries) |
|
||||
@@ -720,6 +792,19 @@ transports:
|
||||
# # hostname_file: "/var/lib/tor/fips/hostname"
|
||||
# # bind_addr: "127.0.0.1:8444"
|
||||
# # max_inbound_connections: 64
|
||||
# ble: # uncomment to enable BLE transport (Linux only, requires BlueZ)
|
||||
# adapter: "hci0" # HCI adapter name
|
||||
# psm: 0x0085 # L2CAP PSM (133)
|
||||
# mtu: 2048 # default MTU (negotiated per-link)
|
||||
# max_connections: 7 # max concurrent BLE connections
|
||||
# connect_timeout_ms: 10000 # outbound connect timeout
|
||||
# advertise: true # broadcast BLE beacons
|
||||
# scan: true # listen for BLE beacons
|
||||
# auto_connect: false # connect to discovered peers
|
||||
# accept_connections: true # accept incoming L2CAP connections
|
||||
# scan_interval_secs: 10 # interval between scan cycles
|
||||
# beacon_interval_secs: 10 # interval between beacon bursts
|
||||
# beacon_duration_secs: 3 # duration of each beacon burst
|
||||
|
||||
peers: # static peer list
|
||||
# - npub: "npub1..."
|
||||
|
||||
@@ -524,9 +524,10 @@ forwarding, a publicly addressed peer, or relay through other mesh nodes.
|
||||
UDP hole punching and relay-assisted NAT traversal are potential future
|
||||
mechanisms but are not part of the current design.
|
||||
|
||||
> **Implementation status**: UDP/IP, TCP/IP, Ethernet, and Tor
|
||||
> (SOCKS5 outbound + directory-mode inbound via onion service)
|
||||
> transports are implemented. All others are future directions.
|
||||
> **Implementation status**: UDP/IP, TCP/IP, Ethernet, Tor
|
||||
> (SOCKS5 outbound + directory-mode inbound via onion service),
|
||||
> and Bluetooth (BLE L2CAP CoC) transports are implemented.
|
||||
> All others are future directions.
|
||||
|
||||
See [fips-transport-layer.md](fips-transport-layer.md) for the full transport
|
||||
layer specification.
|
||||
|
||||
Reference in New Issue
Block a user