feat(nat): hard NAT port allocation detection + prediction + HardNatProbe signal (#29)
Some checks failed
Mirror to GitHub / mirror (push) Failing after 31s
Build Release Binaries / build-amd64 (push) Failing after 3m30s

Phase A of hard NAT traversal (PRD-hard-nat.md):

- PortAllocation enum: PortPreserving / Sequential{delta} / Random / Unknown
- detect_port_allocation(): sequential STUN probes from single socket,
  analyzes port sequence for allocation pattern
- classify_port_allocation(): pure function with jitter tolerance,
  wraparound handling, 60% threshold for noisy sequences
- predict_ports(): generates target port range from last_port + delta
- HardNatProbe signal message: carries port_sequence, allocation
  pattern, external_ip for peer coordination
- Relay forwards HardNatProbe to call peer
- Netcheck gains port_allocation field + format_report display

588 tests pass (17 new), 0 regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-14 11:29:35 +04:00
parent ee14862376
commit ec1bdf3cd5
5 changed files with 434 additions and 2 deletions

View File

@@ -947,6 +947,26 @@ pub enum SignalMessage {
generation: u32,
},
// ── Hard NAT traversal (port prediction) ──────────────────────
/// Hard NAT probe coordination — exchanged when both peers
/// detect symmetric NAT. Carries the port allocation pattern
/// and recent port sequence so the peer can predict which port
/// to dial.
HardNatProbe {
call_id: String,
/// Last observed external ports (most recent first).
/// Typically 3-5 entries from sequential STUN probes.
port_sequence: Vec<u16>,
/// Detected allocation pattern as string:
/// "sequential:N" (N=delta), "random", "preserving"
allocation: String,
/// Probe timestamp (ms since epoch) for synchronization.
probe_time_ms: u64,
/// External IP from STUN.
external_ip: String,
},
// ── Phase 4: cross-relay direct-call signaling ────────────────────
/// Phase 4: relay-to-relay envelope for forwarding direct-call