Skip to content

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

ModeWho needs a public portNAT-friendlyDeterministic
ListenerListener (server)No — needs open portYes
CallerListener (server)Caller works through NATYes
RendezvousNeitherYes — hole punchingProbabilistic

Punch uses rendezvous mode — both sides initiate simultaneously, and NAT hole punching creates a direct path.

How UDP hole punching works

DecoderNAT BNAT AEncoderDecoderNAT BNAT AEncodert = 0 — both sides fire simultaneouslyCreates outbound mappingCreates outbound mappingt = Δ — packets cross in flightDirect UDP tunnel established (bidirectional)UDP packet to B's IP:port1UDP packet to A's IP:port2B's packet (matches mapping)3A's packet (matches mapping)4
  1. Both sides send a UDP packet to the other’s public IP:port simultaneously
  2. Each NAT sees an outbound packet and creates a mapping (source port → destination)
  3. When the other side’s packet arrives, the NAT matches it to the outbound mapping
  4. Both NATs now allow bidirectional traffic on that port pair
  5. 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 TypeMapping behaviourHole-punch successPrevalence
Full ConeSame external port for all destinations~95%+Home routers (older)
Restricted ConeSame port, filtered by destination IP~85-90%Home routers (common)
Port-Restricted ConeSame port, filtered by destination IP+port~70-80%Home routers (newer)
SymmetricDifferent 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

  1. Both peers connect to Punch via WebSocket
  2. Punch records each peer’s public IP (from CF-Connecting-IP header)
  3. Peers report their local SRT port
  4. Punch sends each peer the other’s IP:port
  5. Both peers start SRT in rendezvous mode simultaneously
  6. Hole punching succeeds (90% of cases) → direct P2P stream

Phase 2 (roadmap): Relay fallback

For the ~10% of cases where hole punching fails:

SRT caller

SRT caller

forwards

forwards

Encoder

Relay server

Decoder

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:

  1. Sending peer messages to both sides near-simultaneously
  2. Both peers are already connected via WebSocket → minimal coordination delay
  3. 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

SystemApproachRelay?Protocol
PunchSignalling + SRT rendezvousPlannedSRT (UDP)
TailscaleDERP relay + WireGuard hole punchYes (default)WireGuard (UDP)
WebRTCICE + STUN + TURNYes (standard)SRTP (UDP/TCP)
LiveU LRTProprietary bondingYes (cloud)Proprietary
Teradek SharelinkProprietary relayYes (cloud)Proprietary

Punch’s architecture is most similar to Tailscale’s DERP model, adapted for SRT instead of WireGuard.

References