/ docs / archive / STUN_NAT_TRAVERSAL.md
STUN_NAT_TRAVERSAL.md
  1  # Abzu STUN & NAT Traversal
  2  
  3  Technical documentation for Abzu's STUN-based NAT discovery and UDP hole punching.
  4  
  5  ---
  6  
  7  ## Overview
  8  
  9  Abzu uses STUN (Session Traversal Utilities for NAT) to discover public IP:port mappings and establish direct peer connections through NATs. The implementation lives in `abzu-transport/src/nat.rs`.
 10  
 11  ## Architecture
 12  
 13  ```
 14  ┌─────────────────┐     STUN Query     ┌────────────────────┐
 15  │   Local Node    │──────────────────▶│  STUN Server(s)    │
 16  │  (Private IP)   │◀──────────────────│  (Public Internet)  │
 17  └─────────────────┘   XOR-MAPPED-ADDR  └────────────────────┘
 18 19           │  Mapped Address                      
 20 21  ┌─────────────────┐                    ┌────────────────────┐
 22  │  NAT Gateway    │◀──────────────────▶│    Peer Node       │
 23  │  (Port Mapping) │   UDP Hole Punch   │ (After Punch)      │
 24  └─────────────────┘                    └────────────────────┘
 25  ```
 26  
 27  ## STUN Servers
 28  
 29  Default configured servers (reliable public infrastructure):
 30  
 31  | Server | Port | Provider |
 32  |--------|------|----------|
 33  | `stun.cloudflare.com` | 3478 | Cloudflare |
 34  | `stun.l.google.com` | 19302 | Google |
 35  | `stun1.l.google.com` | 19302 | Google |
 36  | `stun2.l.google.com` | 19302 | Google |
 37  | `stun.stunprotocol.org` | 3478 | Community |
 38  
 39  ---
 40  
 41  ## NAT Type Detection
 42  
 43  Abzu queries **3 servers** (configurable) and compares the returned mapped addresses:
 44  
 45  ### Detection Algorithm
 46  
 47  ```rust
 48  if mappings_from_all_servers_identical() {
 49      NatType::RestrictedCone  // Conservative: could be FullCone
 50  } else {
 51      NatType::Symmetric       // Different port per destination = worst case
 52  }
 53  ```
 54  
 55  ### NAT Types
 56  
 57  | Type | Behavior | Traversability |
 58  |------|----------|----------------|
 59  | **None** | No NAT, public IP | ✅ Easy |
 60  | **Full Cone** | Any host can send to mapped addr | ✅ Easy |
 61  | **Restricted Cone** | Only hosts we've sent to can reply | ⚠️ Moderate |
 62  | **Port-Restricted Cone** | Host + port must match | ⚠️ Hard |
 63  | **Symmetric** | Different mapping per destination | ❌ Very Hard (relay needed) |
 64  
 65  ```rust
 66  // Helper methods
 67  nat_type.is_traversable()  // true for all cone types
 68  nat_type.needs_relay()     // true only for Symmetric
 69  ```
 70  
 71  ---
 72  
 73  ## Configuration
 74  
 75  ```rust
 76  pub struct NatConfig {
 77      /// STUN servers to query
 78      pub stun_servers: Vec<String>,
 79      
 80      /// Timeout for each STUN request (default: 3s)
 81      pub request_timeout: Duration,
 82      
 83      /// Number of retries per server (default: 2)
 84      pub retries: u32,
 85      
 86      /// Base delay between retries (default: 500ms)
 87      /// Note: Exponential backoff applied
 88      pub retry_delay: Duration,
 89      
 90      /// Servers to query for NAT detection (default: 3)
 91      pub detection_server_count: usize,
 92      
 93      /// Hole punch attempts (default: 5)
 94      pub punch_attempts: u32,
 95      
 96      /// Delay between punch attempts (default: 100ms)
 97      pub punch_delay: Duration,
 98  }
 99  ```
