niom-turn/docs/turn_end_to_end_flow.md

5.8 KiB

End-to-End TURN Flow (UDP + TLS)

Dieses Dokument beschreibt den konkret implementierten End-to-End Ablauf in niom-turn anhand des aktuellen Codes (MVP):

Begriffe

  • Client: TURN-Client (z.B. WebRTC ICE Agent)
  • Server: niom-turn (dieses Projekt)
  • Peer: Gegenstelle, zu der relayed werden soll (typischerweise anderer WebRTC-Endpunkt)
  • Allocation: Server-seitige Sitzung, die ein Relay-UDP-Socket bereitstellt
  • Permission: Erlaubnis, zu einem Peer zu senden/Peer-Pakete zu akzeptieren
  • Channel Binding: Zuordnung channel-number -> peer, um ChannelData nutzen zu können

UDP: Sequenzgrafik (Happy Path)

sequenceDiagram
    autonumber
    participant C as Client (TURN)
    participant S as niom-turn (UDP:3478)
    participant R as Relay Socket (UDP:ephemeral)
    participant P as Peer

    Note over C,S: 1) Allocate ohne Auth → 401 Challenge
    C->>S: STUN Allocate Request (ohne MI)
    S->>C: STUN Error Response 401 + REALM + NONCE

    Note over C,S: 2) Allocate mit Long-Term Auth
    C->>S: STUN Allocate Request + USERNAME/REALM/NONCE + MESSAGE-INTEGRITY
    S->>R: bind("0.0.0.0:0"), spawn Relay-Loop
    S->>C: Allocate Success + XOR-RELAYED-ADDRESS + LIFETIME (+ MESSAGE-INTEGRITY + FINGERPRINT)

    Note over C,S: 3) CreatePermission (Pflicht vor Send/ChannelBind)
    C->>S: CreatePermission + XOR-PEER-ADDRESS (+ Auth + MI)
    S->>C: Success (200) (+ MESSAGE-INTEGRITY + FINGERPRINT)

    Note over C,S: 4) Send (Client→Peer via Relay)
    C->>S: Send + XOR-PEER-ADDRESS + DATA (+ Auth + MI)
    S->>R: relay.send_to(DATA, Peer)
    R->>P: UDP payload (source = relay_addr)

    Note over P,C: 5) Rückweg (Peer→Client)
    P->>R: UDP payload (dest = relay_addr)
    alt Channel Binding existiert
        R->>S: recv_from(Peer)
        S->>C: ChannelData(channel, payload)
    else Kein Channel Binding
        R->>S: recv_from(Peer)
        S->>C: Data Indication (METHOD_DATA|INDICATION) + XOR-PEER-ADDRESS + DATA
    end

    Note over C,S: 6) Optional: ChannelBind + ChannelData
    C->>S: ChannelBind + CHANNEL-NUMBER + XOR-PEER-ADDRESS (+ Auth + MI)
    S->>C: Success (200) (+ MESSAGE-INTEGRITY + FINGERPRINT)
    C->>S: ChannelData(channel, payload)
    S->>R: relay.send_to(payload, Peer)
    R->>P: UDP payload

    Note over C,S: 7) Refresh
    C->>S: Refresh + LIFETIME (+ Auth + MI)
    S->>C: Success + LIFETIME(applied) (+ MESSAGE-INTEGRITY + FINGERPRINT)

Was der Server dabei genau macht

  • Eingangspunkt: udp_reader_loop in src/server.rs
  • Frühe Abzweigung: Wenn parse_channel_data(...) erfolgreich ist, wird kein STUN geparst, sondern ChannelData direkt weitergeleitet (nur wenn Allocation+Binding+Permission passt).
  • STUN/TURN Requests werden mit parse_message(...) in src/stun.rs geparst.

RFC-Interop Hinweis: FINGERPRINT

  • Alle vom Server gebauten STUN-Nachrichten enthalten FINGERPRINT als letztes Attribut.
  • Wenn ein Client FINGERPRINT mitsendet, wird es validiert; bei ungültigem FINGERPRINT wird die Nachricht verworfen (kein Response).

Auth-Entscheidungen und typische Error-Codes

Die Auth-Policy ist zentral in AuthManager::authenticate in src/auth.rs.

  • 401 Unauthorized: Wenn MESSAGE-INTEGRITY fehlt → Challenge mit REALM + NONCE.
  • 438 Stale Nonce: Wenn NONCE abgelaufen/ungültig ist → neue Challenge.
  • 437 Allocation Mismatch: Wenn CreatePermission/Send/ChannelBind/Refresh ohne Allocation kommt.
  • 403 Peer Not Permitted: Wenn ein Peer nicht (mehr) permitted ist.
  • 400 Missing/Invalid ...: Wenn Attribute fehlen oder XOR-PEER-ADDRESS nicht dekodierbar ist.

TLS (turns): Sequenzgrafik (Control-Plane)

Wichtig: Die TLS-Implementierung in src/tls.rs nutzt denselben TURN-Handler wie TCP und implementiert eine echte Stream-Data-Plane.

sequenceDiagram
    autonumber
    participant C as Client (turns)
    participant T as niom-turn (TLS:5349)
    participant R as Relay Socket (UDP:ephemeral)
    participant P as Peer

    Note over C,T: STUN framing über TCP/TLS: read → chunk by (len+20)
    C->>T: STUN Allocate (ohne MI)
    T->>C: 401 + REALM + NONCE (über TLS)

    C->>T: STUN Allocate + Auth + MI
    T->>R: allocate_for(peer, stream-sink)
    T->>C: Allocate Success + XOR-RELAYED-ADDRESS + LIFETIME (über TLS)

    C->>T: CreatePermission/Send/Refresh/ChannelBind (über TLS)
    T->>C: Success/Error (über TLS)

    Note over P,C: Peer-Daten kommen über den TLS-Stream zurück
    P->>R: UDP payload an relay_addr
    R->>T: recv_from(Peer)
    T->>C: Data Indication / ChannelData (über TLS)

Konsequenz

  • Control-Plane über TLS funktioniert (Allocate/Refresh/… werden über TLS beantwortet).
  • Der Data-Plane Rückweg (Peer → Client) läuft ebenfalls über den TLS-Stream (Relay → ClientSink::Stream).

Mehr Details dazu: docs/tcp_tls_data_plane.md


Mini-Checkliste: Minimaler Ablauf (praktisch)

  1. ALLOCATE ohne MI → 401 + REALM + NONCE
  2. ALLOCATE mit USERNAME/REALM/NONCE + MESSAGE-INTEGRITYXOR-RELAYED-ADDRESS + LIFETIME
  3. CREATE_PERMISSION für Peer → 200
  4. SEND mit DATA → Server sendet via Relay an Peer
  5. Peer sendet zurück an Relay → Server liefert an Client als DATA-INDICATION oder CHANNEL-DATA
  6. Optional CHANNEL_BIND + ChannelData für effizientere Data-Plane
  7. REFRESH zum Verlängern oder LIFETIME=0 zum Freigeben