feat(nat): smart candidate filtering + acceptor NAT tickle + 4s timeout
Major P2P improvements for cross-network calls: Smart candidate filtering (smart_dial_order): - Strip LAN candidates when peer's public IP differs from ours (172.16.x.x is unreachable from a different network) - Strip all IPv6 candidates (Phase 7 disabled, wastes dial slots) - Only keep mapped + reflexive for cross-network calls - LAN candidates preserved when both peers share the same public IP Acceptor NAT tickle: - A-role sends a 1-byte UDP packet to each peer candidate BEFORE accepting. This opens the NAT pinhole for return traffic from the Dialer's IP — critical for address-restricted NATs that only allow inbound from IPs they've seen outbound traffic to. - Uses SO_REUSEADDR on the same port as the quinn endpoint. Direct timeout increased from 2s to 4s: - Cross-network QUIC handshakes through CGNAT can take 2-3s - 2s was too aggressive for 5G/LTE networks Diagnostic fix: - Record "timeout:4s" for candidates still in-flight when the timeout fires (previously these had no diagnostic entry) 5 new tests for smart_dial_order edge cases. 593 tests pass, 0 regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -118,6 +118,7 @@ async fn dual_path_direct_wins_on_loopback() {
|
||||
relay_addr,
|
||||
"test-room".into(),
|
||||
"call-test".into(),
|
||||
None, // own_reflexive: not needed in tests
|
||||
None, // Phase 5: tests use fresh endpoints (no shared signal)
|
||||
None, // Phase 7: no IPv6 endpoint in tests
|
||||
)
|
||||
@@ -162,6 +163,7 @@ async fn dual_path_relay_wins_when_direct_is_dead() {
|
||||
relay_addr,
|
||||
"test-room".into(),
|
||||
"call-test".into(),
|
||||
None, // own_reflexive: not needed in tests
|
||||
None, // Phase 5: tests use fresh endpoints (no shared signal)
|
||||
None, // Phase 7: no IPv6 endpoint in tests
|
||||
)
|
||||
@@ -202,6 +204,7 @@ async fn dual_path_errors_cleanly_when_both_paths_dead() {
|
||||
dead_relay,
|
||||
"test-room".into(),
|
||||
"call-test".into(),
|
||||
None, // own_reflexive: not needed in tests
|
||||
None, // Phase 5: tests use fresh endpoints (no shared signal)
|
||||
None, // Phase 7: no IPv6 endpoint in tests
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user