100  
101  ---
102  
103  ## STUN Protocol Flow
104  
105  ### 1. Binding Request
106  
107  ```
108  Client                    STUN Server
109    │                            │
110    │─── Binding Request ───────▶│
111    │    (Transaction ID)        │
112    │                            │
113    │◀── Binding Response ───────│
114    │    (XOR-MAPPED-ADDRESS)    │
115  ```
116  
117  ### 2. Wire Format (RFC 5389)
118  
119  ```
120   0                   1                   2                   3
121   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
122  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
123  |0 0|     STUN Message Type     |         Message Length        |
124  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
125  |                     Magic Cookie (0x2112A442)                 |
126  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
127  |                                                               |
128  |                   Transaction ID (96 bits)                    |
129  |                                                               |
130  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
131  ```
132  
133  ### 3. Response Parsing
134  
135  Abzu extracts the **XOR-MAPPED-ADDRESS** attribute:
136  
137  - Validates Transaction ID matches request
138  - Verifies MessageClass is `Success`
139  - Unmarshals the XORed IP:port with transaction ID
140  
141  ---
142  
143  ## UDP Hole Punching
144  
145  ### Simple Punch
146  
147  Sends multiple packets to create NAT bindings:
148  
149  ```rust
150  resolver.punch(target_addr).await?;
151  // Sends 5 packets with exponential backoff delays
152  ```
153  
154  ### Coordinated Punch (Both Sides Simultaneous)
155  
156  For reliable traversal, both peers should punch at the same time:
157  
158  ```rust
159  // Packet format: "ABZU_COORD" + [32-byte peer ID]
160  resolver.punch_coordinated(&socket, peer_addr, &our_id).await?;
161  ```
162  
163  **Protocol:**
164  
165  1. Both sides send `ABZU_COORD{id}` packets
166  2. Short receive timeout (200ms) between attempts
167  3. Success when receiving peer's packet
168  4. 5 attempts with configurable delay
169  
170  ---
171  
172  ## Integration with Node
173  
174  The `abzu-core` crate exposes high-level APIs:
175  
176  ```rust
177  // Discover NAT type and public address
178  let mapping = node.discover_nat().await?;
179  println!("Public: {}", mapping.mapped_addr);
180  println!("NAT Type: {:?}", mapping.nat_type);
181  
182  // NAT-aware peer connection
183  let peer_key = node.connect_peer_nat(&peer_info, &psk).await?;
184  ```
185  
186  ### Connection Flow
187  
188  ```
189  1. discover_nat()           → Get our public mapping
190  2. Exchange mappings        → Share via signaling/announce
191  3. punch_coordinated()      → Both sides punch simultaneously  
192  4. TLS/TCP connection       → Establish encrypted channel
193  ```
194  
195  ---
196  
197  ## Announce Frame (P2P Address Exchange)
198  
199  Peers broadcast their NAT mapping via `AbzuFrame::Announce`:
200  
201  ```rust
202  AbzuFrame::Announce {
203      peer_key: [u8; 32],      // Sender's public key
204      public_addr: Vec<u8>,     // Serialized SocketAddr
205      nat_type: u8,             // NatType discriminant
206      timestamp: u64,           // Unix epoch seconds
207  }
208  ```
209  
210  **Validation:**
211  
212  - Announcements older than **5 minutes** are rejected
213  - Prevents replay attacks and stale data
214  
215  ---
216  
217  ## Error Handling
218  
219  | Error | Meaning | Recovery |
220  |-------|---------|----------|
221  | `NoResponse` | All STUN servers unreachable | Check connectivity |
222  | `Timeout` | Server didn't respond in time | Retry, check firewall |
223  | `SymmetricNat` | Direct connection unlikely | Use relay |
224  | `DnsError` | Can't resolve STUN server | Check DNS |
225  | `PunchFailed` | Hole punch exhausted attempts | Try relay |
226  
227  ---
228  
229  ## Testing
230  
231  ```bash
232  # Unit tests (no network)
233  cargo test -p abzu-transport nat::
234  
235  # Live integration test (requires network)
236  cargo test -p abzu-transport --test '*' -- --ignored test_discover_public_ip
237  ```
238  
239  ---
240  
241  ## Security Considerations
242  
243  1. **Transaction ID Verification**: Prevents response spoofing
244  2. **Source Address Check**: Response must come from queried server
245  3. **Timestamp Validation**: 5-minute window on announcements
246  4. **No STUN Authentication**: Using public servers (no credential exchange)
247  
248  ---
249  
250  ## Dependencies
251  
252  ```toml
253  # abzu-transport/Cargo.toml
254  stun_proto = "0.1"  # RFC 5389 STUN protocol
255  rand = "0.8"        # Transaction ID generation
256  ```
257  
258  ---
259  
260  ## Future Work
261  
262  - [ ] TURN relay fallback for symmetric NAT
263  - [ ] ICE-lite for optimal candidate selection
264  - [ ] UPnP/NAT-PMP for direct port mapping
265  - [ ] Gossip-based announcement forwarding