5.8 KiB
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):
- UDP-Control-Plane und UDP-Data-Plane: src/server.rs, src/alloc.rs, src/stun.rs, src/auth.rs
- TLS-Control-Plane ("turns") mit STUN-Framing: src/tls.rs
- Test-Builder (wie Requests gebaut werden): tests/support/stun_builders.rs
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_loopin 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
FINGERPRINTals letztes Attribut. - Wenn ein Client
FINGERPRINTmitsendet, wird es validiert; bei ungültigemFINGERPRINTwird 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-INTEGRITYfehlt → Challenge mitREALM+NONCE. - 438 Stale Nonce: Wenn
NONCEabgelaufen/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)
ALLOCATEohne MI →401+REALM+NONCEALLOCATEmitUSERNAME/REALM/NONCE+MESSAGE-INTEGRITY→XOR-RELAYED-ADDRESS+LIFETIMECREATE_PERMISSIONfür Peer →200SENDmitDATA→ Server sendet via Relay an Peer- Peer sendet zurück an Relay → Server liefert an Client als
DATA-INDICATIONoderCHANNEL-DATA - Optional
CHANNEL_BIND+ ChannelData für effizientere Data-Plane REFRESHzum Verlängern oderLIFETIME=0zum Freigeben