Skip to content
Menu

Networking8 min read

STUN, TURN & ICE

The protocols that punch through NATs

STUN (Session Traversal Utilities for NAT)

Defined in RFC 8489 (supersedes RFC 5389 and the original RFC 3489). STUN's core purpose is simple: tell a client what its public IP:port looks like from the outside. It's a lightweight, stateless protocol — just a few UDP packets.

How STUN Works

  1. Client sends a Binding Request to a STUN server (typically on port 3478)
  2. The STUN server sees the source IP:port of the incoming packet — this is the NAT's external mapping
  3. Server replies with a Binding Response containing XOR-MAPPED-ADDRESS — the client's public IP:port
  4. Client now knows its own public address as seen by the outside world

STUN Binding Request/Response

Client
192.168.1.5:12345
STUN Server
198.51.100.1:3478
Binding Request (UDP)
NAT translates src to 203.0.113.1:54321
Binding Response: XOR-MAPPED-ADDRESS = 203.0.113.1:54321
Type 0x0001 (Binding Req) / 0x0101 (Binding Resp)
Length Message body length
Magic Cookie 0x2112A442 (fixed)
Transaction ID 96 bits — matches request to response

Note

Why XOR-MAPPED-ADDRESS instead of plain MAPPED-ADDRESS? Some middleboxes (ALGs, overzealous firewalls) inspect packet payloads for IP addresses and rewrite them. XOR-ing with the magic cookie prevents these devices from interfering with the address in the response.

STUN Limitations

What STUN Cannot Do

  • Useless with Symmetric NAT (APDM): The address STUN reports is only valid for that specific STUN server. A different peer will see a different mapping.
  • No relay capability: STUN only discovers addresses — if direct connectivity fails, STUN can't help.
  • Blocked by strict firewalls: If outbound UDP is blocked entirely, STUN requests never reach the server.
  • No authentication by default: Basic Binding requests are unauthenticated (though STUN supports MESSAGE-INTEGRITY for authenticated uses in ICE).

UDP Hole Punching — Step by Step

The fundamental technique for establishing direct UDP connectivity between two hosts behind NAT. Requires both NATs to use Endpoint-Independent Mapping (EIM) for reliable operation.

Prerequisites

  1. STUN Discovery

    Both peers learn their public IP:port

  2. Signaling Exchange

    Peers share addresses via a server

  3. Simultaneous Send

    Both send to each other's public addr

  4. Direct Connection

    NAT mappings now allow bidirectional flow

Detailed Sequence

