niom-turn/docs/tcp_tls_data_plane.md

4.6 KiB

TCP/TLS Data-Plane (TURN over TCP/TLS) in niom-turn

Dieses Dokument erklärt wofür eine TCP/TLS Data-Plane bei TURN gebraucht wird und wie niom-turn sie (aktuell) implementiert.

Wofür wird das benötigt?

TURN hat zwei Arten von Traffic:

  • Control-Plane: STUN/TURN Requests/Responses (Allocate, CreatePermission, ChannelBind, Refresh, …)
  • Data-Plane: Nutzdaten zwischen Client und Peer, die über das Relay laufen (Send/Data-Indication oder ChannelData)

In vielen realen Netzen ist UDP eingeschränkt oder komplett blockiert (Corporate WLAN, Mobilfunk-APNs, Captive Portals, Proxy-Umgebungen). WebRTC/ICE versucht deshalb typischerweise:

  1. UDP (schnell, bevorzugt)
  2. TURN über TCP
  3. TURN über TLS ("turns:") als letzte, aber oft funktionierende Option

Damit TURN über TCP/TLS wirklich nutzbar ist, muss nicht nur die Control-Plane über den Stream laufen, sondern auch der Rückweg Peer → Client (Data-Plane) über dieselbe TCP/TLS Verbindung beim Client ankommen.

Was implementiert niom-turn konkret?

  • Client ↔ Server Transport kann UDP, TCP oder TLS sein.
  • Das Relay zum Peer ist weiterhin UDP (klassisches TURN UDP-Relay).
  • Bei TCP/TLS liefert der Server die Data-Plane zurück an den Client über den Stream (statt über UDP an die Client-Adresse zu senden).

Das entspricht dem üblichen WebRTC-Fallback: "Client-Server über TCP/TLS, Peer-Transport über UDP".

Architektur im Code

Schlüsselidee: ClientSink

Damit der Relay-Loop Peer-Pakete an unterschiedliche Client-Transporte schicken kann, gibt es in src/alloc.rs eine Abstraktion:

  • ClientSink::Udp { sock, addr } → sendet Peer-Daten per udp.send_to(..., addr)
  • ClientSink::Stream { tx } → queued Bytes in einen Writer-Task, der auf den TCP/TLS Stream schreibt

Wenn ein Client über TCP/TLS allocatet, wird die Allocation mit einem ClientSink::Stream erzeugt.

Framing: STUN vs. ChannelData auf einem Byte-Stream

Auf UDP bekommt man Datagramme; auf TCP/TLS bekommt man einen kontinuierlichen Byte-Stream. TURN over TCP/TLS multiplexed:

  • STUN/TURN Messages (Control-Plane)
  • ChannelData Frames (Data-Plane, Client → Server)

niom-turn parst daher im Stream in einer Schleife "nächstes Frame" (siehe try_pop_next_frame(...) in src/turn_stream.rs):

STUN Message

  • Header ist 20 Bytes
  • Length-Feld ist die Body-Länge
  • Gesamtlänge ist: 20 + length

ChannelData Frame

  • 4 Byte Header: CHANNEL-NUMBER (2) + LENGTH (2)
  • Channel-Nummern liegen im Bereich 0x4000..=0x7FFF (Top-Bits 01)
  • Gesamtlänge ist: 4 + length

Wichtig: Bei TCP/TLS darf kein Padding als "separate Bytes" im Stream verbleiben. Deshalb baut niom-turn ChannelData als exakt 4 + len Bytes (siehe src/stun.rs).

Hardening: Resync & Limits

Da TCP/TLS ein Byte-Stream ist, können kaputte oder bösartige Clients den Parser sonst leicht „desynchronisieren“. niom-turn implementiert daher im Stream-Parser:

  • Magic-Cookie Check für STUN: Ungültige Cookies führen zu einem Byte-weisen Resync (statt auf riesige Längen zu warten).
  • Frame-Size Limits (STUN-Body und ChannelData), um Speicher-/DoS-Risiken zu begrenzen.
  • Max Buffer Limit pro Verbindung: wenn der Eingangspuffer zu groß wird, wird die Verbindung geschlossen.

Datenfluss (TCP/TLS)

  1. Client verbindet sich per TCP oder TLS.
  2. Der Stream-Handler liest Frames:
    • STUN/TURN Requests → verarbeitet wie UDP-Pfad (Auth, Allocation, Permission, ChannelBind, Send, Refresh)
    • ChannelData (Client→Peer) → wird über das UDP-Relay an den Peer geschickt
  3. Peer sendet UDP an die Relay-Adresse.
  4. Relay-Loop leitet die Bytes an den ClientSink weiter:
    • bei Stream: tx.send(bytes) → Writer-Task schreibt Data-Indication oder ChannelData zurück auf denselben Stream

Grenzen / Noch nicht implementiert

  • Kein TCP-Relay zum Peer (TURN TCP allocations / CONNECT-Methoden wie in RFC6062).
  • Fokus liegt auf: Client-Server Transport über TCP/TLS + UDP-Relay.
  • IPv6 ist im aktuellen MVP noch nicht vollständig umgesetzt.

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.