140 lines
5.8 KiB
Markdown
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
|