niom-turn/docs/architecture/tcp_tls_data_plane.md

99 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TCP/TLS Data Plane (TURN over TCP/TLS) in niom-turn
This document explains **why** a TCP/TLS data plane matters for TURN and **how** `niom-turn` currently implements it.
## Why is this needed?
TURN has two kinds of traffic:
- **Control plane**: STUN/TURN requests/responses (Allocate, CreatePermission, ChannelBind, Refresh, …)
- **Data plane**: application data between client and peer flowing through the relay (Send/Data Indication or ChannelData)
In many real-world networks, **UDP is restricted or fully blocked** (corporate WiFi, mobile APNs, captive portals, proxy environments). WebRTC/ICE typically tries:
1. UDP (fast, preferred)
2. TURN over TCP
3. TURN over TLS ("turns:") as the last, but often working option
For TURN over TCP/TLS to be usable, not only the control plane must run over the stream, but also the **return path peer → client** (data plane) must arrive over the **same TCP/TLS connection**.
## What does niom-turn implement?
- Client ↔ server transport can be **UDP**, **TCP**, or **TLS**.
- The relay to the peer remains **UDP** (classic TURN UDP relay).
- For **TCP/TLS**, the server delivers the data-plane return path back to the client over the **stream**.
This matches the common WebRTC fallback: “client↔server over TCP/TLS, peer transport over UDP”.
## Architecture in code
- Stream-Handler (TCP/TLS): [src/turn_stream.rs](../src/turn_stream.rs)
- TCP Listener: [src/tcp.rs](../src/tcp.rs)
- TLS Listener: [src/tls.rs](../src/tls.rs)
- Allocation + Relay: [src/alloc.rs](../src/alloc.rs)
### Key idea: `ClientSink`
So the relay loop can send peer packets back over different client transports, [src/alloc.rs](../src/alloc.rs) uses an abstraction:
- `ClientSink::Udp { sock, addr }` → sends peer data via `udp.send_to(..., addr)`
- `ClientSink::Stream { tx }` → queues bytes into a writer task that writes onto the TCP/TLS stream
When a client allocates over TCP/TLS, the allocation is created with a `ClientSink::Stream`.
## Framing: STUN vs. ChannelData on a byte stream
On UDP you receive datagrams; on TCP/TLS you receive a **continuous byte stream**. TURN over TCP/TLS multiplexes:
- STUN/TURN messages (control plane)
- ChannelData frames (data plane, client → server)
`niom-turn` therefore parses the stream in a loop as “next frame” (see `try_pop_next_frame(...)` in [src/turn_stream.rs](../src/turn_stream.rs)):
### STUN Message
- Header is 20 bytes
- The Length field is the body length
- Total length: $20 + length$
### ChannelData Frame
- 4-byte header: `CHANNEL-NUMBER` (2) + `LENGTH` (2)
- Channel numbers are in the range `0x4000..=0x7FFF` (top bits `01`)
- Total length: $4 + length$
Important: for TCP/TLS, **no padding** may remain as “extra bytes” in the stream. Therefore `niom-turn` builds ChannelData as exactly `4 + len` bytes (see [src/stun.rs](../src/stun.rs)).
### Hardening: resync & limits
Because TCP/TLS is a byte stream, broken or malicious clients can otherwise easily “desynchronise” the parser.
`niom-turn` therefore implements in the stream parser:
- **Magic cookie check** for STUN: invalid cookies trigger byte-wise resync (instead of waiting for huge lengths).
- **Frame size limits** (STUN body and ChannelData) to limit memory/DoS risk.
- **Max buffer limit** per connection: if the input buffer grows too large, the connection is closed.
## Data flow (TCP/TLS)
1. Client connects over TCP or TLS.
2. The stream handler reads frames:
- STUN/TURN requests → processed like the UDP path (auth, allocation, permission, channel bind, send, refresh)
- ChannelData (client→peer) → forwarded to the peer via the UDP relay
3. Peer sends UDP to the relay address.
4. The relay loop forwards bytes to the `ClientSink`:
- for streams: `tx.send(bytes)` → a writer task writes Data Indication or ChannelData back onto the same stream
## Limitations / not implemented
- No TCP relay to the peer (TURN TCP allocations / CONNECT methods like RFC6062).
- Focus is: client↔server transport over TCP/TLS + UDP relay.
- Full IPv6 operational coverage is not the focus of the MVP.
## Tests
- TCP Stream Data-Plane: `tests/tcp_turn.rs`
- TLS Stream Data-Plane: `tests/tls_data_plane.rs`
- Gemeinsames Framing (STUN + ChannelData): `tests/support/stream.rs`
Wenn du willst, kann ich als nächsten Schritt die Doku um eine kurze Interop-Checkliste (WebRTC/ICE Verhalten, Candidate-Types, typische Fehlerbilder) ergänzen.