feat(nat): Tailscale-inspired STUN/ICE + port mapping + mid-call re-gathering (#28)
Some checks failed
Mirror to GitHub / mirror (push) Failing after 23s
Build Release Binaries / build-amd64 (push) Failing after 6m8s

Phase 8: 5 new modules bringing NAT traversal close to Tailscale's approach.

- stun.rs: RFC 5389 STUN client — public server reflexive discovery,
  XOR-MAPPED-ADDRESS parsing, parallel probe with retry, STUN fallback
  in desktop try_reflect_own_addr()
- portmap.rs: NAT-PMP (RFC 6886) + PCP (RFC 6887) + UPnP IGD port
  mapping — gateway discovery, acquire/release/refresh lifecycle,
  new PeerCandidates.mapped candidate type in dial order
- ice_agent.rs: candidate lifecycle — gather(), re_gather(),
  apply_peer_update() with monotonic generation counter,
  CandidateUpdate signal message forwarded by relay
- netcheck.rs: comprehensive diagnostic — NAT type, IPv4/v6,
  port mapping availability, relay latencies, CLI --netcheck
- relay_map.rs: RTT-sorted relay map, preferred() selection,
  populate_from_ack() for RegisterPresenceAck.available_relays

Relay: CallRegistry stores + cross-wires caller/callee_mapped_addr
into CallSetup.peer_mapped_addr. Region config + available_relays
populated from federation peers in RegisterPresenceAck.

Desktop: place_call/answer_call call acquire_port_mapping() and
fill caller/callee_mapped_addr. STUN+relay combined NAT detection.

571 tests pass (66 new), 0 regressions, 0 warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-14 10:17:17 +04:00
parent 9377a9009c
commit 8fcf1be341
26 changed files with 4555 additions and 44 deletions

View File

@@ -105,15 +105,25 @@ Sentinel value `0xFF` means "no change pending". The recv task polls on every re
~~The Tauri engine doesn't use `AdaptiveQualityController` — quality is resolved once at call start.~~ **Update (2026-04-13):** Desktop now has `AdaptiveQualityController` wired into the recv task with `pending_profile` AtomicU8 bridge. Network monitoring on desktop is now feasible — the blocker was adaptive quality, which is done. Remaining work: platform-specific network change detection (macOS: `SCNetworkReachability` or `NWPathMonitor`; Linux: `netlink` socket).
### Mid-Call ICE Re-gathering
### Mid-Call ICE Re-gathering — PARTIALLY IMPLEMENTED (2026-04-14)
When the device's IP address changes, ideally we should:
1. Re-gather local host candidates (`local_host_candidates()`)
2. Re-probe STUN (`probe_reflect_addr()`)
3. Send updated candidates to the peer (`CandidateUpdate` signal message)
4. Attempt new dual-path race for path upgrade
When the device's IP address changes, the system now:
1. Re-gather local host candidates (`local_host_candidates()`)
2. Re-probe STUN (`stun::discover_reflexive()` + `portmap::acquire_port_mapping()`)
3. Send updated candidates to the peer (`CandidateUpdate` signal message)
4. Relay forwards `CandidateUpdate` to peer (same pattern as `MediaPathReport`) ✅
5. Peer receives and can parse via `IceAgent::apply_peer_update()`
6. Attempt new dual-path race for path upgrade — **NOT YET WIRED** (transport hot-swap)
`NetworkMonitor.onIpChanged` fires on `onLinkPropertiesChanged` — the hook is ready, but the signaling and re-racing logic is not yet implemented.
`NetworkMonitor.onIpChanged` fires on `onLinkPropertiesChanged` — the hook is ready.
The signaling plane is fully implemented via `IceAgent` + `CandidateUpdate`.
Remaining: wire `onIpChanged` → JNI → `pending_ice_regather` AtomicBool → recv task → `ice_agent.re_gather()` → transport swap.
New modules added in Phase 8 (Tailscale-inspired):
- `crates/wzp-client/src/ice_agent.rs` — candidate lifecycle management
- `crates/wzp-client/src/stun.rs` — public STUN server probing (independent of relay)
- `crates/wzp-client/src/portmap.rs` — NAT-PMP/PCP/UPnP port mapping
- `crates/wzp-client/src/netcheck.rs` — comprehensive network diagnostic
## Testing