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

@@ -473,6 +473,40 @@ pub fn classify_nat(probes: &[NatProbeResult]) -> (NatType, Option<String>) {
}
}
/// Enhanced NAT detection that combines relay-based reflection with
/// public STUN server probes for more robust classification.
///
/// Runs both probe sets concurrently:
/// 1. Relay probes via `detect_nat_type` (existing behavior)
/// 2. Public STUN probes via `probe_stun_servers`
///
/// Merges all results and classifies. More probes = higher confidence
/// in the NAT type classification. Falls back gracefully: if STUN
/// servers are unreachable, relay probes still work (and vice versa).
pub async fn detect_nat_type_with_stun(
relays: Vec<(String, SocketAddr)>,
timeout_ms: u64,
shared_endpoint: Option<wzp_transport::Endpoint>,
stun_config: &crate::stun::StunConfig,
) -> NatDetection {
// Run relay probes and STUN probes concurrently.
let relay_fut = detect_nat_type(relays, timeout_ms, shared_endpoint);
let stun_fut = crate::stun::probe_stun_servers(stun_config);
let (relay_detection, stun_probes) = tokio::join!(relay_fut, stun_fut);
// Merge all probes and re-classify.
let mut all_probes = relay_detection.probes;
all_probes.extend(stun_probes);
let (nat_type, consensus_addr) = classify_nat(&all_probes);
NatDetection {
probes: all_probes,
nat_type,
consensus_addr,
}
}
// ── Unit tests for the pure classifier ───────────────────────────
#[cfg(test)]