NAT traversal
How Punch gets SRT through NAT — and what happens when it can’t.
The problem
SRT requires both endpoints to know each other’s public IP and port before connecting. In the real world:
- Hotel WiFi — double NAT, no port forwarding, shared public IP
- Event networks — IT controls the firewall, no exceptions for your one-off stream
- Mobile data — carrier-grade NAT (CGNAT), symmetric mapping, short UDP session timeouts
- Corporate networks — proxy-only egress, UDP blocked entirely
The classic caller/listener model requires one side to have an open port on a public IP. When neither side can open a port, SRT cannot connect.
SRT connection modes
| Mode | Who needs a public port | NAT-friendly | Deterministic |
|---|---|---|---|
| Listener | Listener (server) | No — needs open port | Yes |
| Caller | Listener (server) | Caller works through NAT | Yes |
| Rendezvous | Neither | Yes — hole punching | Probabilistic |
Punch uses rendezvous mode — both sides initiate simultaneously, and NAT hole punching creates a direct path.
How UDP hole punching works
- Both sides send a UDP packet to the other’s public IP:port simultaneously
- Each NAT sees an outbound packet and creates a mapping (source port → destination)
- When the other side’s packet arrives, the NAT matches it to the outbound mapping
- Both NATs now allow bidirectional traffic on that port pair
- SRT completes its handshake over this tunnel
The critical requirement: Both sides must know each other’s public IP:port before they start sending. This is what Punch provides.
NAT types and success rates
Not all NATs behave the same. Success depends on how the NAT maps outbound connections:
| NAT Type | Mapping behaviour | Hole-punch success | Prevalence |
|---|---|---|---|
| Full Cone | Same external port for all destinations | ~95%+ | Home routers (older) |
| Restricted Cone | Same port, filtered by destination IP | ~85-90% | Home routers (common) |
| Port-Restricted Cone | Same port, filtered by destination IP+port | ~70-80% | Home routers (newer) |
| Symmetric | Different port per destination | ~0-5% | Corporate, CGNAT, mobile |
Real-world data (Tailscale, 2024): Direct NAT traversal succeeds >90% of the time across their production fleet of millions of devices. The majority of consumer routers are cone NATs.
The symmetric NAT problem
Symmetric NAT assigns a different external port for each destination. When the encoder sends to the decoder’s known port, NAT A creates a new mapping on a random port. The decoder’s reply arrives at its NAT on a port that doesn’t match any mapping. Rejected.
This affects:
- Corporate firewalls — especially Cisco ASA, Palo Alto in strict mode
- Carrier-grade NAT (CGNAT) — common on mobile data (especially 4G/LTE)
- Double NAT — where the outer NAT is symmetric (ISP-controlled)
Symmetric NAT represents approximately 10% of real-world connections.
Punch’s approach
Phase 1 (current): Signalling-assisted rendezvous
- Both peers connect to Punch via WebSocket
- Punch records each peer’s public IP (from
CF-Connecting-IPheader) - Peers report their local SRT port
- Punch sends each peer the other’s
IP:port - Both peers start SRT in rendezvous mode simultaneously
- Hole punching succeeds (90% of cases) → direct P2P stream
Phase 2 (roadmap): Relay fallback
For the ~10% of cases where hole punching fails:
A lightweight UDP relay accepts SRT caller connections from both peers and forwards packets between them. The relay is a separate component from the signalling server.
Design principle (from Tailscale): Relay-first, upgrade-second.
The ideal architecture starts media flowing through the relay immediately, then attempts hole punching in the background. If hole punching succeeds, the stream upgrades to direct P2P transparently. The operator never waits.
Phase 3 (future): STUN-based NAT classification
Before attempting rendezvous, peers could probe their NAT type using STUN:
Peer → STUN server: "What's my public IP:port?"STUN → Peer: "You're 203.0.113.5:48291"
Peer → STUN server (different port): "What's my public IP:port now?"STUN → Peer: "You're 203.0.113.5:48291" ← Same port = cone NAT (good) "You're 203.0.113.5:52107" ← Different port = symmetric NAT (bad)Punch could use this to:
- Predict success before attempting rendezvous
- Route immediately to relay when both sides are symmetric NAT
- Display NAT type on the dashboard for diagnostics
Timing considerations
SRT rendezvous mode has a timeout window. Both sides must start within this window for the handshake to succeed. Punch coordinates this by:
- Sending
peermessages to both sides near-simultaneously - Both peers are already connected via WebSocket → minimal coordination delay
- SRT’s default rendezvous timeout is generous (configurable, typically 30s)
The WebSocket approach is significantly better than HTTP polling for timing coordination — the server pushes coordinates to both peers within milliseconds of each other.
Known limitations
CGNAT with short UDP timeouts
Some mobile carriers set UDP session timeouts as low as 30 seconds. If the hole-punch takes longer than this, the NAT mapping expires before the SRT handshake completes. Mitigation: keep-alive packets during the rendezvous phase.
Identical cookies
A rare edge case in SRT’s HSv5 handshake: if both peers generate the same 32-bit cookie, the handshake stalls for up to 60 seconds before retrying with a new cookie. Punch could detect this (both peers report WAVING state for >5s) and trigger a reconnect.
IPv6
libsrt has had IPv6 support for caller and listener modes since 1.4 (SRTO_IPV6ONLY socket option), but rendezvous-mode IPv6 has a documented history of implementation issues (see Haivision/srt#1545 and related tickets). Punch v1 treats IPv6 rendezvous as untested rather than supported — the signalling layer carries whatever address the peer reports, but we have not validated end-to-end IPv6 rendezvous across real CGNAT-affected carriers. Treat dual-stack as best-effort until we publish field results; do not rely on IPv6 as a CGNAT-bypass strategy in this version.
UDP-blocked networks
Some networks block all outbound UDP traffic (corporate proxies, some hotel WiFi captive portals). In these environments, neither rendezvous nor relay will work over UDP. A future TURN-over-TCP or TURN-over-HTTPS relay could address this, at the cost of significant latency.
Comparison with other NAT traversal systems
| System | Approach | Relay? | Protocol |
|---|---|---|---|
| Punch | Signalling + SRT rendezvous | Planned | SRT (UDP) |
| Tailscale | DERP relay + WireGuard hole punch | Yes (default) | WireGuard (UDP) |
| WebRTC | ICE + STUN + TURN | Yes (standard) | SRTP (UDP/TCP) |
| LiveU LRT | Proprietary bonding | Yes (cloud) | Proprietary |
| Teradek Sharelink | Proprietary relay | Yes (cloud) | Proprietary |
Punch’s architecture is most similar to Tailscale’s DERP model, adapted for SRT instead of WireGuard.
References
- How NAT Traversal Works — Tailscale — definitive deep dive on NAT types and hole punching
- RFC 4787 — NAT Behavioural Requirements for UDP — NAT classification
- RFC 8489 — STUN — NAT type detection
- SRT RFC Draft — Rendezvous — SRT handshake mechanics
- UDP Hole Punching — Wikipedia — overview