niom-turn/docs/turn_end_to_end_flow.md

140 lines
5.8 KiB
Markdown

# 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/server.rs), [src/alloc.rs](../src/alloc.rs), [src/stun.rs](../src/stun.rs), [src/auth.rs](../src/auth.rs)
- TLS-Control-Plane ("turns") mit STUN-Framing: [src/tls.rs](../src/tls.rs)
- Test-Builder (wie Requests gebaut werden): [tests/support/stun_builders.rs](../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)
```mermaid
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](../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](../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](../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](../src/tls.rs) nutzt denselben TURN-Handler wie TCP und implementiert eine echte **Stream-Data-Plane**.
```mermaid
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](tcp_tls_data_plane.md)
---
## Mini-Checkliste: Minimaler Ablauf (praktisch)
1. `ALLOCATE` ohne MI → `401` + `REALM` + `NONCE`
2. `ALLOCATE` mit `USERNAME/REALM/NONCE` + `MESSAGE-INTEGRITY``XOR-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