Peer A
10.0.0.5:8000
NAT A
203.0.113.1
NAT B
198.51.100.1
Peer B
10.0.0.9:8000
Phase 1: STUN Discovery (both peers learn their public addresses)
A discovers: 203.0.113.1:54321
B discovers: 198.51.100.1:54987
Phase 2: Exchange addresses via signaling/coordination server
Phase 3: Simultaneous send (both peers send to each other's public address)
A sends to 198.51.100.1:54987
B sends to 203.0.113.1:54321
Both NATs now have outbound mappings AND accept inbound from each other

Why It Works (with EIM NATs)

  • EIM guarantees stable mappings: Peer A's STUN-discovered address 203.0.113.1:54321 is the same address used when A sends to Peer B. The NAT reuses the mapping.
  • Outbound creates the "hole": When A sends to B's public address, NAT A creates an outbound mapping that also allows inbound from B's IP (ADF) or B's IP:port (APDF).
  • Timing matters with APDF: With Address+Port-Dependent Filtering, B's NAT will only accept packets from an IP:port that B has sent to. So B must also send to A's public address, creating the reciprocal hole.
  • Simultaneous = both holes open: Once both peers have sent packets, both NATs have outbound mappings and will accept the other's packets.

Warning

Why it fails with APDM (Symmetric) NAT: When Peer A sends to Peer B, the Symmetric NAT creates a new mapping (different from what STUN reported). B is sending to A's STUN-reported address, which is now the wrong port. Neither peer's packets reach the other.

TCP Hole Punching (Simultaneous Open)

TCP's three-way handshake (SYN, SYN-ACK, ACK) doesn't seem compatible with hole-punching. But TCP has a rarely-used feature: simultaneous open — both sides send SYN at the same time, and the connection establishes via SYN, SYN+ACK, ACK on both sides.

CLOSED
→ both send SYN
SYN-SENT
→ both receive SYN
SYN-RECEIVED
→ both send SYN+ACK
ESTABLISHED
UDP Hole Punching
  • Stateless — just send packets
  • Timing is forgiving (seconds window)
  • Well-understood, high success rate
  • Works with most EIM NATs
TCP Hole Punching
  • Requires simultaneous SYN (tight timing)
  • Many NATs don't handle TCP simultaneous open correctly
  • OS-level quirks: some stacks reset on unexpected SYN
  • Firewalls often statefully track TCP and block this

Tip

Practical reality: TCP hole punching works maybe 60% of the time vs 90%+ for UDP. It's used as a fallback when UDP is blocked (e.g., some corporate firewalls block all UDP except DNS). Tailscale primarily uses UDP (WireGuard) but can fall back to TCP via DERP relay over HTTPS.

TURN (Traversal Using Relays around NAT)

Defined in RFC 8656. TURN is the "if all else fails" protocol. When direct connectivity is impossible (both sides behind Symmetric NAT, UDP blocked, etc.), traffic is relayed through a TURN server. Every packet goes: Client A → TURN server → Client B.

How TURN Works

  1. Allocate: Client sends an Allocate request to the TURN server. Server allocates a relay address (a public IP:port on the TURN server) and returns it to the client.
  2. Permission: Client creates permissions on the TURN server specifying which peers can send to its relay address.
  3. Channel Bind: Optionally, client binds a channel number (2 bytes) to a specific peer for reduced overhead (4-byte channel header vs 36-byte TURN header).
  4. Relay: Data from the peer arrives at the relay address, and the TURN server forwards it to the client. Bidirectional.

TURN Relay Topology

Peer A
Behind Symmetric NAT
NAT A
APDM + APDF
TURN Server
Relays all traffic
NAT B
APDM + APDF
Peer B
Behind Symmetric NAT

TURN Costs & Trade-offs

Aspect Impact
Bandwidth TURN server carries ALL data — doubles bandwidth cost (ingress + egress). Expensive at scale.
Latency Extra hop through the relay. Path: A → TURN → B instead of A → B. Can add 10-100ms+ RTT.
Reliability TURN server is a single point of failure and a bottleneck. Must be highly available.
Privacy The relay sees unencrypted metadata (source/dest IPs). Payload can be encrypted (DTLS/SRTP), but the relay knows who's talking to whom.

Note

TURN transport options: The client-to-TURN-server connection can use UDP, TCP, or TLS-over-TCP. The TLS option is important because some networks block all UDP — TLS over TCP on port 443 looks like HTTPS and passes through most firewalls. The TURN-server-to-peer leg is typically UDP.

ICE (Interactive Connectivity Establishment)

Defined in RFC 8445. ICE is not a single protocol — it's a framework that orchestrates STUN, TURN, and direct connectivity into a systematic candidate gathering and connectivity checking process. If STUN is "discover your address" and TURN is "relay traffic," ICE is "try everything and pick the best working path."

Candidate Types

Candidate Type Source Priority Description
Host Local interface Highest Direct local IP addresses (e.g., 192.168.1.5:8000). Works if peers are on the same LAN.
Server Reflexive (srflx) STUN Medium Public IP:port discovered via STUN. Enables hole-punching for NAT traversal.
Peer Reflexive (prflx) Connectivity check Medium Discovered during ICE checks — a mapping that wasn't known during gathering. Happens with some NATs.
Relay TURN Lowest TURN relay address. Last resort — guaranteed to work but adds latency and cost.

ICE Process Flow

  1. Gather Candidates

    Host, STUN (srflx), TURN (relay)

  2. Exchange via Signaling

    SDP offer/answer carries candidates

  3. Pair Candidates

    Every local candidate paired with every remote

  4. Connectivity Checks

    STUN Binding on each pair, ordered by priority

  5. Nominate Best Pair

    Highest-priority working pair wins

Candidate Pairing & Priority

How ICE Selects the Best Path

ICE creates a checklist of candidate pairs, ordered by priority. Priority formula favors:

  1. Host-to-Host — both peers on the same LAN (direct, zero NAT traversal)
  2. Host-to-srflx / srflx-to-Host — one peer on public IP, other behind NAT
  3. srflx-to-srflx — both behind NAT, hole-punching
  4. Any-to-relay / relay-to-Any — TURN relay, last resort

Connectivity checks use STUN Binding requests on each pair. The check succeeds if both peers can exchange STUN messages on that candidate pair. Failed checks are pruned, and the highest-priority surviving pair is nominated for data transfer.

  1. Gathering phase: Agent collects all candidates (host IPs, STUN-derived reflexive addresses, TURN allocations). This happens in parallel — STUN and TURN requests fire simultaneously.
  2. Exchange phase: Candidates are sent to the remote peer via the signaling channel (WebRTC uses SDP in offer/answer; Tailscale uses the coordination server).
  3. Checking phase: Both agents perform STUN connectivity checks on each candidate pair in priority order. Checks happen from both sides simultaneously (ICE is symmetric).
  4. Nomination: The controlling agent picks the best valid pair. In regular nomination, it re-checks with a USE-CANDIDATE flag. In aggressive nomination, every check includes USE-CANDIDATE and the first success wins.
  5. Completed: Data flows over the nominated pair. ICE can perform periodic keep-alive checks and switch paths if the nominated pair fails (ICE restart).

Tip

ICE-Lite: Servers with public IPs don't need the full ICE state machine. ICE-Lite is a minimal implementation for hosts that aren't behind NAT — they only respond to checks, never initiate them. Common for SFUs (Selective Forwarding Units) and media servers.

Trickle ICE

Regular ICE
  • Gather ALL candidates first
  • Then exchange them
  • Then start checking
  • Slow: must wait for TURN allocation (can take seconds)
Trickle ICE
  • Start exchanging candidates as they're discovered
  • Connectivity checks begin immediately
  • New candidates added to the checklist incrementally
  • Much faster: host candidates checked while STUN/TURN still gathering

How It All Fits Together

ICE — Framework / Orchestrator
STUN — Address Discovery & Connectivity Checks
TURN — Relay (last resort)
UDP / TCP / TLS Transport

Decision Tree

  1. Same LAN?

    Use host candidate

  2. EIM NAT?

    STUN + hole punch

  3. Symmetric NAT?

    Port prediction (maybe)

  4. All else fails

    TURN relay

Warning

The cost of TURN at scale: WebRTC services (video calls, etc.) pay heavily for TURN bandwidth. A 1:1 video call at 2 Mbps through TURN costs the provider 2 Mbps ingress + 2 Mbps egress per stream direction. This is why optimizing NAT traversal to avoid relay is critical — and why Tailscale invested so heavily in DERP (their TURN equivalent) optimization.

Solidnines — solidnines.com