SUDPH - Skywire UDP Hole-Punch¶
SUDPH (Skywire UDP Hole-Punch) is a UDP-based transport that uses hole-punching techniques to establish direct peer-to-peer connections between visors behind NAT or firewalls.
Overview¶
SUDPH enables direct connections between visors that would otherwise be unreachable due to NAT. It uses an address-resolver server as a rendezvous point to coordinate the hole-punching process, then establishes a direct UDP connection using the KCP protocol for reliable delivery.
Architecture¶
┌──────────────────┐
│ Address Resolver │
│ (Rendezvous) │
└────────┬─────────┘
│
┌────────────────────┼────────────────────┐
│ UDP (register) │ UDP (register) │
▼ │ ▼
┌────────────┐ │ ┌────────────┐
│ NAT A │ │ │ NAT B │
└──────┬─────┘ │ └──────┬─────┘
│ │ │
┌──────┴─────┐ UDP hole-punch ┌──────┴─────┐
│ Visor A │◄─────────────────────────►│ Visor B │
│ (port AP) │ Direct connection │ (port BP) │
└────────────┘ └────────────┘
How UDP Hole-Punching Works¶
The NAT Problem¶
When a visor is behind NAT, it has a private IP address that is not directly reachable from the internet. NAT creates temporary mappings when the visor sends outbound packets, but these mappings aren't usable for incoming connections from arbitrary hosts.
The Solution¶
- Both visors (
AandB) register with the address-resolver server (S) - When
Awants to connect toB: AasksSforB's public addressSnotifiesBthatAwants to connect and providesA's address- Both visors send UDP packets to each other simultaneously
- These packets "punch holes" in their respective NATs by creating outbound mappings
- Once mappings exist on both sides, direct communication is possible
Same LAN Optimization¶
When both visors share the same public IP (same LAN), SUDPH: 1. Detects matching public IPs during address resolution 2. Retrieves the remote visor's local/LAN addresses 3. Attempts hole-punch to LAN addresses first for lower latency
Connection Flow¶
Binding (Server-side)¶
- Visor listens on a UDP port (configurable, or auto-increments if in use)
- Creates a
PacketFilterto multiplex the UDP socket between: - Address-resolver connection (for registration and signaling)
- Peer connections (for hole-punched transports)
- Performs the four-frame nonce-challenge handshake with the address-resolver over UDP (the same exchange STCP uses — see STCP.md — not Noise)
- Sends binding information (port, local addresses)
- Waits for incoming dial requests via
addrChchannel
Dialing (Client-side)¶
- Dialing visor calls
GET /resolve/sudph/{pk}to get target's address - Address-resolver notifies the target visor of the incoming dial request
- Both visors send "holepunch" packets to each other's addresses
- Dialing visor creates a KCP connection over the hole-punched UDP path
- Transport handshake completes the connection
Key Components¶
Packet Filter (pfilter)¶
The pfilter.PacketFilter multiplexes a single UDP socket for multiple logical connections:
| Priority | Connection Type | Purpose |
|---|---|---|
| 3 | visorsConn |
Accepts incoming hole-punch connections |
| 2 | dialConn |
Used for outgoing dial attempts |
Higher priority connections are checked first when routing incoming packets.
KCP Protocol¶
KCP provides reliable, ordered delivery over UDP:
- ARQ (Automatic Repeat reQuest) for reliability
- Configurable congestion control
- Lower latency than TCP in high-loss networks
Hole-Punch Message¶
A constant message "holepunch" is sent in initial UDP packets to establish NAT mappings:
Address Resolver API¶
UDP Binding (nonce-challenge handshake)¶
Visors bind via UDP with the same four-frame nonce-challenge handshake used for
STCP transports (get_nonce -> nonce -> signed addresses -> OK). It is NOT a
Noise handshake:
- Visor completes the nonce-challenge handshake with the address-resolver
- After handshake, visor sends binding data:
- Address-resolver stores the visor's public address and local addresses
GET /resolve/sudph/{pk}¶
Resolves a public key to network address. Also triggers notification to the target visor.
Response:
GET /security/nonces/{pk}¶
Returns the next expected nonce for HTTP authentication.
Timing Parameters¶
| Parameter | Value | Description |
|---|---|---|
| Dial timeout | 30 seconds | Maximum time to wait for hole-punch to succeed |
| Hole-punch retries | Continuous | Keeps trying until timeout |
Configuration¶
| Field | Description |
|---|---|
port |
Local UDP port to listen on (0 = auto-select) |
address_resolver |
URL of the address-resolver service |
Advantages¶
- NAT traversal: Works through most NAT types without port forwarding
- Direct connection: After hole-punch, traffic flows directly between peers
- Low latency: UDP + KCP provides lower latency than TCP
- Same-LAN optimization: Uses local addresses when visors share public IP
Limitations¶
- Symmetric NAT: May fail with symmetric NAT that assigns different ports per destination
- UDP blocking: Some networks block UDP traffic
- Requires rendezvous: Depends on address-resolver for initial coordination
- Connection time: Hole-punching adds setup latency
NAT Type Compatibility¶
| NAT Type | Success Rate |
|---|---|
| Full Cone | High |
| Restricted Cone | High |
| Port Restricted Cone | Medium-High |
| Symmetric | Low |
Code References¶
- Implementation:
pkg/transport/network/sudph.go - Packet filter:
internal/packetfilter/ - Address resolver client:
pkg/transport/network/addrresolver/ - Transport type constant:
pkg/transport/types/types.go:13
Useful Links¶
- UDP Hole Punching (Bford) - Seminal paper on NAT traversal
- KCP Protocol - Reliable UDP implementation