RFC 5766
Traversal Using Relays around NAT (TURN): Relay Extensions to Session Traversal Utilities for NAT (STUN)
RFC 5766: TURN, the relay extensions to STUN ๐ฆ
Title: Traversal Using Relays around NAT (TURN): Relay Extensions to Session Traversal Utilities for NAT (STUN)
- Request for Comments: 5766 ๐
- Category: Standards Track
- Date: April 2010
- Status: ISSN 2070-1721
- Authors: R. Mahy (Unaffiliated), P. Matthews (Alcatel-Lucent), J. Rosenberg (jdrosen.net)
- Publisher: Internet Engineering Task Force (IETF) ๐
- Obsoletes and updates: this title block has no Obsoletes or Updates line, so there's nothing to report here โจ
Abstract ๐ธ
okay so picture this: you're a host hiding behind a NAT, and sometimes that NAT means you literally cannot talk directly to the other hosts (peers) you want to reach ๐ญ in those moments you need help from a middleman node that acts as a communication relay. this spec defines a protocol called TURN (Traversal Using Relays around NAT) ๐ which lets your host control how the relay works and send packets to its peers through it.
what makes TURN iconic is that it lets one client talk to multiple peers using a single relay address. that's not something every relay control protocol can do, so it's a whole main character moment ๐
TURN was built to be part of the ICE (Interactive Connectivity Establishment) approach to NAT traversal ๐ซถ but it can also be used without ICE, which is lowkey so useful.
Status of This Memo ๐
this is an Internet Standards Track document โจ
it's a product of the Internet Engineering Task Force (IETF) ๐ and it represents the consensus of the IETF community. it got public review and was approved for publication by the Internet Engineering Steering Group (IESG). if you want more on Internet Standards, check out Section 2 of RFC 5741 ๐
for the latest on this doc's status, any errata, or how to send feedback, go to http://www.rfc-editor.org/info/rfc5766 ๐ฅ
Copyright Notice ๐
Copyright (c) 2010 IETF Trust and the persons identified as the document authors. All rights reserved. ๐
this document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they stand on the date this document was published. please read these carefully, because they spell out your rights and restrictions for this document ๐ซถ
if you pull Code Components out of this document, they must include the Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. they come without warranty, as described in the Simplified BSD License ๐
Table of Contents ๐ง
here's the whole roadmap, copied exactly so you can find every section (the numbers matter, since the text points to them ๐):
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4
2. Overview of Operation . . . . . . . . . . . . . . . . . . . . 5
2.1. Transports . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2. Allocations . . . . . . . . . . . . . . . . . . . . . . . 9
2.3. Permissions . . . . . . . . . . . . . . . . . . . . . . . 11
2.4. Send Mechanism . . . . . . . . . . . . . . . . . . . . . . 12
2.5. Channels . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.6. Unprivileged TURN Servers . . . . . . . . . . . . . . . . 15
2.7. Avoiding IP Fragmentation . . . . . . . . . . . . . . . . 16
2.8. RTP Support . . . . . . . . . . . . . . . . . . . . . . . 17
2.9. Anycast Discovery of Servers . . . . . . . . . . . . . . . 17
3. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 18
4. General Behavior . . . . . . . . . . . . . . . . . . . . . . . 19
5. Allocations . . . . . . . . . . . . . . . . . . . . . . . . . 22
6. Creating an Allocation . . . . . . . . . . . . . . . . . . . . 23
6.1. Sending an Allocate Request . . . . . . . . . . . . . . . 23
6.2. Receiving an Allocate Request . . . . . . . . . . . . . . 24
6.3. Receiving an Allocate Success Response . . . . . . . . . . 28
6.4. Receiving an Allocate Error Response . . . . . . . . . . . 29
7. Refreshing an Allocation . . . . . . . . . . . . . . . . . . . 31
7.1. Sending a Refresh Request . . . . . . . . . . . . . . . . 31
7.2. Receiving a Refresh Request . . . . . . . . . . . . . . . 31
7.3. Receiving a Refresh Response . . . . . . . . . . . . . . . 32
8. Permissions . . . . . . . . . . . . . . . . . . . . . . . . . 32
9. CreatePermission . . . . . . . . . . . . . . . . . . . . . . . 34
9.1. Forming a CreatePermission Request . . . . . . . . . . . . 34
9.2. Receiving a CreatePermission Request . . . . . . . . . . . 34
9.3. Receiving a CreatePermission Response . . . . . . . . . . 35
10. Send and Data Methods . . . . . . . . . . . . . . . . . . . . 35
10.1. Forming a Send Indication . . . . . . . . . . . . . . . . 35
10.2. Receiving a Send Indication . . . . . . . . . . . . . . . 35
10.3. Receiving a UDP Datagram . . . . . . . . . . . . . . . . . 36
10.4. Receiving a Data Indication . . . . . . . . . . . . . . . 37
11. Channels . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
11.1. Sending a ChannelBind Request . . . . . . . . . . . . . . 39
11.2. Receiving a ChannelBind Request . . . . . . . . . . . . . 39
11.3. Receiving a ChannelBind Response . . . . . . . . . . . . . 40
11.4. The ChannelData Message . . . . . . . . . . . . . . . . . 41
11.5. Sending a ChannelData Message . . . . . . . . . . . . . . 41
11.6. Receiving a ChannelData Message . . . . . . . . . . . . . 42
11.7. Relaying Data from the Peer . . . . . . . . . . . . . . . 43
12. IP Header Fields . . . . . . . . . . . . . . . . . . . . . . . 43
13. New STUN Methods . . . . . . . . . . . . . . . . . . . . . . . 45
14. New STUN Attributes . . . . . . . . . . . . . . . . . . . . . 45
14.1. CHANNEL-NUMBER . . . . . . . . . . . . . . . . . . . . . . 45
14.2. LIFETIME . . . . . . . . . . . . . . . . . . . . . . . . . 46
14.3. XOR-PEER-ADDRESS . . . . . . . . . . . . . . . . . . . . . 46
14.4. DATA . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
14.5. XOR-RELAYED-ADDRESS . . . . . . . . . . . . . . . . . . . 46
14.6. EVEN-PORT . . . . . . . . . . . . . . . . . . . . . . . . 46
14.7. REQUESTED-TRANSPORT . . . . . . . . . . . . . . . . . . . 47
14.8. DONT-FRAGMENT . . . . . . . . . . . . . . . . . . . . . . 47
14.9. RESERVATION-TOKEN . . . . . . . . . . . . . . . . . . . . 48
15. New STUN Error Response Codes . . . . . . . . . . . . . . . . 48
16. Detailed Example . . . . . . . . . . . . . . . . . . . . . . . 48
17. Security Considerations . . . . . . . . . . . . . . . . . . . 55
17.1. Outsider Attacks . . . . . . . . . . . . . . . . . . . . . 55
17.1.1. Obtaining Unauthorized Allocations . . . . . . . . . 55
17.1.2. Offline Dictionary Attacks . . . . . . . . . . . . . 56
17.1.3. Faked Refreshes and Permissions . . . . . . . . . . . 56
17.1.4. Fake Data . . . . . . . . . . . . . . . . . . . . . . 56
17.1.5. Impersonating a Server . . . . . . . . . . . . . . . 57
17.1.6. Eavesdropping Traffic . . . . . . . . . . . . . . . . 58
17.1.7. TURN Loop Attack . . . . . . . . . . . . . . . . . . 58
17.2. Firewall Considerations . . . . . . . . . . . . . . . . . 59
17.2.1. Faked Permissions . . . . . . . . . . . . . . . . . . 59
17.2.2. Blacklisted IP Addresses . . . . . . . . . . . . . . 60
17.2.3. Running Servers on Well-Known Ports . . . . . . . . . 60
17.3. Insider Attacks . . . . . . . . . . . . . . . . . . . . . 60
17.3.1. DoS against TURN Server . . . . . . . . . . . . . . . 60
17.3.2. Anonymous Relaying of Malicious Traffic . . . . . . . 61
17.3.3. Manipulating Other Allocations . . . . . . . . . . . 61
17.4. Other Considerations . . . . . . . . . . . . . . . . . . . 61
18. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 61
19. IAB Considerations . . . . . . . . . . . . . . . . . . . . . . 62
20. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 63
21. References . . . . . . . . . . . . . . . . . . . . . . . . . . 64
21.1. Normative References . . . . . . . . . . . . . . . . . . . 64
21.2. Informative References . . . . . . . . . . . . . . . . . . 64
Part 1 of 17 done ๐ more parts coming, so stay tuned bestie ๐ฆ
1. Introduction โจ
okay so picture this ๐ a host sitting behind a NAT wants to chat directly with other hosts, and some of those are also behind NATs ๐ญ the hosts can try "hole punching" techniques (see [RFC5128]) to find a direct path, meaning a route from one host to the other through the NATs and routers in between that doesn't touch any relays ๐ฆ
but ngl, hole punching is picky. As described in [RFC5128] and [RFC4787], it will fail if both hosts are behind NATs that aren't well behaved ๐ for example, if both hosts are behind NATs with a mapping behavior of "address-dependent mapping" or "address- and port-dependent mapping", hole punching generally fails. not me getting rejected by my own NAT ๐
so when a direct path can't be found, you need an intermediate host to act as a relay for the packets ๐ง this relay usually lives out on the public Internet and passes packets between two hosts that are both stuck behind NATs. main character relay energy ๐
this spec defines a protocol called TURN ๐ it lets a host behind a NAT (called the TURN client) ask another host (called the TURN server) to act as a relay. the client can set things up so the server relays packets to and from certain other hosts (called peers), and it can control how that relaying actually works โจ the client gets this going by obtaining an IP address and port on the server, called the relayed transport address ๐ธ when a peer sends a packet to that relayed transport address, the server forwards it to the client. when the client sends a data packet to the server, the server relays it to the right peer, using the relayed transport address as the source ๐
a client using TURN must have some way to tell its peers about the relayed transport address, and to learn each peer's IP address and port (more precisely, each peer's server-reflexive transport address, see Section 2) ๐ซถ how that happens is out of scope for the TURN protocol. one option is for the client and peers to swap email messages ๐ another is for them to use a special-purpose "introduction" or "rendezvous" protocol (see [RFC5128] for more details) ๐
if TURN is used with ICE [RFC5245], then the relayed transport address and the peers' IP addresses and ports go into the ICE candidate information that the rendezvous protocol has to carry ๐ฅ for example, if TURN and ICE are part of a multimedia solution using SIP [RFC3261], then SIP is the rendezvous protocol, carrying the ICE candidate info inside the body of its messages ๐ if you're using TURN and ICE with some other rendezvous protocol, [MMUSIC-ICE-NONSIP] has guidance on what that protocol needs to handle ๐
okay real talk, a TURN server is expensive to run ๐ญ it's very likely to get two NATted hosts talking, but the provider usually needs a high-bandwidth Internet connection for it. so it's best to only use a TURN server when a direct path can't be found ๐ซ when the client and a peer use ICE to pick their path, ICE tries hole punching first to look for a direct route, and only falls back to a TURN server if that doesn't work ๐ฆโจ
TURN was originally made to support multimedia sessions signaled with SIP ๐ since SIP supports forking, TURN supports multiple peers per relayed transport address, which other approaches (e.g., SOCKS [RFC1928]) don't do ๐ that said, care was taken to make sure TURN works for other kinds of applications too ๐ง
TURN was designed as one piece of the bigger ICE approach to NAT traversal ๐ implementors are strongly urged to look into ICE and seriously consider using it for their application ๐ฅ but it's totally possible to use TURN without ICE ๐ธ
TURN is an extension of the STUN (Session Traversal Utilities for NAT) protocol [RFC5389] ๐ซถ most TURN messages are STUN-formatted messages, though not all of them ๐ if you're reading this doc, you should already be familiar with STUN ๐
2. Overview of Operation โจ
this section gives a quick overview of how TURN works ๐ฆ it is non-normative, so think of it as the vibe check, not the rulebook ๐
in a typical setup, a TURN client is connected to a private network [RFC1918] and, through one or more NATs, to the public Internet ๐ธ out on the public Internet sits a TURN server ๐ง somewhere else on the Internet are one or more peers the TURN client wants to talk to ๐ those peers may or may not be behind NATs too ๐ the client uses the server as a relay to send packets to these peers and receive packets from them โจ
Peer A
Server-Reflexive +---------+
Transport Address | |
192.0.2.150:32102 | |
| /| |
TURN | / ^| Peer A |
Client's Server | / || |
Host Transport Transport | // || |
Address Address | // |+---------+
10.1.1.2:49721 192.0.2.15:3478 |+-+ // Peer A
| | ||N| / Host Transport
| +-+ | ||A|/ Address
| | | | v|T| 192.168.100.2:49582
| | | | /+-+
+---------+| | | |+---------+ / +---------+
| || |N| || | // | |
| TURN |v | | v| TURN |/ | |
| Client |----|A|----------| Server |------------------| Peer B |
| | | |^ | |^ ^| |
| | |T|| | || || |
+---------+ | || +---------+| |+---------+
| || | |
| || | |
+-+| | |
| | |
| | |
Client's | Peer B
Server-Reflexive Relayed Transport
Transport Address Transport Address Address
192.0.2.1:7000 192.0.2.15:50000 192.0.2.210:49191
Figure 1
Figure 1 shows a typical deployment ๐ here the TURN client and TURN server are separated by a NAT, with the client on the private side and the server on the public side ๐ง this NAT is assumed to be a "bad" NAT, for example one with a mapping property of "address-and-port-dependent mapping" (see [RFC4787]) ๐
the client talks to the server from a (IP address, port) combo called the client's HOST TRANSPORT ADDRESS ๐ฆ (an IP address plus a port together is called a TRANSPORT ADDRESS ๐)
the client sends TURN messages from its host transport address to a transport address on the TURN server called the TURN SERVER TRANSPORT ADDRESS ๐ธ the client learns that address through some unspecified means (e.g., configuration), and it's usually shared by lots of clients at once ๐ซถ
since the client is behind a NAT, the server sees the client's packets as coming from a transport address on the NAT itself ๐ this is called the client's SERVER-REFLEXIVE transport address ๐ packets the server sends to that server-reflexive address get forwarded by the NAT to the client's host transport address ๐
the client uses TURN commands to create and manage an ALLOCATION on the server โจ an allocation is a data structure living on the server ๐ง it holds, among other things, the RELAYED TRANSPORT ADDRESS for that allocation ๐ the relayed transport address is the transport address on the server that peers use to have the server relay data to the client ๐ an allocation is uniquely identified by its relayed transport address ๐ฅ
once an allocation exists, the client can send application data to the server along with a note about which peer it's meant for, and the server relays it to that peer ๐ the data rides inside a TURN message; when it reaches the server, it's pulled out and sent to the peer in a UDP datagram ๐ going the other way, a peer sends application data in a UDP datagram to the allocation's relayed transport address ๐ญ the server wraps that data in a TURN message, sends it to the client, and includes which peer sent it ๐ since every TURN message says which peer the client is talking to, one allocation can handle multiple peers at once ๐
when the peer is behind a NAT, the client has to identify it by its server-reflexive transport address, not its host transport address ๐ฆ for example, to send application data to Peer A in the diagram above, the client must specify 192.0.2.150:32102 (Peer A's server-reflexive transport address), not 192.168.100.2:49582 (Peer A's host transport address) ๐
each allocation on the server belongs to a single client and has exactly one relayed transport address that only that allocation uses ๐ซถ so when a packet shows up at a relayed transport address, the server knows exactly which client it's meant for ๐
and the client can have multiple allocations on a server at the same time ๐งโจ
2.1. Transports โจ
Okay bestie, so TURN as defined in this spec always uses UDP between the server and the peer ๐ฆ. The spec does let you pick any one of UDP, TCP, or TLS over TCP to carry TURN messages between the client and the server ๐
+----------------------------+---------------------+
| TURN client to TURN server | TURN server to peer |
+----------------------------+---------------------+
| UDP | UDP |
| TCP | UDP |
| TLS over TCP | UDP |
+----------------------------+---------------------+
If the client uses TCP or TLS-over-TCP to reach the server, the server converts between those and UDP when it relays data to and from the peer ๐
Since this version of TURN only supports UDP between the server and the peer, most clients will probably pick UDP between the client and the server too ๐ธ So you might be like, "wait, why even bother with TCP and TLS-over-TCP?" Valid question, bestie ๐ง
TURN supports TCP between the client and the server because some firewalls are set up to block UDP entirely ๐ซ These firewalls block UDP but let TCP through, partly because TCP has traits that make it obvious to the firewall what the protected node actually wants. Like, TCP has a three-way handshake that makes it clearer the protected node really wants that particular connection, whereas with UDP the firewall can only guess which flows are wanted by using filtering rules ๐คทโโ๏ธ Also, TCP has explicit connection teardown, while with UDP the firewall has to use timers to guess when a flow is done โฒ๏ธ
TURN supports TLS-over-TCP between the client and the server because TLS gives extra security properties that TURN's default digest authentication doesn't, and some clients might want those ๐ Specifically, TLS lets the client check that it's talking to the right server, and it keeps TURN control messages confidential ๐ซถ TURN doesn't require TLS, though, because TLS has more overhead than digest authentication. For example, with TLS, most application data would likely be doubly encrypted (once by TLS, and once to make sure it's still encrypted inside the UDP datagram) ๐ญ
There's a planned extension to TURN that adds TCP between the server and the peers [TURN-TCP] ๐ Because of that, allocations that use UDP between the server and the peers are called UDP allocations, and allocations that use TCP between the server and the peers are called TCP allocations. This specification only describes UDP allocations ๐
TURN, as defined in this specification, only supports IPv4. All IP addresses in this specification must be IPv4 addresses. There's a planned extension to add IPv6 support and relaying between IPv4 and IPv6 [TURN-IPv6] ๐
In some TURN applications, the client might send and receive packets other than TURN packets on the host transport address it uses to talk to the server. That can happen, for example, when TURN is used with ICE ๐ง In those cases, the client can tell TURN packets apart from everything else by checking the source address of each arriving packet: anything coming from the TURN server is a TURN packet โจ
2.2. Allocations ๐
To create an allocation on the server, the client runs an Allocate transaction. The client sends an Allocate request to the server, and the server replies with an Allocate success response that contains the allocated relayed transport address ๐ The client can include attributes in the Allocate request to describe the kind of allocation it wants (for example, the lifetime). Relaying data has security implications, so the server requires the client to authenticate itself, usually with STUN's long-term credential mechanism, to prove it's allowed to use the server ๐
Once a relayed transport address is allocated, the client has to keep the allocation alive. The client must periodically send a Refresh request to the server to do that ๐ TURN deliberately uses a different method (Refresh instead of Allocate) for refreshes, so the client finds out if the allocation disappears for some reason ๐
How often the Refresh transaction happens depends on the lifetime of the allocation. The default lifetime is 10 minutes. That number was picked to be long enough that refreshing isn't usually a burden on the client, but short enough that allocations from clients who've unexpectedly quit get cleaned up in a timely manner ๐ The client can ask for a longer lifetime in the Allocate request and may change its request in a Refresh request, but the server always tells you the actual lifetime in the response โณ The client must issue a new Refresh transaction within "lifetime" seconds of the previous Allocate or Refresh transaction. When a client no longer wants an allocation, it should delete it with a Refresh request that has a requested lifetime of 0 ๐๏ธ
Both the server and the client keep track of something called the 5-TUPLE. On the client side, the 5-tuple is made up of the client's host transport address, the server transport address, and the transport protocol the client uses to talk to the server. On the server side, it's the same except the client's host transport address gets swapped for the client's server-reflexive address, since that's the address the server actually sees ๐
Both the client and the server remember the 5-tuple from the Allocate request, and later messages between them use that same 5-tuple. That's how they both know which allocation is being talked about ๐ฆ If the client wants a second relayed transport address, it must create a second allocation using a different 5-tuple (for example, by using a different client host address or port) ๐
NOTE: While this document talks about 5-tuples, the TURN server can store whatever identifier it likes, as long as the results come out identical. Specifically, an implementation may use a file-descriptor in place of a 5-tuple to represent a TCP connection ๐ง
TURN TURN Peer Peer
client server A B
|-- Allocate request --------------->| | |
| | | |
|<--------------- Allocate failure --| | |
| (401 Unauthorized) | | |
| | | |
|-- Allocate request --------------->| | |
| | | |
|<---------- Allocate success resp --| | |
| (192.0.2.15:50000) | | |
// // // //
| | | |
|-- Refresh request ---------------->| | |
| | | |
|<----------- Refresh success resp --| | |
| | | |
Figure 2
In Figure 2, the client sends an Allocate request to the server with no credentials ๐ The server requires every request to be authenticated with STUN's long-term credential mechanism, so it rejects the request with a 401 (Unauthorized) error code. The client tries again, this time including credentials (not shown). This time the server accepts the Allocate request and returns an Allocate success response with (among other things) the relayed transport address assigned to the allocation ๐ Later, the client decides to refresh the allocation and sends a Refresh request. The refresh is accepted, and the server replies with a Refresh success response ๐
2.3. Permissions ๐ฅ
To ease the worries of enterprise IT admins that TURN could be used to sneak past corporate firewalls, TURN includes the idea of permissions ๐ก๏ธ TURN permissions copy the address-restricted filtering behaviour of NATs that comply with [RFC4787] ๐
An allocation can have zero or more permissions. Each permission is made of an IP address and a lifetime. When the server gets a UDP datagram on the allocation's relayed transport address, it first checks the list of permissions. If the datagram's source IP address matches a permission, the application data is relayed to the client. Otherwise, the UDP datagram is silently discarded ๐ช
A permission expires after 5 minutes if it isn't refreshed, and there's no way to explicitly delete one. This was chosen to match how a NAT that complies with [RFC4787] behaves ๐ง
The client can install or refresh a permission using either a CreatePermission request or a ChannelBind request. With CreatePermission, you can install or refresh multiple permissions in a single request, which matters a lot for applications that use ICE ๐ซถ For security reasons, permissions can only be installed or refreshed by transactions that can be authenticated. So Send indications and ChannelData messages (the ones used to send data to peers) do not install or refresh any permissions ๐
Permissions live within the context of an allocation, so adding or expiring a permission in one allocation doesn't affect any other allocations ๐ฆ
2.4. Send Mechanism โจ
okay so there are two ways the client and its peers can swap application data through the TURN server ๐ The first one uses the Send and Data methods, and the second one uses channels ๐ฆ Both ways let the client chat with multiple peers over one single allocated relayed transport address, so both need a way for the client to tell the server which peer should get the data, and for the server to tell the client which peer sent it ๐ซถ
The Send mechanism uses Send and Data indications ๐จ Send indications carry application data from the client up to the server, and Data indications carry it from the server back down to the client ๐
When the client is using the Send mechanism, it sends a Send indication to the TURN server that contains (a) an XOR-PEER-ADDRESS attribute with the (server-reflexive) transport address of the peer, and (b) a DATA attribute holding the application data ๐ When the server gets that Send indication, it pulls the application data out of the DATA attribute and sends it off in a UDP datagram to the peer, using the allocated relay address as the source address ๐ Also, you don't need to specify the relayed transport address, because it's already implied by the 5-tuple used for the Send indication ๐ง
Going the other way, UDP datagrams that arrive at the relayed transport address on the TURN server get turned into Data indications and sent to the client ๐ธ The server-reflexive transport address of the peer goes in an XOR-PEER-ADDRESS attribute, and the data itself goes in a DATA attribute ๐ Since the relayed transport address uniquely identified the allocation, the server knows exactly which client should receive the data ๐
โ ๏ธ Send and Data indications cannot be authenticated, because the long-term credential mechanism of STUN doesn't support authenticating indications. Lowkey this is not as big a deal as it sounds, since the client-to-server leg is only half of the total path to the peer ๐ Applications that want proper security should encrypt the data sent between the client and a peer ๐
Because Send indications aren't authenticated, an attacker can send bogus Send indications to the server, which will then relay them to a peer ๐ To partly mitigate this attack, TURN requires that the client install a permission towards a peer before sending data to it using a Send indication ๐ก๏ธ
TURN TURN Peer Peer
client server A B
| | | |
|-- CreatePermission req (Peer A) -->| | |
|<-- CreatePermission success resp --| | |
| | | |
|--- Send ind (Peer A)-------------->| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<-------------- Data ind (Peer A) --| | |
| | | |
| | | |
|--- Send ind (Peer B)-------------->| | |
| | dropped | |
| | | |
| |<== data ==================|
| dropped | | |
| | | |
Figure 3
So in Figure 3, the client already has an allocation and now wants to send data to its peers ๐ฌ First it makes a permission by sending the server a CreatePermission request with Peer A's (server-reflexive) IP address in the XOR-PEER-ADDRESS attribute. If that didn't happen, the server wouldn't relay data between the client and the server, so it's a must-do step ๐
โโ๏ธ Then the client sends data to Peer A with a Send indication, and the server pulls out the application data and forwards it in a UDP datagram to Peer A, using the relayed transport address as the source transport address ๐ When a UDP datagram from Peer A shows up at the relayed transport address, its contents get placed into a Data indication and forwarded to the client ๐ฆ Later on, the client tries to chat with Peer B. But no permission has been installed for Peer B, so the server drops both the Send indication from the client and the UDP datagram from the peer ๐ญ Not the vibe!
2.5. Channels ๐
For some apps (like Voice over IP ๐), the 36 bytes of overhead that a Send indication or Data indication adds to the application data can really pump up the bandwidth needed between the client and the server ๐ฉ To fix that, TURN gives us a second way for the client and server to link data to a specific peer ๐ซถ
This second way uses an alternate packet format called the ChannelData message ๐ The ChannelData message doesn't use the STUN header that other TURN messages use. Instead, it has a 4-byte header that includes a number called a channel number ๐ข Each channel number in use is bound to one specific peer, so it acts as a shorthand for that peer's host transport address โจ
To bind a channel to a peer, the client sends a ChannelBind request to the server and includes an unbound channel number and the transport address of the peer ๐ Once the channel is bound, the client can use a ChannelData message to send the server data meant for that peer ๐ And the server can relay data from that peer back toward the client using a ChannelData message too ๐
Channel bindings last for 10 minutes unless they get refreshed. This lifetime was picked to be longer than the permission lifetime ๐ฐ๏ธ To refresh a channel binding, the client sends another ChannelBind request that rebinds the channel to the peer ๐ Like permissions (but unlike allocations), there's no way to explicitly delete a channel binding. The client just has to wait for it to time out ๐ค
TURN TURN Peer Peer
client server A B
| | | |
|-- ChannelBind req ---------------->| | |
| (Peer A to 0x4001) | | |
| | | |
|<---------- ChannelBind succ resp --| | |
| | | |
|-- [0x4001] data ------------------>| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<------------------ [0x4001] data --| | |
| | | |
|--- Send ind (Peer A)-------------->| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<------------------ [0x4001] data --| | |
| | | |
Figure 4
Figure 4 shows the channel mechanism in action ๐ฅ The client already has an allocation and now wants to bind a channel to Peer A. So it sends a ChannelBind request to the server, specifying Peer A's transport address and a channel number (0x4001) ๐ After that, the client can send application data wrapped up inside ChannelData messages to Peer A. That's the "[0x4001] data" in the figure, where 0x4001 is the channel number ๐ When the ChannelData message reaches the server, the server moves the data into a UDP datagram and sends it to Peer A, which is the peer bound to channel number 0x4001 ๐
Now the reverse direction. When Peer A sends a UDP datagram to the relayed transport address, it arrives at the server on the relayed transport address assigned to the allocation ๐ฌ Since that datagram came from Peer A, and Peer A has a channel number assigned to it, the server wraps the data in a ChannelData message when it sends it to the client ๐
Once a channel has been bound, the client is free to mix ChannelData messages and Send indications together ๐น In the figure, the client later decides to use a Send indication instead of a ChannelData message to send more data to Peer A. The client might do this, for example, so it can use the DONT-FRAGMENT attribute (see the next section) ๐งฉ However, once a channel is bound, the server will always use a ChannelData message, as shown in the call flow ๐ซ
Also, ChannelData messages can only be used for peers that the client has bound a channel to ๐ In the example above, Peer A has been bound to a channel but Peer B hasn't, so application data to and from Peer B would use the Send mechanism ๐
2.6. Unprivileged TURN Servers ๐ฆธโโ๏ธ
This version of TURN is designed so the server can be built as an application that runs in user space under commonly available operating systems, without needing special privileges ๐ท This was a deliberate choice to make a TURN server easy to deploy. For example, a TURN server can be built right into a peer-to-peer app, so one peer can offer NAT traversal services to another peer ๐ค
This design decision has the following implications for data relayed by a TURN server ๐
- The value of the Diffserv field may not be preserved across the server ๐
- The Time to Live (TTL) field may be reset, rather than decremented, across the server โณ
- The Explicit Congestion Notification (ECN) field may be reset by the server ๐ฉ๏ธ
- ICMP messages are not relayed by the server ๐
- There is no end-to-end fragmentation, since the packet is re-assembled at the server ๐งฉ
Future work may specify alternate TURN semantics that address these limitations ๐ฎ
2.7. Avoiding IP Fragmentation โจ
okay so fragmentation is the thing that can absolutely ruin your whole vibe ๐. For reasons described in [Frag-Harmful], applications, especially ones sending large volumes of data, should try hard to avoid having their packets fragmented. TCP apps can more or less ignore this because fragmentation avoidance is already a standard part of TCP ๐. But UDP apps (and therefore any app using this version of TURN) have to handle fragmentation avoidance themselves ๐ซถ.
There are two approaches the client and the peer can take to avoid IP fragmentation ๐ฆ.
The first approach is to avoid sending large amounts of application data in the TURN messages and UDP datagrams exchanged between the client and the peer. This is what most VoIP (Voice-over-IP) apps do ๐. The trick is that the IP specification [RFC0791] says IP packets up to 576 bytes should never need to be fragmented, so the app can use that as its safe zone ๐ง.
How much application data fits without fragmentation depends on the details of the TURN session between the client and the server. That includes whether UDP, TCP, or TLS transport is used, whether ChannelData messages or Send and Data indications are used, and whether any additional attributes (such as the DONT-FRAGMENT attribute) are included ๐. Another factor that's hard to figure out is whether the MTU gets reduced somewhere along the path for other reasons, like IP-in-IP tunneling ๐.
As a guideline, sending a maximum of 500 bytes of application data in a single TURN message (sent by the client on the client-to-server leg) or a UDP datagram (sent by the peer on the peer-to-server leg) will generally avoid IP fragmentation ๐. If you want to lower the chance of fragmentation even more, it is recommended that the client use ChannelData messages when transferring significant volumes of data. The reason is that the overhead of a ChannelData message is less than that of Send and Data indications ๐.
The second approach the client and peer can take is to use a path MTU discovery algorithm to find the maximum amount of application data that can be sent without fragmentation ๐ฅ.
Here's the catch: servers implementing this version of TURN do not relay ICMP messages, so the classic path MTU discovery algorithm defined in [RFC1191] can't discover the MTU of the transmission path between the client and the peer ๐ญ. (Even if servers did relay ICMP messages, the algorithm still wouldn't always work, since combined NAT/firewall devices often filter ICMP messages out ๐.)
So the client and server need to use a path MTU discovery algorithm that doesn't require ICMP messages. The Packetized Path MTU Discovery algorithm defined in [RFC4821] is one such algorithm ๐.
The details of how to use the algorithm of [RFC4821] with TURN are still under investigation. As a step towards that goal, though, this version of TURN supports a DONT-FRAGMENT attribute โจ. When the client includes this attribute in a Send indication, it tells the server to set the DF bit in the resulting UDP datagram it sends to the peer. Some servers may be unable to set the DF bit, so the client should also include this attribute in the Allocate request. Any server that doesn't support the DONT-FRAGMENT attribute will indicate this by rejecting the Allocate request ๐
โโ๏ธ.
2.8. RTP Support ๐ถ
One of the envisioned uses of TURN is as a relay for clients and peers that want to exchange real-time data (like voice or video) using RTP. To make TURN work well for this, it includes some special support for older versions of RTP ๐.
Old versions of RTP [RFC3550] required the RTP stream to be on an even port number, and the associated RTP Control Protocol (RTCP) stream, if present, to be on the next highest port ๐ธ. So that clients can work with peers that still need this, TURN lets the client ask the server to allocate a relayed transport address with an even port number. The client can also optionally ask the server to reserve the next-highest port number for a later allocation ๐ง.
2.9. Anycast Discovery of Servers ๐ก
This version of TURN has been designed so that a method of doing anycast discovery of a TURN server over UDP can be specified in the future ๐.
Specifically, a TURN server can reject an Allocate request and suggest that the client try an alternate server. To avoid certain types of attacks, the client must use the same credentials with the alternate server that it would have used with the initial server ๐.
3. Terminology ๐
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [RFC2119] ๐ .
Readers are expected to be familiar with [RFC5389] and the terms defined there ๐ค.
The following terms are used in this document:
TURN: The protocol spoken between a TURN client and a TURN server. It's an extension to the STUN protocol [RFC5389]. The protocol lets a client allocate and use a relayed transport address ๐ฆ.
TURN client: A STUN client that implements this specification โจ.
TURN server: A STUN server that implements this specification. It relays data between a TURN client and its peer(s) ๐ซถ.
Peer: A host the TURN client wants to communicate with. The TURN server relays traffic between the TURN client and its peer(s). The peer doesn't interact with the TURN server using the protocol defined in this document. Instead, the peer receives data sent by the TURN server and sends data towards the TURN server ๐ง.
Transport Address: The combination of an IP address and a port ๐.
Host Transport Address: A transport address on a client or a peer ๐ป.
Server-Reflexive Transport Address: A transport address on the "public side" of a NAT. The NAT allocates this address to correspond to a specific host transport address ๐.
Relayed Transport Address: A transport address on the TURN server that is used for relaying packets between the client and a peer. A peer sends to this address on the TURN server, and the packet is then relayed to the client ๐.
TURN Server Transport Address: A transport address on the TURN server that is used for sending TURN messages to the server. This is the transport address the client uses to communicate with the server ๐ฌ.
Peer Transport Address: The transport address of the peer as seen by the server. When the peer is behind a NAT, this is the peer's server-reflexive transport address ๐.
Allocation: The relayed transport address granted to a client through an Allocate request, along with related state such as permissions and expiration timers ๐ฐ.
5-tuple: The combination (client IP address and port, server IP address and port, and transport protocol, which is currently one of UDP, TCP, or TLS) used to communicate between the client and the server. The 5-tuple uniquely identifies this communication stream. It also uniquely identifies the Allocation on the server ๐.
Channel: A channel number and an associated peer transport address. Once a channel number is bound to a peer's transport address, the client and server can use the more bandwidth-efficient ChannelData message to exchange data ๐.
Permission: The IP address and transport protocol (but not the port) of a peer that is allowed to send traffic to the TURN server and have that traffic relayed to the TURN client. The TURN server will only forward traffic to its client from peers that match an existing permission ๐ช.
Realm: A string used to describe the server or a context within the server. The realm tells the client which username and password combination to use to authenticate requests ๐ชช.
Nonce: A string chosen at random by the server and included in the message-digest. To prevent reply attacks, the server should change the nonce regularly ๐.
4. General Behavior โจ
okay so this section is all the general TURN rules that apply to every TURN message ๐
TURN is an extension to STUN. Every TURN message is STUN-formatted except the ChannelData message ๐ฆ All the base processing rules in [RFC5389] apply to STUN-formatted messages, which means every message-forming and message-processing description in this document is implicitly prefixed with the rules of [RFC5389] ๐ซถ
[RFC5389] specifies an authentication mechanism called the long-term credential mechanism. TURN servers and clients MUST implement this mechanism. The server MUST demand that all requests from the client be authenticated using this mechanism, or that an equally strong or stronger mechanism for client authentication is used ๐
Note that the long-term credential mechanism only applies to requests and can't be used to authenticate indications, so indications in TURN are never authenticated ๐ If the server requires requests to be authenticated, then the server's administrator MUST choose a realm value that uniquely identifies the username and password combination the client must use, even if the client uses multiple servers under different administrations ๐ฐ The administrator MAY give each client a unique username, or MAY give the same username to more than one client (like everyone from the same department or company) ๐ฅ For each allocation, the server SHOULD generate a new random nonce when the allocation is first attempted, following the randomness recommendations in [RFC4086]. It SHOULD also expire the nonce at least once every hour during the lifetime of the allocation โณ
All requests after the initial Allocate must use the same username that created the allocation, so attackers can't hijack the client's allocation ๐ซ Specifically: if the server requires the long-term credential mechanism, a non-Allocate request passes authentication under it, the 5-tuple identifies an existing allocation, but the request doesn't use the same username that created the allocation, then the request MUST be rejected with a 441 (Wrong Credentials) error. Not it ๐ค
When a TURN message reaches the server from the client, the server uses the 5-tuple in the message to find the matching allocation ๐ For all TURN messages (including ChannelData) except an Allocate request, if the 5-tuple doesn't identify an existing allocation, then the message MUST either be rejected with a 437 Allocation Mismatch error (if it's a request) or silently ignored (if it's an indication or a ChannelData message). A client that gets a 437 error response to a request other than Allocate MUST assume the allocation no longer exists ๐
[RFC5389] defines a bunch of attributes, including the SOFTWARE and FINGERPRINT attributes โจ The client SHOULD include the SOFTWARE attribute in all Allocate and Refresh requests, and MAY include it in any other requests or indications. The server SHOULD include the SOFTWARE attribute in all Allocate and Refresh responses (success or failure), and MAY include it in other responses or indications. The client and the server MAY include the FINGERPRINT attribute in any STUN-formatted messages defined in this document ๐ง
TURN doesn't use the backwards-compatibility mechanism described in [RFC5389] ๐ โโ๏ธ
TURN, as defined in this spec, only supports IPv4 ๐ธ The client's IP address, the server's IP address, and all IP addresses appearing in a relayed transport address MUST be IPv4 addresses.
By default, TURN runs on the same ports as STUN: 3478 for TURN over UDP and TCP, and 5349 for TURN over TLS ๐ TURN also has its own SRV names: "turn" for UDP and TCP, and "turns" for TLS. You can use either the SRV procedures or the ALTERNATE-SERVER procedures (both described in Section 6) to run TURN on a different port ๐
To keep things interoperable, a TURN server MUST support UDP transport between the client and the server, and SHOULD support TCP and TLS transport ๐ซถ
When UDP is used between the client and the server, the client retransmits a request if it doesn't get a response within a certain timeout period ๐ Because of this, the server may receive two (or more) requests with the same 5-tuple and the same transaction id. STUN requires the server to recognize this and treat the request as idempotent (see [RFC5389]) ๐ง Some implementations meet this by remembering all received requests and their responses for 40 seconds. Other implementations reprocess the request and arrange for that reprocessing to return essentially the same response. To help implementors who go with the latter approach (the so-called "stateless stack approach"), this specification includes some implementation notes on how that might work ๐ Implementations are free to pick either approach, or some other approach that gives the same results ๐
When TCP is used between the client and the server, a bit error can corrupt a length field in a TURN packet, and then the receiver loses sync with the incoming stream of TURN messages ๐ญ A client or server that detects a long run of invalid TURN messages over TCP SHOULD close the TCP connection, so the other end can notice the situation faster ๐
Denial-of-service is a real threat here. Clients with valid usernames and passwords can attack the server, intentionally or by accident, so guard against it.
To mitigate denial-of-service attacks against the server (intentional or unintentional) by clients with valid usernames and passwords, it is RECOMMENDED that the server impose limits on both the number of allocations active at one time for a given username, and on the amount of bandwidth those allocations can use ๐ง The server should reject new allocations that would exceed the limit on active allocations with a 486 (Allocation Quota Exceeded) error (see Section 6.2), and should discard application data traffic that goes over the bandwidth quota ๐
5. Allocations ๐
All TURN operations revolve around allocations, and every TURN message is associated with an allocation ๐ An allocation conceptually consists of the following state data:
- the relayed transport address;
- the 5-tuple: (client's IP address, client's port, server IP address, server port, transport protocol);
- the authentication information;
- the time-to-expiry;
- a list of permissions;
- a list of channel to peer bindings.
The relayed transport address is the transport address the server allocates for talking to peers, while the 5-tuple describes the communication path between the client and the server ๐ค๏ธ On the client, the 5-tuple uses the client's host transport address. On the server, the 5-tuple uses the client's server-reflexive transport address ๐ช
Both the relayed transport address and the 5-tuple MUST be unique across all allocations, so either one can uniquely identify the allocation ๐
The authentication information (like username, password, realm, and nonce) is used both to verify subsequent requests and to compute the message integrity of responses ๐ The username, realm, and nonce values start out as the ones used in the authenticated Allocate request that created the allocation, though the server can change the nonce value during the allocation's lifetime using a 438 (Stale Nonce) reply ๐ Also, rather than storing the password explicitly (for security reasons), it may be desirable for the server to store the key value instead. The key value is an MD5 hash over the username, realm, and password (see [RFC5389]) ๐
The time-to-expiry is the number of seconds left until the allocation expires โฐ Each Allocate or Refresh transaction sets this timer, which then ticks down towards 0. By default, each Allocate or Refresh transaction resets the timer to the default lifetime value of 600 seconds (10 minutes), but the client can request a different value in the Allocate and Refresh request ๐ Allocations can only be refreshed using the Refresh request. Sending data to a peer does not refresh an allocation, so don't count on it ๐ซ When an allocation expires, the state data associated with it can be freed ๐งน
The list of permissions is described in Section 8, and the list of channels is described in Section 11 ๐ฆ
6. Creating an Allocation ๐
An allocation on the server is created using an Allocate transaction.
6.1. Sending an Allocate Request ๐
okay so first things first, the client has to build its Allocate request, and it's giving very deliberate energy ๐
The client starts by picking a host transport address. It is RECOMMENDED that the client grab a currently unused one, usually by letting the underlying OS pick a free port for a brand new socket ๐ง
Then the client picks a transport protocol to use between itself and the server. The transport protocol MUST be one of UDP, TCP, or TLS-over-TCP. Since this spec only allows UDP between the server and the peers, it is RECOMMENDED that the client pick UDP unless it has a real reason to go with something else ๐ฆ One reason would be that the client believes, either from configuration or from experimenting, that it can't reach any TURN server over UDP. More on that in Section 2.1 โจ
The client also picks a server transport address, and it SHOULD do this part as follows ๐ซถ The client gets a domain name for a TURN server, maybe through configuration. Then it uses the DNS procedures from [RFC5389], except it swaps the SRV service name from "stun" to "turn" (or "turns" for TURN over TLS) ๐ For example, to find servers in the example.com domain, the client looks up _turn._udp.example.com if it wants UDP, _turn._tcp.example.com for TCP, or _turns._tcp.example.com for TLS-over-TCP. Main character energy for sure ๐
The client MUST include a REQUESTED-TRANSPORT attribute in the request. This attribute says what transport protocol to use between the server and the peers, and not the one that shows up in the 5-tuple ๐ In this spec, the REQUESTED-TRANSPORT type is always UDP. It's only there so future extensions can name other protocols, which is so smart ๐
If the client wants the server to set the time-to-expiry field of the allocation to something other than the default lifetime, it MAY include a LIFETIME attribute with the value it wants ๐ธ That's just a request though, so the server can pick a different value if it wants. Also, the server will ignore any request to set the field to less than the default value. Not me the server being so strict about it ๐
If the client plans to use the DONT-FRAGMENT attribute later in one or more Send indications on this allocation, it SHOULD include DONT-FRAGMENT in the Allocate request too ๐ฅ That way the client can test whether the server supports this attribute at all. Smart move, bestie ๐
If the client needs the port number of the relayed transport address to be even, it includes the EVEN-PORT attribute ๐ฌ If that attribute isn't included, the port can be even or odd, no drama. Setting the R bit in the EVEN-PORT attribute to 1 asks the server to reserve the next highest port number (on the same IP address) for a later allocation โจ If the R bit is 0, no such request is made.
The client MAY also include a RESERVATION-TOKEN attribute in the request, asking the server to use a port that was reserved earlier for this allocation ๐ฆ If RESERVATION-TOKEN is included, then the client MUST omit the EVEN-PORT attribute. You can't have both, periodt ๐
Once everything is built, the client sends the Allocate request on the 5-tuple ๐ And that's the whole vibe!
6.2. Receiving an Allocate Request โจ
okay so when the server gets an Allocate request, it runs through a little checklist ๐ . Here are the checks, in order:
- The server MUST require that the request be authenticated ๐. This authentication MUST be done using the long-term credential mechanism of [RFC5389] unless the client and server agree to use another mechanism through some procedure outside the scope of this document.
- The server checks if the 5-tuple is currently in use by an existing allocation. If yes, the server rejects the request with a 437 (Allocation Mismatch) error. ๐ โโ๏ธ
- The server checks if the request contains a
REQUESTED-TRANSPORTattribute. If theREQUESTED-TRANSPORTattribute is not included or is malformed, the server rejects the request with a 400 (Bad Request) error. Otherwise, if the attribute is included but specifies a protocol other than UDP, the server rejects the request with a 442 (Unsupported Transport Protocol) error. ๐ซ - The request may contain a
DONT-FRAGMENTattribute. If it does, but the server does not support sending UDP datagrams with the DF bit set to 1 (see Section 12), then the server treats theDONT-FRAGMENTattribute in the Allocate request as an unknown comprehension-required attribute. ๐งฉ - The server checks if the request contains a
RESERVATION-TOKENattribute. If yes, and the request also contains anEVEN-PORTattribute, then the server rejects the request with a 400 (Bad Request) error. Otherwise, it checks to see if the token is valid (i.e., the token is in range and has not expired and the corresponding relayed transport address is still available). If the token is not valid for some reason, the server rejects the request with a 508 (Insufficient Capacity) error. ๐๏ธ - The server checks if the request contains an
EVEN-PORTattribute. If yes, then the server checks that it can satisfy the request (i.e., can allocate a relayed transport address as described below). If the server cannot satisfy the request, then the server rejects the request with a 508 (Insufficient Capacity) error. ๐ฅฒ - At any point, the server MAY choose to reject the request with a 486 (Allocation Quota Reached) error if it feels the client is trying to exceed some locally defined allocation quota. The server is free to define this allocation quota any way it wishes, but the server SHOULD define it based on the username used to authenticate the request, and not on the client's transport address. ๐
- Also at any point, the server MAY choose to reject the request with a 300 (Try Alternate) error if it wishes to redirect the client to a different server. The use of this error code and attribute follow the specification in [RFC5389]. ๐
If all the checks pass, the server creates the allocation. The 5-tuple is set to the 5-tuple from the Allocate request, while the list of permissions and the list of channels are initially empty. ๐
The server chooses a relayed transport address for the allocation as follows:
- If the request contains a
RESERVATION-TOKEN, the server uses the previously reserved transport address corresponding to the included token (if it is still available). Note that the reservation is a server-wide reservation and is not specific to a particular allocation, since the Allocate request containing theRESERVATION-TOKENuses a different 5-tuple than the Allocate request that made the reservation. The 5-tuple for the Allocate request containing theRESERVATION-TOKENattribute can be any allowed 5-tuple; it can use a different client IP address and port, a different transport protocol, and even different server IP address and port (provided, of course, that the server IP address and port are ones on which the server is listening for TURN requests). ๐ง - If the request contains an
EVEN-PORTattribute with the R bit set to 0, then the server allocates a relayed transport address with an even port number. ๐ - If the request contains an
EVEN-PORTattribute with the R bit set to 1, then the server looks for a pair of port numbers N and N+1 on the same IP address, where N is even. Port N is used in the current allocation, while the relayed transport address with port N+1 is assigned a token and reserved for a future allocation. The server MUST hold this reservation for at least 30 seconds, and MAY choose to hold longer (e.g., until the allocation with port N expires). The server then includes the token in aRESERVATION-TOKENattribute in the success response. ๐ซถ - Otherwise, the server allocates any available relayed transport address. ๐ธ
In all cases, the server SHOULD only allocate ports from the range 49152 - 65535 (the Dynamic and/or Private Port range [Port-Numbers]), unless the TURN server application knows, through some means not specified here, that other applications running on the same host as the TURN server application will not be impacted by allocating ports outside this range. This condition can often be satisfied by running the TURN server application on a dedicated machine and/or by arranging that any other applications on the machine allocate ports before the TURN server application starts. In any case, the TURN server SHOULD NOT allocate ports in the range 0 - 1023 (the Well-Known Port range) to discourage clients from using TURN to run standard services. ๐ โโ๏ธโจ
NOTE: The IETF is currently investigating the topic of randomized port assignments to avoid certain types of attacks (see [TSVWG-PORT]). It is strongly recommended that a TURN implementor keep abreast of this topic and, if appropriate, implement a randomized port assignment algorithm. This is especially applicable to servers that choose to pre-allocate a number of ports from the underlying OS and then later assign them to allocations; for example, a server may choose this technique to implement the EVEN-PORT attribute. ๐ฒ
The server determines the initial value of the time-to-expiry field as follows. If the request contains a LIFETIME attribute, then the server computes the minimum of the client's proposed lifetime and the server's maximum allowed lifetime. If this computed value is greater than the default lifetime, then the server uses the computed lifetime as the initial value of the time-to-expiry field. Otherwise, the server uses the default lifetime. It is RECOMMENDED that the server use a maximum allowed lifetime value of no more than 3600 seconds (1 hour). Servers that implement allocation quotas or charge users for allocations in some way may wish to use a smaller maximum allowed lifetime (perhaps as small as the default lifetime) to more quickly remove orphaned allocations (that is, allocations where the corresponding client has crashed or terminated or the client connection has been lost for some reason). Also, note that the time-to-expiry is recomputed with each successful Refresh request, and thus the value computed here applies only until the first refresh. โฐ
Once the allocation is created, the server replies with a success response. The success response contains:
- An
XOR-RELAYED-ADDRESSattribute containing the relayed transport address. ๐ฎ - A
LIFETIMEattribute containing the current value of the time-to-expiry timer. โณ - A
RESERVATION-TOKENattribute (if a second relayed transport address was reserved). ๐๏ธ - An
XOR-MAPPED-ADDRESSattribute containing the client's IP address and port (from the 5-tuple). ๐
NOTE: The XOR-MAPPED-ADDRESS attribute is included in the response as a convenience to the client. TURN itself does not make use of this value, but clients running ICE can often need this value and can thus avoid having to do an extra Binding transaction with some STUN server to learn it. ๐ง
The response (either success or error) is sent back to the client on the 5-tuple. ๐ฌ
NOTE: When the Allocate request is sent over UDP, section 7.3.1 of [RFC5389] requires that the server handle the possible retransmissions of the request so that retransmissions do not cause multiple allocations to be created. Implementations may achieve this using the so-called "stateless stack approach" as follows. To detect retransmissions when the original request was successful in creating an allocation, the server can store the transaction id that created the request with the allocation data and compare it with incoming Allocate requests on the same 5-tuple. Once such a request is detected, the server can stop parsing the request and immediately generate a success response. When building this response, the value of theLIFETIMEattribute can be taken from the time-to-expiry field in the allocate state data, even though this value may differ slightly from theLIFETIMEvalue originally returned. In addition, the server may need to store an indication of any reservation token returned in the original response, so that this may be returned in any retransmitted responses. For the case where the original request was unsuccessful in creating an allocation, the server may choose to do nothing special. Note, however, that there is a rare case where the server rejects the original request but accepts the retransmitted request (because conditions have changed in the brief intervening time period). If the client receives the first failure response, it will ignore the second (success) response and believe that an allocation was not created. An allocation created in this matter will eventually timeout, since the client will not refresh it. Furthermore, if the client later retries with the same 5-tuple but different transaction id, it will receive a 437 (Allocation Mismatch), which will cause it to retry with a different 5-tuple. The server may use a smaller maximum lifetime value to minimize the lifetime of allocations "orphaned" in this manner. ๐
6.3. Receiving an Allocate Success Response โจ
okay so if the client gets an Allocate success response, it MUST check that the mapped address and the relayed transport address are in an address family it understands and is ready to handle ๐ซถ this spec only covers the case where both of those addresses are IPv4 addresses ๐ธ if either one is in an address family the client can't handle, then the client MUST delete the allocation (Section 7) and MUST NOT attempt to create another allocation on that server until it believes the mismatch has been fixed ๐
The IETF is currently considering transition mechanisms between IPv4 and IPv6 that could result in a client sending an Allocate request over IPv6, but the request would arrive at the server over IPv4, or vice versa. Lowkey a whole plot twist ๐ฆ
otherwise, the client makes its own copy of the allocation data structure so it can track what's happening on the server โจ the big thing is that it needs to remember the actual lifetime the server sent back, not the value it asked for in the request ๐
the client also has to remember the 5-tuple it used for the request, plus the username and password it used to authenticate, so it can reuse them for later messages ๐ it also needs to keep track of the channels and permissions it sets up on the server ๐ง
the client will probably want to send the relayed transport address to its peers (using some method not specified here) so the peers can talk to it ๐ it may also want to use the server-reflexive address it gets in the XOR-MAPPED-ADDRESS attribute during its ICE processing ๐
6.4. Receiving an Allocate Error Response ๐
if the client gets an Allocate error response, what it does next depends on the actual error code ๐ญ
- (Request timed out): there's either a problem with the server, or a problem reaching the server over the chosen transport ๐ฅฒ the client counts the current transaction as failed, but it MAY choose to retry the Allocate request using a different transport (e.g., TCP instead of UDP) ๐
- 300 (Try Alternate): the server wants the client to use the server named in the
ALTERNATE-SERVERattribute instead ๐งญ the client counts this transaction as failed, but it SHOULD try the Allocate request with the alternate server before trying any other servers (e.g., other servers discovered using the SRV procedures) ๐ when it does, it follows theALTERNATE-SERVERprocedures specified in [RFC5389] โจ - 400 (Bad Request): the server thinks the client's request is malformed somehow ๐ the client counts the transaction as failed. It MAY notify the user or operator, and it SHOULD NOT retry the request with this server until it believes the problem has been fixed ๐ ๏ธ
- 401 (Unauthorized): if the client followed the long-term credential mechanism procedures and still gets this error, the server is not accepting its credentials ๐ค the client counts the transaction as failed and SHOULD notify the user or operator. It SHOULD NOT send any further requests to this server until it believes the problem has been fixed ๐ซ
- 403 (Forbidden): the request is valid, but the server is refusing to do it, probably because of administrative restrictions ๐ the client counts the transaction as failed. It MAY notify the user or operator, and it SHOULD NOT retry the same request with this server until it believes the problem has been fixed ๐
- 420 (Unknown Attribute): if the client put a
DONT-FRAGMENTattribute in the request, and the server rejected it with a 420 and listedDONT-FRAGMENTin theUNKNOWN-ATTRIBUTESattribute of the error response, then the client now knows the server doesn't supportDONT-FRAGMENT๐ง the client counts the transaction as failed but MAY choose to retry the Allocate request without theDONT-FRAGMENTattribute ๐ - 437 (Allocation Mismatch): this means the client picked a 5-tuple that the server sees as already in use ๐ one way that could happen is if an in-between NAT gave out a mapped transport address that was recently used by another client that crashed ๐ the client counts the transaction as failed. It SHOULD pick another client transport address and retry the Allocate request (using a different transaction id) โจ it SHOULD try three different client transport addresses before giving up on this server ๐ฅน once it gives up on the server, it SHOULD NOT try to create another allocation on the server for 2 minutes โณ
- 438 (Stale Nonce): see the long-term credential mechanism procedures [RFC5389] ๐
- 441 (Wrong Credentials): the client shouldn't get this error in response to an Allocate request ๐คจ if it does, it MAY notify the user or operator, and it SHOULD NOT retry the same request with this server until it believes the problem has been fixed ๐ โโ๏ธ
- 442 (Unsupported Transport Address): the client shouldn't get this error for a request for a UDP allocation ๐ต if it does, it MAY notify the user or operator, and it SHOULD NOT reattempt the request with this server until it believes the problem has been fixed ๐ซ
- 486 (Allocation Quota Reached): the server can't create any more allocations with this username right now ๐ต the client counts the transaction as failed. It SHOULD wait at least 1 minute before trying to create any more allocations on the server โฐ
- 508 (Insufficient Capacity): the server has no more relayed transport addresses available, or none with the requested properties, or the one that was reserved isn't available anymore ๐ฉ the client counts the current operation as failed. If it used either the
EVEN-PORTor theRESERVATION-TOKENattribute, it MAY choose to remove or change that attribute and try again right away ๐ช otherwise, it SHOULD wait at least 1 minute before trying to create any more allocations on this server ๐ฐ๏ธ
An unknown error response MUST be handled as described in [RFC5389] ๐ซถ
7. Refreshing an Allocation ๐
okay this part is all about the Refresh transaction ๐ it can do one of two things: (a) refresh an existing allocation and update its time-to-expiry, or (b) delete an existing allocation ๐๏ธ
if a client wants to keep using an allocation, it MUST refresh it before it expires ๐ค it's suggested that the client refresh the allocation roughly 1 minute before it expires โณ if a client doesn't want to use an allocation anymore, it SHOULD explicitly delete it ๐งน a client MAY refresh an allocation at any time for other reasons ๐ธ
7.1. Sending a Refresh Request ๐ค
if the client wants to delete an existing allocation right now, it includes a LIFETIME attribute with a value of 0 ๐ every other form of the request refreshes the allocation โจ
the Refresh transaction updates the time-to-expiry timer of an allocation ๐ฐ๏ธ if the client wants the server to set that timer to something other than the default lifetime, it includes a LIFETIME attribute with the value it wants ๐ the server then works out a new time-to-expiry value the same way it does for an Allocate transaction, with one exception: a requested lifetime of 0 makes the server delete the allocation immediately ๐
โโ๏ธ
7.2. Receiving a Refresh Request ๐ฅ
when the server gets a Refresh request, it processes it as per Section 4, plus the specific rules mentioned here ๐ง
the server works out a value called the "desired lifetime" like this: if the request has a LIFETIME attribute and its value is 0, then the "desired lifetime" is 0 ๐ otherwise, if the request has a LIFETIME attribute, the server takes the minimum of the client's requested lifetime and the server's maximum allowed lifetime ๐งฎ if that computed value is greater than the default lifetime, then the "desired lifetime" is the computed value ๐ otherwise, the "desired lifetime" is the default lifetime ๐
what happens next depends on the "desired lifetime" value ๐
- if the "desired lifetime" is 0, the request succeeds and the allocation is deleted ๐๏ธ
- if the "desired lifetime" is non-zero, the request succeeds and the allocation's time-to-expiry is set to the "desired lifetime" โฐ
if the request succeeds, the server sends a success response containing:
- a
LIFETIMEattribute containing the current value of the time-to-expiry timer ๐
NOTE: a server doesn't need to do anything special to make Refresh requests idempotent over UDP using the "stateless stack approach" ๐ง retransmitted Refresh requests with a non-zero "desired lifetime" will simply refresh the allocation ๐ท a retransmitted Refresh request with a zero "desired lifetime" will cause a 437 (Allocation Mismatch) response if the allocation has already been deleted, but the client will treat this as equivalent to a success response (see below) ๐ซถ
7.3. Receiving a Refresh Response ๐
if the client gets a success response to its Refresh request with a non-zero lifetime, it updates its copy of the allocation data structure with the time-to-expiry value in the response ๐
if the client gets a 437 (Allocation Mismatch) error response to a request to delete the allocation, then the allocation no longer exists, and it should consider its request as having effectively succeeded ๐ฅณ iconic behaviour, honestly ๐
8. Permissions โจ
okay so every allocation keeps a little list of permissions ๐ each one has an IP address and a time-to-expiry (how many seconds until it poofs). while a permission exists, any peer using that IP address is allowed to send data to the client ๐ and inside an allocation, the IP address is the thing that makes a permission unique, so it's basically the permission's ID ๐
the client can make the server install or refresh a permission for an IP address by sending either a CreatePermission request or a ChannelBind request ๐ซถ and that does one of two things:
- if there's no permission for that IP address yet, the server makes one with that IP address and a time-to-expiry equal to Permission Lifetime ๐
- if there's already a permission for that IP address, its time-to-expiry gets reset to Permission Lifetime ๐
the Permission Lifetime MUST be 300 seconds (= 5 minutes) โฐ so that's the main number to remember bestie ๐
each permission's time-to-expiry ticks down once per second until it hits 0 ๐ then it expires and gets deleted, bye bye ๐
CreatePermission and ChannelBind requests can be freely intermixed on a permission ๐ฆ a permission can get installed and/or refreshed with a CreatePermission request and then later refreshed with a ChannelBind request, or the other way around. it's giving flexible energy โจ
now the important part ๐ when a UDP datagram shows up at the relayed transport address for the allocation, the server pulls the source IP address out of the IP header ๐ then it compares that address with the IP address of every permission in the allocation's list. if nothing matches, relaying isn't allowed and the server silently discards the UDP datagram ๐ if there's an exact match, the permission check passes and the server keeps processing the UDP datagram the way it's described elsewhere (Section 10.3) ๐ธ and just to be clear, only the addresses get compared. port numbers don't matter at all ๐ซข
the permissions for one allocation are totally unrelated to the permissions for a different allocation ๐ and if an allocation expires, all of its permissions expire right along with it ๐
NOTE: Though TURN permissions expire after 5 minutes, many NATs deployed at the time of publication expire their UDP bindings considerably faster. Thus, an application using TURN will probably wish to send some sort of keep-alive traffic at a much faster rate. Applications using ICE should follow the keep-alive guidelines of ICE [RFC5245], and applications not using ICE are advised to do something similar. ๐จ
9. CreatePermission
okay so TURN gives the client two ways to install or refresh permissions on the server ๐ this section is about one of them: the CreatePermission request ๐
a CreatePermission request can be used together with either the Send mechanism in Section 10 or the Channel mechanism in Section 11 ๐ง
9.1. Forming a CreatePermission Request
any client who wants to install or refresh one or more permissions can send a CreatePermission request to the server ๐
when you're forming a CreatePermission request, you MUST include at least one XOR-PEER-ADDRESS attribute ๐ซถ and you MAY include more than one of them. the IP address part of each XOR-PEER-ADDRESS attribute is the IP address that should get a permission installed or refreshed ๐ธ the port part gets ignored, so it can be any arbitrary value ๐คทโโ๏ธ and the XOR-PEER-ADDRESS attributes can show up in any order ๐
9.2. Receiving a CreatePermission Request
when the server gets a CreatePermission request, it follows Section 4 plus the specific rules in this section ๐
first the message gets checked for validity โ
the CreatePermission request MUST contain at least one XOR-PEER-ADDRESS attribute and MAY contain multiple of them. if there aren't any, or if any of them are invalid, the server returns a 400 (Bad Request) error ๐ and if the request is valid but the server can't satisfy it because of a capacity limit or something similar, the server returns a 508 (Insufficient Capacity) error ๐ญ
the server MAY impose restrictions on which IP addresses are allowed in the XOR-PEER-ADDRESS attribute ๐ง if an address isn't allowed, the server rejects the request with a 403 (Forbidden) error ๐
โโ๏ธ
if the message is valid and the server can actually carry out the request, then the server installs or refreshes a permission for the IP address in each XOR-PEER-ADDRESS attribute, as described in Section 8 ๐ฅ the port part of each attribute is ignored and can be any arbitrary value ๐ฒ
then the server responds with a CreatePermission success response ๐ and there are no mandatory attributes in that success response, so it's pretty chill ๐
NOTE: A server need not do anything special to implement idempotency of CreatePermission requests over UDP using the "stateless stack approach". Retransmitted CreatePermission requests will simply refresh the permissions. ๐
9.3. Receiving a CreatePermission Response
if the client gets a valid CreatePermission success response, then it updates its data structures to show that the permissions have been installed or refreshed ๐โจ easy peasy ๐
10. Send and Data Methods
okay so TURN has two mechanisms for sending and receiving data from peers ๐ this section covers the Send and Data mechanisms, and Section 11 covers the Channel mechanism ๐ฆ
10.1. Forming a Send Indication
the client can use a Send indication to hand data to the server so it can relay it to a peer ๐ the client can even use a Send indication when a channel is already bound to that peer ๐ค BUT the client MUST make sure there's a permission installed for the IP address of the peer that the Send indication is going to ๐ this is what stops a third party from using a TURN server to send data to random destinations, which would be so not it ๐ โโ๏ธ
when you're forming a Send indication, you MUST include an XOR-PEER-ADDRESS attribute and a DATA attribute ๐ the XOR-PEER-ADDRESS attribute holds the transport address of the peer the data is going to, and the DATA attribute holds the actual application data being sent ๐ฆ
the client MAY include a DONT-FRAGMENT attribute in the Send indication if it wants the server to set the DF bit on the UDP datagram sent to the peer ๐งฉ
10.2. Receiving a Send Indication
when the server gets a Send indication, it follows Section 4 plus the specific rules here ๐
first the message gets checked for validity ๐ the Send indication MUST contain both an XOR-PEER-ADDRESS attribute and a DATA attribute. if either one is missing or invalid, the message gets discarded ๐๏ธ also, the DATA attribute is allowed to contain zero bytes of data ๐ซง
the Send indication may also contain the DONT-FRAGMENT attribute ๐ if the server can't set the DF bit on outgoing UDP datagrams when that attribute is present, then the server acts like the DONT-FRAGMENT attribute is an unknown comprehension-required attribute, and so the Send indication gets discarded ๐
the server also checks that there's a permission installed for the IP address in the XOR-PEER-ADDRESS attribute ๐ if there isn't one, the message gets discarded ๐ and a Send indication never makes the server refresh the permission, so it can't keep a permission alive by itself ๐ซ
the server MAY impose restrictions on the IP address and port values allowed in the XOR-PEER-ADDRESS attribute ๐ if a value isn't allowed, the server silently discards the Send indication ๐คซ
if everything's OK, the server forms a UDP datagram like this ๐
- the source transport address is the relayed transport address of the allocation, where the allocation is figured out from the 5-tuple the Send indication arrived on ๐
- the destination transport address is taken from the
XOR-PEER-ADDRESSattribute ๐ฏ - the data after the UDP header is the contents of the value field of the
DATAattribute ๐
the way the DONT-FRAGMENT attribute gets handled (if it's there) is described in Section 12 ๐ง
then the resulting UDP datagram gets sent to the peer ๐๐
10.3. Receiving a UDP Datagram
when the server gets a UDP datagram at a currently allocated relayed transport address, it looks up the allocation associated with that relayed transport address ๐ then it checks whether the allocation's set of permissions allows the datagram to be relayed, as described in Section 8 โ
if relaying is permitted, the server checks whether there's a channel bound to the peer that sent the UDP datagram (see Section 11) ๐ฆ if a channel is bound, processing goes on as described in Section 11.7 ๐
if relaying is permitted but no channel is bound to the peer, then the server forms and sends a Data indication ๐ the Data indication MUST contain both an XOR-PEER-ADDRESS and a DATA attribute. the DATA attribute is set to the value of the 'data octets' field from the datagram, and the XOR-PEER-ADDRESS attribute is set to the source transport address of the received UDP datagram ๐ then the Data indication gets sent on the 5-tuple associated with the allocation ๐
10.4. Receiving a Data Indication
when the client gets a Data indication, it checks that the Data indication contains both an XOR-PEER-ADDRESS and a DATA attribute, and it discards the indication if it doesn't ๐๏ธ the client SHOULD also check that the XOR-PEER-ADDRESS attribute value has an IP address the client believes has an active permission, and discard the Data indication otherwise ๐ต๏ธโโ๏ธ again, the DATA attribute is allowed to contain zero bytes of data ๐ซง
NOTE: The latter check protects the client against an attacker who somehow manages to trick the server into installing permissions not desired by the client. ๐ก๏ธ๐ค
if the Data indication passes the checks above, the client delivers the data octets inside the DATA attribute to the application ๐ along with an indication that they came from the peer whose transport address is given by the XOR-PEER-ADDRESS attribute ๐โจ
11. Channels โจ
Okay so channels are how the client and server send app data using ChannelData messages ๐, which have less overhead than Send and Data indications. Less stuff in the way, more data going through ๐ซถ
The ChannelData message (see Section 11.4) starts with a two-byte field that carries the channel number. Here's how those values are split up ๐
0x0000through0x3FFF: these can never be used for channel numbers ๐ โโ๏ธ0x4000through0x7FFF: these are the allowed channel numbers (16,383 possible values) ๐0x8000through0xFFFF: these are reserved for future use ๐ฎ
Because of that split, you can tell ChannelData messages apart from STUN-formatted messages (like an Allocate request or a Send indication, etc.) just by checking the first two bits of the message ๐
0b00: STUN-formatted message (since the first two bits of a STUN-formatted message are always zero).0b01: ChannelData message (since the channel number is the first field in the ChannelData message and channel numbers fall in the range 0x4000 - 0x7FFF).0b10: Reserved0b11: Reserved
The reserved values may be used in the future to extend the range of channel numbers, so an implementation MUST NOT assume that a TURN message always starts with a 0 bit. Don't hardcode that, bestie ๐ โโ๏ธ
Channel bindings are always initiated by the client ๐ . The client can bind a channel to a peer at any time during the lifetime of the allocation. It may bind a channel to a peer before exchanging data with it, or after exchanging data with it (using Send and Data indications) for some time, or it may choose never to bind a channel to it. The client can also bind channels to some peers while not binding channels to other peers, which is very much a choose your own adventure situation ๐ฆ
Channel bindings are specific to an allocation. That means using a channel number or peer transport address in a channel binding in one allocation has no impact on their use in a different allocation ๐ธ If an allocation expires, all its channel bindings expire with it ๐ญ
A channel binding consists of:
- a channel number;
- a transport address (of the peer); and
- a time-to-expiry timer.
Inside an allocation, a channel binding is uniquely identified either by the channel number or by the peer's transport address. So the same channel cannot be bound to two different transport addresses, and the same transport address cannot be bound to two different channels. No sharing the spotlight ๐
A channel binding lasts for 10 minutes unless refreshed. Refreshing the binding (by the server receiving a ChannelBind request rebinding the channel to the same peer) resets the time-to-expiry timer back to 10 minutes โฐ
When the channel binding expires, the channel becomes unbound. Once it's unbound, the channel number can be bound to a different transport address, and the transport address can be bound to a different channel number. To prevent race conditions, the client MUST wait 5 minutes after the channel binding expires before attempting to bind the channel number to a different transport address or the transport address to a different channel number. Patience is a whole aesthetic ๐ง
When binding a channel to a peer, the client SHOULD be prepared to receive ChannelData messages on the channel from the server as soon as it has sent the ChannelBind request. Over UDP, it's possible for the client to get ChannelData messages from the server before it gets a ChannelBind success response, so stay ready ๐ซฃ
In the other direction, the client MAY elect to send ChannelData messages before receiving the ChannelBind success response. Doing so, however, runs the risk of having the ChannelData messages dropped by the server if the ChannelBind request does not succeed for some reason (e.g., packet lost if the request is sent over UDP, or the server being unable to fulfill the request). A client that wants to be safe should either queue the data or use Send indications until the channel binding is confirmed ๐ก๏ธ
11.1. Sending a ChannelBind Request
A channel binding is created or refreshed using a ChannelBind transaction. A ChannelBind transaction also creates or refreshes a permission towards the peer (see Section 8) โจ
To start the ChannelBind transaction, the client forms a ChannelBind request. The channel to be bound goes in a CHANNEL-NUMBER attribute, and the peer's transport address goes in an XOR-PEER-ADDRESS attribute. Section 11.2 describes the restrictions on these attributes ๐
Rebinding a channel to the same transport address it's already bound to is a way to refresh a channel binding and the corresponding permission without sending any data to the peer. Just note that permissions need to be refreshed more frequently than channels ๐
11.2. Receiving a ChannelBind Request
When the server receives a ChannelBind request, it processes it as per Section 4, plus the specific rules mentioned here ๐ฅ๏ธ
The server checks the following:
- the request contains both a
CHANNEL-NUMBERand anXOR-PEER-ADDRESSattribute; - the channel number is in the range
0x4000through0x7FFE(inclusive); - the channel number is not currently bound to a different transport address (same transport address is OK);
- the transport address is not currently bound to a different channel number.
If any of these tests fail, the server replies with a 400 (Bad Request) error ๐
The server MAY impose restrictions on the IP address and port values allowed in the XOR-PEER-ADDRESS attribute. If a value isn't allowed, the server rejects the request with a 403 (Forbidden) error ๐ซ
If the request is valid but the server can't fulfill it because of some capacity limit or something similar, the server replies with a 508 (Insufficient Capacity) error ๐ต
Otherwise, the server replies with a ChannelBind success response. There are no required attributes in a successful ChannelBind response ๐
If the server can satisfy the request, then it creates or refreshes the channel binding using the channel number in the CHANNEL-NUMBER attribute and the transport address in the XOR-PEER-ADDRESS attribute. It also installs or refreshes a permission for the IP address in the XOR-PEER-ADDRESS attribute, as described in Section 8 ๐
NOTE: A server need not do anything special to implement idempotency of ChannelBind requests over UDP using the "stateless stack approach". Retransmitted ChannelBind requests will simply refresh the channel binding and the corresponding permission. Furthermore, the client must wait 5 minutes before binding a previously bound channel number or peer address to a different channel, eliminating the possibility that the transaction would initially fail but succeed on a retransmission. ๐
11.3. Receiving a ChannelBind Response
When the client receives a ChannelBind success response, it updates its data structures to record that the channel binding is now active. It also updates them to record that the corresponding permission has been installed or refreshed โ
If the client gets a ChannelBind failure response that says the channel information is out-of-sync between the client and the server (e.g., an unexpected 400 "Bad Request" response), it is RECOMMENDED that the client immediately delete the allocation and start afresh with a new allocation. A clean slate is the move ๐งน๐
11.4. The ChannelData Message
The ChannelData message carries application data between the client and the server. It has the following format ๐ฆ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Channel Number | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
/ Application Data /
/ /
| |
| +-------------------------------+
| |
+-------------------------------+
The Channel Number field specifies the number of the channel the data is traveling on, and thus the address of the peer that is sending or is to receive the data ๐
The Length field specifies the length in bytes of the application data field (it does not include the size of the ChannelData header). Note that 0 is a valid length ๐ค
The Application Data field carries the data the client is trying to send to the peer, or that the peer is sending to the client ๐
11.5. Sending a ChannelData Message
Once a client has bound a channel to a peer, then when it has data to send to that peer, it may use either a ChannelData message or a Send indication. In other words, the client isn't obligated to use the channel when it exists, and it can freely mix the two message types when sending data to the peer ๐ The server, on the other hand, MUST use the ChannelData message if a channel has been bound to the peer. ๐ค
The fields of the ChannelData message are filled in as described in Section 11.4 ๐
Over TCP and TLS-over-TCP, the ChannelData message MUST be padded to a multiple of four bytes in order to ensure the alignment of subsequent messages. The padding is not reflected in the length field of the ChannelData message, so the actual size of a ChannelData message (including padding) is (4 + Length) rounded up to the nearest multiple of 4. Over UDP, the padding is not required but MAY be included. ๐ซง
The ChannelData message is then sent on the 5-tuple associated with the allocation ๐
11.6. Receiving a ChannelData Message ๐
okay so the receiver checks the first two bits to figure out if it's a ChannelData message or a STUN-formatted one, as we talked about above ๐ง . if the message uses a value in the reserved range (0x8000 through 0xFFFF), it gets silently discarded ๐ no drama, just gone.
if a ChannelData message shows up in a UDP datagram but the datagram is too short for the length the message claims, it also gets silently discarded. the UDP header length field has to be at least the ChannelData header length field value + 4 + 8, or it's a no-go ๐
if the ChannelData message comes in over TCP or over TLS-over-TCP, the actual length is whatever Section 11.5 describes ๐
if the ChannelData message lands on a channel that isn't bound to any peer, yep, silently discarded ๐ฆ
now for the client side: it is RECOMMENDED that the client discard the ChannelData message if it believes there's no active permission towards the peer. and on the server, receiving a ChannelData message MUST NOT refresh either the channel binding or the permission towards the peer. no sneaky refreshes, bestie ๐ โโ๏ธโจ
on the server, if no errors are detected, the server relays the application data to the peer by forming a UDP datagram like this:
- the source transport address is the relayed transport address of the allocation, where the allocation is determined by the 5-tuple on which the ChannelData message arrived ๐ธ
- the destination transport address is the transport address the channel is bound to ๐
- the data right after the UDP header is the contents of the data field of the ChannelData message ๐
the UDP datagram then gets sent to the peer. one thing to note: if the Length field in the ChannelData message is 0, there's no data in the datagram, but the datagram still gets formed and sent anyway ๐ซถ
11.7. Relaying Data from the Peer ๐
when the server gets a UDP datagram on the relayed transport address tied to an allocation, it processes it the way Section 10.3 describes. if that section says a ChannelData message should be sent (because there's a channel bound to the peer that sent the datagram), the server forms and sends a ChannelData message as Section 11.5 explains ๐จโจ
12. IP Header Fields โจ
this section is all about how the server sets different fields in the IP header when it's relaying between the client and the peer, or the other way around ๐ง these descriptions apply when (a) the server sends a UDP datagram to the peer, or (b) the server sends a Data indication or ChannelData message to the client over UDP transport. they do NOT apply to TURN messages sent over TCP or TLS transport from the server to the client, so chill on those ๐
each description has two parts: a preferred behavior and an alternate behavior. the server SHOULD implement the preferred behavior. but if that's not possible for a particular field, then it SHOULD implement the alternative behavior. so it's basically plan A, plan B ๐
Time to Live (TTL) field โณ
Preferred Behavior: if the incoming value is 0, then drop the incoming packet. otherwise, set the outgoing Time to Live/Hop Count to one less than the incoming value ๐ฆ
Alternate Behavior: set the outgoing value to the default for outgoing packets ๐ธ
Differentiated Services Code Point (DSCP) field [RFC2474] ๐โโ๏ธ
Preferred Behavior: set the outgoing value to the incoming value, unless the server includes a differentiated services classifier and marker [RFC2474] ๐
Alternate Behavior: set the outgoing value to a fixed value, which by default is Best Effort unless it's configured otherwise ๐
in both cases, if the server is immediately adjacent to a differentiated services classifier and marker, then DSCP MAY be set to any arbitrary value in the direction towards the classifier. โจ
Explicit Congestion Notification (ECN) field [RFC3168] ๐ฆ
Preferred Behavior: set the outgoing value to the incoming value, unless the server is doing Active Queue Management, the incoming ECN field is ECT(1) (=0b01) or ECT(0) (=0b10), and the server wants to indicate that congestion has been experienced. in that case, set the outgoing value to CE (=0b11) ๐ฅ
Alternate Behavior: set the outgoing value to Not-ECT (=0b00) ๐
IPv4 Fragmentation fields ๐งฉ
Preferred Behavior: when the server sends a packet to a peer in response to a Send indication containing the DONT-FRAGMENT attribute, set the DF bit in the outgoing IP header to 1. in all other cases when sending an outgoing packet containing application data (for example, a Data indication, a ChannelData message, or a Send indication that doesn't include the DONT-FRAGMENT attribute), copy the DF bit from the DF bit of the incoming packet that contained the application data ๐
set the other fragmentation fields (Identification, More Fragments, Fragment Offset) as appropriate for a packet originating from the server ๐ซถ
Alternate Behavior: same as the preferred behavior, except always assume the incoming DF bit is 0 ๐
in both the Preferred and Alternate Behaviors, the resulting packet may be too large for the outgoing link. if that happens, the normal fragmentation rules apply [RFC1122] ๐
IPv4 Options ๐
Preferred Behavior: the outgoing packet is sent without any IPv4 options ๐
Alternate Behavior: same as preferred ๐ฅฐ
13. New STUN Methods ๐ฆ
this section lists the codepoints for the new STUN methods defined in this specification. the semantics of these new methods live elsewhere in the document, so go look there ๐
0x003 : Allocate (only request/response semantics defined)
0x004 : Refresh (only request/response semantics defined)
0x006 : Send (only indication semantics defined)
0x007 : Data (only indication semantics defined)
0x008 : CreatePermission (only request/response semantics defined
0x009 : ChannelBind (only request/response semantics defined)
14. New STUN Attributes ๐
this STUN extension defines the following new attributes:
0x000C: CHANNEL-NUMBER
0x000D: LIFETIME
0x0010: Reserved (was BANDWIDTH)
0x0012: XOR-PEER-ADDRESS
0x0013: DATA
0x0016: XOR-RELAYED-ADDRESS
0x0018: EVEN-PORT
0x0019: REQUESTED-TRANSPORT
0x001A: DONT-FRAGMENT
0x0021: Reserved (was TIMER-VAL)
0x0022: RESERVATION-TOKEN
some of these attributes have lengths that aren't multiples of 4. by the rules of STUN, any attribute whose length is not a multiple of 4 bytes MUST be immediately followed by 1 to 3 padding bytes, so the next attribute (if there is one) starts on a 4-byte boundary (see [RFC5389]). padding is non-negotiable, babe ๐
14.1. CHANNEL-NUMBER ๐ข
the CHANNEL-NUMBER attribute contains the number of the channel. its value portion is 4 bytes long and consists of a 16-bit unsigned integer, followed by a two-octet RFFU (Reserved For Future Use) field, which MUST be set to 0 on transmission and MUST be ignored on reception ๐ธ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Channel Number | RFFU = 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14.2. LIFETIME โฐ
the LIFETIME attribute represents how long the server will keep an allocation going when there's no refresh. its value portion is 4 bytes long and consists of a 32-bit unsigned integral value representing the number of seconds remaining until expiration ๐ฐ๏ธ
14.3. XOR-PEER-ADDRESS ๐งญ
the XOR-PEER-ADDRESS specifies the address and port of the peer as seen from the TURN server. (for example, the peer's server-reflexive transport address if the peer is behind a NAT.) it's encoded in the same way as XOR-MAPPED-ADDRESS [RFC5389] ๐
14.4. DATA ๐ฆ
the DATA attribute is present in all Send and Data indications. its value portion is variable length and holds the application data, meaning the data that would immediately follow the UDP header if the data was been sent directly between the client and the peer ๐ if the length of this attribute is not a multiple of 4, then padding must be added after this attribute ๐งธ
14.5. XOR-RELAYED-ADDRESS ๐
the XOR-RELAYED-ADDRESS is present in Allocate responses. it specifies the address and port that the server allocated to the client. it's encoded in the same way as XOR-MAPPED-ADDRESS [RFC5389] โจ
14.6. EVEN-PORT ๐ฆ
this attribute lets the client request that the port in the relayed transport address be even, and (optionally) that the server reserve the next-higher port number. its value portion is 1 byte long, and its format is:
0
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|R| RFFU |
+-+-+-+-+-+-+-+-+
the value contains a single 1-bit flag:
R: if 1, the server is requested to reserve the next-higher port number (on the same IP address) for a subsequent allocation. if 0, no such reservation is requested ๐ซ
the other 7 bits of the attribute's value must be set to zero on transmission and ignored on reception. and since the length of this attribute is not a multiple of 4, padding must immediately follow this attribute ๐
14.7. REQUESTED-TRANSPORT ๐
the client uses this attribute to request a specific transport protocol for the allocated transport address. its value is 4 bytes with the following format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Protocol | RFFU |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
the Protocol field specifies the desired protocol. the codepoints used in this field are taken from those allowed in the Protocol field in the IPv4 header and the NextHeader field in the IPv6 header [Protocol-Numbers]. this specification only allows codepoint 17 (User Datagram Protocol) ๐
the RFFU field MUST be set to zero on transmission and MUST be ignored on reception. it's reserved for future uses ๐
14.8. DONT-FRAGMENT ๐
the client uses this attribute to ask the server to set the DF (Don't Fragment) bit in the IP header when it relays the application data onward to the peer. this attribute has no value part, so the attribute length field is 0 ๐ซง
14.9. RESERVATION-TOKEN ๐๏ธ
the RESERVATION-TOKEN attribute contains a token that uniquely identifies a relayed transport address being held in reserve by the server. the server includes this attribute in a success response to tell the client about the token, and the client includes this attribute in a subsequent Allocate request to ask the server to use that relayed transport address for the allocation ๐
the attribute value is 8 bytes and contains the token value. ๐ฅน
15. New STUN Error Response Codes ๐จ
This document adds some brand-new error response codes, so let's go through them. ๐
403(Forbidden): The request was totally valid, but the server can't do it because of admin rules or something similar. ๐ โโ๏ธ437(Allocation Mismatch): Either the server wanted an allocation to already exist and there isn't one, or the request needed no allocation and there's already one sitting there. ๐คจ441(Wrong Credentials): The credentials in a non-Allocate request don't match the ones used to make the allocation. ๐โ442(Unsupported Transport Protocol): The Allocate request asked the server to use a transport protocol between itself and the peer that it doesn't support. NOTE: This does NOT refer to the transport protocol used in the 5-tuple. ๐ง486(Allocation Quota Reached): This username has hit its limit and can't make any more allocations right now. ๐508(Insufficient Capacity): The server is at some capacity limit and can't finish the request. In an Allocate response, that could mean there are no more relayed transport addresses free at the moment, none that match the requested properties, or the one tied to the reservation token you sent isn't available. ๐ญ
16. Detailed Example โจ
Okay, this section is the fun one, because it walks through a real TURN conversation and shows what's inside every single message. ๐ It uses the network diagram from the Overview (Figure 1).
For each message, we list the attributes it carries and their values. To keep things readable, values are shown in a human-friendly way instead of the actual octets. For example, XOR-RELAYED-ADDRESS=192.0.2.15:9000 means the XOR-RELAYED-ADDRESS attribute is included with an address of 192.0.2.15 and a port of 9000. The address and port are written before the xor-ing happens. For attributes with string-like values, such as SOFTWARE="Example client, version 1.03" and NONCE="adl7W7PeDU4hKE72jdaQvbAMcr6h39sm", the quotes are just there so you can see where the value starts and ends. They aren't actually part of the value. ๐
TURN TURN Peer Peer
client server A B
| | | |
|--- Allocate request -------------->| | |
| Transaction-Id=0xA56250D3F17ABE679422DE85 | |
| SOFTWARE="Example client, version 1.03" | |
| LIFETIME=3600 (1 hour) | | |
| REQUESTED-TRANSPORT=17 (UDP) | | |
| DONT-FRAGMENT | | |
| | | |
|<-- Allocate error response --------| | |
| Transaction-Id=0xA56250D3F17ABE679422DE85 | |
| SOFTWARE="Example server, version 1.17" | |
| ERROR-CODE=401 (Unauthorized) | | |
| REALM="example.com" | | |
| NONCE="adl7W7PeDU4hKE72jdaQvbAMcr6h39sm" | |
| | | |
|--- Allocate request -------------->| | |
| Transaction-Id=0xC271E932AD7446A32C234492 | |
| SOFTWARE="Example client 1.03" | | |
| LIFETIME=3600 (1 hour) | | |
| REQUESTED-TRANSPORT=17 (UDP) | | |
| DONT-FRAGMENT | | |
| USERNAME="George" | | |
| REALM="example.com" | | |
| NONCE="adl7W7PeDU4hKE72jdaQvbAMcr6h39sm" | |
| MESSAGE-INTEGRITY=... | | |
| | | |
|<-- Allocate success response ------| | |
| Transaction-Id=0xC271E932AD7446A32C234492 | |
| SOFTWARE="Example server, version 1.17" | |
| LIFETIME=1200 (20 minutes) | | |
| XOR-RELAYED-ADDRESS=192.0.2.15:50000 | |
| XOR-MAPPED-ADDRESS=192.0.2.1:7000 | |
| MESSAGE-INTEGRITY=... | | |
First up, the client picks a host transport address to use for the TURN session. In this example it's 10.1.1.2:49721, as you can see in Figure 1. โจ Then it sends an Allocate request to the server's transport address. The client randomly picks a 96-bit transaction id, 0xA56250D3F17ABE679422DE85, for this exchange, and that goes into the transaction id field of the fixed header. ๐ฆ
The client adds a SOFTWARE attribute to say what software it's running. Here it says "Example client, version 1.03," which means version 1.03 of something called the Example client. ๐ It also includes LIFETIME because it wants the allocation to last longer than the default 10 minutes. The value is 3600 seconds, which is 1 hour. ๐
The client must always include a REQUESTED-TRANSPORT attribute in an Allocate request. The only value this specification allows is 17, which means UDP between the server and the peers. ๐ธ
The client also throws in DONT-FRAGMENT, since it plans to use that attribute later in Send indications. This one is just an attribute header with no value part, so it's super lightweight. ๐
We're pretending the client hasn't talked to this server recently, so it leaves out USERNAME, REALM, NONCE and MESSAGE-INTEGRITY. ๐ One last thing: the order of attributes in a message doesn't matter, except that MESSAGE-INTEGRITY and FINGERPRINT have to be placed in their special spots. The client could've ordered them differently and been totally fine. ๐
Servers want every request authenticated, so when the first Allocate request shows up without any auth attributes, the server rejects it. ๐ซ It follows the long-term credential mechanism from STUN [RFC5389]. It sends back an ERROR-CODE of 401 (Unauthorized), a REALM attribute naming its authentication realm (here it's the server's domain, "example.com"), and a nonce in a NONCE attribute. It also sends a SOFTWARE attribute describing its own software. ๐ฅ๏ธ
When the client gets that 401, it tries again, this time with the auth attributes. It picks a fresh transaction id and copies over all the attributes from before. ๐ It adds a USERNAME attribute, using the realm it got from the server to work out which username to use. In this case, the client is set up to use the username "George" for the realm "example.com". It also includes REALM and NONCE, copied straight from the 401 response. ๐ซถ
Last, the client adds MESSAGE-INTEGRITY as the final attribute. Its value is a HMAC-SHA1 hash over the message, which is shown as just "..." above. That HMAC-SHA1 calculation uses a password too, so an attacker can't work out the message integrity value without somehow knowing the secret password. ๐ Nice try, bestie. ๐ค
When the server gets this authenticated Allocate request, it checks that everything looks good and then creates the allocation. Then it sends back an Allocate success response. โ The response includes a LIFETIME attribute with the allocation's lifetime. The client asked for an hour, but this server doesn't allow anything longer than 20 minutes, so it cut it down to 20 minutes. Rude, but fair. ๐
The server also includes an XOR-RELAYED-ADDRESS attribute, which holds the relayed transport address of the allocation. ๐ฆ It includes an XOR-MAPPED-ADDRESS attribute too, which holds the client's server-reflexive address. TURN doesn't use that value for anything else, but the server sends it back as a little bonus for the client. ๐ Then it adds MESSAGE-INTEGRITY to authenticate the response and make sure nobody messed with it. Notice that this response doesn't carry USERNAME, REALM or NONCE. Finally, the server includes a SOFTWARE attribute of its own. ๐
TURN TURN Peer Peer
client server A B
|--- CreatePermission request ------>| | |
| Transaction-Id=0xE5913A8F460956CA277D3319 | |
| XOR-PEER-ADDRESS=192.0.2.150:0 | | |
| USERNAME="George" | | |
| REALM="example.com" | | |
| NONCE="adl7W7PeDU4hKE72jdaQvbAMcr6h39sm" | |
| MESSAGE-INTEGRITY=... | | |
| | | |
|<-- CreatePermission success resp.--| | |
| Transaction-Id=0xE5913A8F460956CA277D3319 | |
| MESSAGE-INTEGRITY=... | | |
Now the client sets up a permission toward Peer A so it can send some application data. ๐ง It does this with a CreatePermission request. The XOR-PEER-ADDRESS attribute holds the IP address the permission is for, which is Peer A's IP. The port in that attribute gets ignored in a CreatePermission request, so here it's just set to 0. โญ Also notice that the client uses Peer A's server-reflexive IP address, not its private host address. That's the move. ๐ The client reuses the same username, realm and nonce from the earlier request on this allocation. It's allowed to include a SOFTWARE attribute, but it decides not to. ๐
The server takes the CreatePermission request, sets up the matching permission, and replies with a CreatePermission success response. ๐ Like the client, the server skips SOFTWARE in its reply. Also, success responses carry a MESSAGE-INTEGRITY attribute (assuming the server is using the long-term credential mechanism), but they don't include USERNAME, REALM or NONCE. Iconic. ๐
Okay bestie, the Detailed Example is still giving us the big finale ๐ Here's the Send indication flow, where the client sends app data to Peer A ๐ฆ
TURN TURN Peer Peer
client server A B
|--- Send indication --------------->| | |
| Transaction-Id=0x1278E9ACA2711637EF7D3328 | |
| XOR-PEER-ADDRESS=192.0.2.150:32102 | |
| DONT-FRAGMENT | | |
| DATA=... | | |
| |-- UDP dgm ->| |
| | data=... | |
| | | |
| |<- UDP dgm --| |
| | data=... | |
|<-- Data indication ----------------| | |
| Transaction-Id=0x8231AE8F9242DA9FF287FEFF | |
| XOR-PEER-ADDRESS=192.0.2.150:32102 | |
| DATA=... | | |
So the client is now sending application data to Peer A using a Send indication ๐ธ Peer A's server-reflexive transport address goes in the XOR-PEER-ADDRESS attribute, and the app data (shown here as just "...") goes in the DATA attribute โจ The client is doing a form of path MTU discovery at the application layer, so it includes the DONT-FRAGMENT attribute to ask the server to set the DF bit in the UDP datagram going to the peer ๐ Indications can't be authenticated with the long-term credential mechanism of STUN, so there's no MESSAGE-INTEGRITY attribute in this message. If an app wants its data to be safe from being altered or forged, it has to integrity-protect the data itself at the application level ๐ฅ
When the server gets the Send indication, it pulls out the app data and sends it in a UDP datagram to Peer A. The relayed transport address is the source, and the DF bit is set like the client asked ๐ Heads up though: if the client never set up a permission for Peer A's server-reflexive IP address, the server would've quietly tossed the Send indication instead ๐
Peer A then replies with its own UDP datagram full of app data. That datagram goes to the relayed transport address on the server. When it arrives, the server makes a Data indication with the source of the UDP datagram in the XOR-PEER-ADDRESS attribute and the data from that datagram in the DATA attribute. Then the Data indication gets sent to the client ๐ง
TURN TURN Peer Peer
client server A B
|--- ChannelBind request ----------->| | |
| Transaction-Id=0x6490D3BC175AFF3D84513212 | |
| CHANNEL-NUMBER=0x4000 | | |
| XOR-PEER-ADDRESS=192.0.2.210:49191 | |
| USERNAME="George" | | |
| REALM="example.com" | | |
| NONCE="adl7W7PeDU4hKE72jdaQvbAMcr6h39sm" | |
| MESSAGE-INTEGRITY=... | | |
| | | |
|<-- ChannelBind success response ---| | |
| Transaction-Id=0x6490D3BC175AFF3D84513212 | |
| MESSAGE-INTEGRITY=... | | |
Now the client binds a channel to Peer B ๐ It picks a free channel number (0x4000) for the CHANNEL-NUMBER attribute and puts Peer B's transport address in XOR-PEER-ADDRESS. Same as before, the client reuses the username, realm, and nonce from its last request in the message ๐
When the server gets the request, it binds the channel number to the peer, installs a permission for Peer B's IP address, and then sends back a ChannelBind success response. Iconic behaviour ๐
TURN TURN Peer Peer
client server A B
|--- ChannelData ------------------->| | |
| Channel-number=0x4000 |--- UDP datagram --------->|
| Data=... | Data=... |
| | | |
| |<-- UDP datagram ----------|
| | Data=... | |
|<-- ChannelData --------------------| | |
| Channel-number=0x4000 | | |
| Data=... | | |
Next, the client sends a ChannelData message to the server with data for Peer B โจ A ChannelData message is not a STUN message, so it doesn't have a transaction id. It only has three fields: a channel number, data, and a data length. Here the channel number is 0x4000, the channel the client just bound to Peer B ๐ฆ When the server gets the ChannelData message, it checks that the channel is currently bound (it is!), then sends the data on to Peer B in a UDP datagram. It uses the relayed transport address as the source and 192.0.2.210:49191 (the XOR-PEER-ADDRESS value from the ChannelBind request) as the destination ๐
Later, Peer B sends a UDP datagram back to the relayed transport address. That makes the server send a ChannelData message to the client with the data from the datagram ๐ญ The server knows which client to send it to because of the relayed transport address the datagram arrived at, and it knows to use channel 0x4000 because that's the channel bound to 192.0.2.210:49191. Also worth noting: if no channel number had been bound to that address, the server would've used a Data indication instead ๐
TURN TURN Peer Peer
client server A B
|--- Refresh request --------------->| | |
| Transaction-Id=0x0864B3C27ADE9354B4312414 | |
| SOFTWARE="Example client 1.03" | | |
| USERNAME="George" | | |
| REALM="example.com" | | |
| NONCE="adl7W7PeDU4hKE72jdaQvbAMcr6h39sm" | |
| MESSAGE-INTEGRITY=... | | |
| | | |
|<-- Refresh error response ---------| | |
| Transaction-Id=0x0864B3C27ADE9354B4312414 | |
| SOFTWARE="Example server, version 1.17" | |
| ERROR-CODE=438 (Stale Nonce) | | |
| REALM="example.com" | | |
| NONCE="npSw1Xw239bBwGYhjNWgz2yH47sxB2j" | |
| | | |
|--- Refresh request --------------->| | |
| Transaction-Id=0x427BD3E625A85FC731DC4191 | |
| SOFTWARE="Example client 1.03" | | |
| USERNAME="George" | | |
| REALM="example.com" | | |
| NONCE="npSw1Xw239bBwGYhjNWgz2yH47sxB2j" | |
| MESSAGE-INTEGRITY=... | | |
| | | |
|<-- Refresh success response -------| | |
| Transaction-Id=0x427BD3E625A85FC731DC4191 | |
| SOFTWARE="Example server, version 1.17" | |
| LIFETIME=600 (10 minutes) | | |
Sometime before the 20 minute lifetime runs out, the client refreshes the allocation with a Refresh request ๐ฐ๏ธ Same as before, it includes the latest username, realm, and nonce. It also adds the SOFTWARE attribute, because the recommended practice is to always include it in Allocate and Refresh messages ๐ซถ When the server gets the Refresh request, it notices the nonce has expired, so it replies with a 438 (Stale Nonce) error and a fresh nonce value. The client then retries with the new nonce, and this second try gets accepted with a success response ๐ The client didn't include a LIFETIME attribute, so the server refreshes the allocation for the default lifetime of 10 minutes. You can see that in the LIFETIME attribute in the success response ๐
17. Security Considerations ๐
This section looks at the attacks that are possible in a TURN deployment, and how they get mitigated by mechanisms in the protocol or by recommended practices in the implementation ๐ค
Most attacks on TURN are stopped by making the server require requests to be authenticated, so this specification requires the use of authentication ๐ก๏ธ The mandatory-to-implement mechanism is the long-term credential mechanism of STUN. Other authentication mechanisms with equal or stronger security properties may be used too. Just make sure they can be invoked in an inter-operable way, because that matters a lot ๐ซ
17.1. Outsider Attacks
Outsider attacks are the ones where the attacker has no credentials in the system and is trying to disrupt the service the client or the server sees ๐
17.1.1. Obtaining Unauthorized Allocations
An attacker might want to grab allocations on a TURN server for all sorts of shady reasons. A TURN server gives a way to send and receive packets while hiding the client's actual IP address, which makes TURN servers a super attractive target for anyone wanting to mask their true identity ๐ฅท
An attacker might also just want to use a TURN server's services without paying for them. TURN services use up the provider's resources, so it's expected that their usage will come with a cost ๐ธ
These attacks are prevented by the long-term credential mechanism, which lets the TURN server figure out who the requestor is and whether they're allowed to get the allocation ๐
17.1.2. Offline Dictionary Attacks
The long-term credential mechanism used by TURN is open to offline dictionary attacks ๐ฌ An attacker who can eavesdrop on a message exchange between a client and server can work out the password by trying a bunch of candidate passwords and checking whether one is correct. This works when the passwords have low entropy, like a plain word from the dictionary. You can mitigate it by using strong passwords with large entropy ๐ช And in situations that need even stronger mitigation, TLS transport between the client and the server can be used ๐โจ
17.1.3. Faked Refreshes and Permissions ๐ โโ๏ธ
Okay so an attacker could try to hit an active allocation with a Refresh request that has an immediate expiration, which deletes it and wrecks the client's service. Not on my watch ๐ค Authenticating refreshes stops that. The same goes for an attacker trying to send CreatePermission requests to make permissions toward undesirable destinations, because authentication blocks that too. The reasons someone would pull this kind of move are covered in Section 17.2. ๐
17.1.4. Fake Data ๐ซฃ
This one is giving sneaky. An attacker might want to send data to the client or the peer while pretending it came from the peer or the client, respectively. To pull that off, they can send the client a faked Data Indication or ChannelData message, or send the TURN server a faked Send Indication or ChannelData message. ๐
Indications and ChannelData messages aren't authenticated, so TURN doesn't prevent this attack. Ngl though, this kind of attack is already a thing in regular IP-based communications, and TURN doesn't make it much worse. ๐ธ Picture a normal, non-TURN IP session between hosts A and B. An attacker can send packets to B that look like they came from A by sending packets toward A with a spoofed IP address of B. That requires the attacker to know the IP addresses of A and B. With TURN, someone who wants to send packets toward a client using a Data indication needs to know its IP address and port, the IP address and port of the TURN server, and the IP address and port of the peer (to include in the XOR-PEER-ADDRESS attribute). To send a fake ChannelData message to a client, an attacker needs the IP address and port of the client, the IP address and port of the TURN server, and the channel number. Honestly, that particular combination is a little more guessable than in the non-TURN case. ๐
The proper fix for these is application-layer authentication. For real-time traffic, using SRTP [RFC3711] stops these attacks. ๐
Here's the spicy part. Sometimes the TURN server sits somewhere in the network where it can send to hosts that the client can't reach directly. For example, the server might be behind a firewall that lets packets from outside reach the server but not other hosts behind that firewall. In that setup, an attacker could send the server a Send indication with an XOR-PEER-ADDRESS attribute holding the transport address of one of those other hosts behind the firewall. If the server allowed relaying to arbitrary peers, the attacker would then have a way to go after arbitrary hosts behind the firewall. ๐ฑ
To stop this, TURN requires that the client establish a permission to a host before sending it data. So an attacker can only go after hosts the client is already talking to, unless they're able to create authenticated requests. โจ On top of that, the server administrator can configure the server to restrict the range of IP addresses and ports it will relay data to. And if they want to go even further for security, the server administrator can require that the client use TLS for all communication between the client and the server. ๐
17.1.5. Impersonating a Server ๐ญ
When a client learns a relayed address from a TURN server, it uses that relayed address in application protocols to receive traffic. So an attacker who wants to intercept or redirect that traffic might try to impersonate a TURN server and hand the client a faked relayed address. Villain arc, honestly. ๐ค
The long-term credential mechanism prevents this attack. It gives integrity protection for responses, and it also verifies that they came from the server. An attacker also can't replay old server responses, because the transaction id in the STUN header stops that. Replay attacks are further thwarted by frequent changes to the nonce value. ๐ง
17.1.6. Eavesdropping Traffic ๐
TURN is mainly about authentication and message integrity. Confidentiality is more of a secondary concern, since TURN control messages don't include anything super sensitive. The main thing in them is the IP address of the peer. If it matters to keep an eavesdropper on a TURN connection from learning that, TURN can be run over TLS. ๐
Confidentiality for the application data relayed by TURN is best handled by the application protocol itself. Running TURN over TLS doesn't protect application data between the server and the peer, so that's not a fix for it. If confidentiality of application data matters, the application should encrypt or otherwise protect its data. For example, for real-time media, SRTP can provide that confidentiality. ๐ง
17.1.7. TURN Loop Attack ๐
Okay, this one is wild. An attacker might try to make data packets loop forever between two TURN servers. Here's how it goes. First, the attacker sends an Allocate request to server A, using the source address of server B. Server A sends its response to server B, and for the attack to work, the attacker has to be able to view or guess the contents of that response so they can learn the allocated relayed transport address. Next, the attacker sends an Allocate request to server B, using the source address of server A. Again, the attacker needs to view or guess the response contents so they can learn the allocated relayed transport address. Using that same spoofed source address trick, the attacker then binds a channel number on server A to the relayed transport address on server B, and similarly binds the same channel number on server B to the relayed transport address on server A. Finally, the attacker sends a ChannelData message to server A. ๐ตโ๐ซ
The result is a data packet that loops from the relayed transport address on server A to the relayed transport address on server B, then from server B's transport address to server A's transport address, and then around the loop again and again. ๐
Good news though, this attack gets mitigated. By requiring all requests to be authenticated and/or by randomizing the port number allocated for the relayed transport address, the server forces the attacker to either intercept or view responses that are sent to a third party (in this case, the other server). That's how the attacker gets the credentials to authenticate requests and learn the relayed transport address. Without one of those two measures, an attacker can guess the contents of the responses without needing to see them, which makes the attack way easier to pull off. ๐ Also, by requiring authenticated requests, the server forces the attacker to have credentials the server accepts. That turns this from an outsider attack into an insider attack and lets the attack be traced back to the client that started it. ๐ต๏ธโโ๏ธ
The attack can be further mitigated by imposing a per-username limit on the bandwidth used to relay data by allocations owned by that username. That limits the impact on other allocations. Even more mitigation comes from decrementing the TTL when relaying data packets, if the underlying OS allows it. ๐ฆ
17.2. Firewall Considerations ๐งฑ
A key security consideration is that TURN should not weaken the protections that firewalls deployed between a client and a TURN server provide. TURN servers are expected to often sit on the public Internet, while clients may often be inside enterprise networks with corporate firewalls. If TURN servers gave a 'backdoor' into the enterprise, these firewalls would block TURN. ๐ซ
So TURN servers emulate the behavior of NAT devices that implement address-dependent filtering [RFC4787], which is also a property common in many firewalls. When a NAT or firewall does this, packets from an outside IP address are only allowed to reach an internal IP address and port if that internal IP address and port recently sent a packet to that outside IP address. TURN servers introduce the concept of permissions, which give exactly the same behavior on the TURN server. An attacker can't send a packet to a TURN server and expect it to be relayed toward the client, unless the client tried to contact the attacker first. ๐ โโ๏ธโจ
Some firewalls have policies that are even stricter than address-dependent filtering. Firewalls can also be configured with address- and port-dependent filtering, or they can be set to disallow inbound traffic entirely. In these cases, if a client is allowed to connect to the TURN server, communications to the client end up less restricted than what the firewall would normally allow. Hmm, that's a whole plot twist. ๐ฌ
17.2.1. Faked Permissions ๐๏ธ
With firewalls and NAT devices, permissions are granted implicitly when a packet travels from the inside of the network toward the outside peer. So by definition, a permission can only be created by an entity inside the firewall or NAT. With TURN, this restriction no longer holds. The TURN server sits outside the firewall, so an attacker outside the firewall can now send a message to the TURN server and try to create a permission for itself. Bold move. ๐ค
This attack is prevented because all messages that create permissions (that is, ChannelBind and CreatePermission) are authenticated. ๐
17.2.2. Blacklisted IP Addresses ๐ท
Many firewalls can be configured with blacklists that stop a client behind the firewall from sending packets to, or receiving packets from, ranges of blacklisted IP addresses. The firewall does this by inspecting the source and destination addresses of packets entering and exiting, respectively. ๐ง
This feature is also present in TURN, since TURN servers are allowed to arbitrarily restrict the range of peer addresses they will relay to. ๐ซถ
17.2.3. Running Servers on Well-Known Ports ๐
A malicious client behind a firewall might try to connect to a TURN server and get an allocation that it then uses to run a server. For example, a client might try to run a DNS server or an FTP server. Nice try. ๐
It's not possible in TURN. A TURN server will never accept traffic from a peer for which the client hasn't installed a permission. So peers can't just connect to the allocated port to get the service. ๐ช๐
17.3. Insider Attacks ๐ต๏ธโโ๏ธ
In insider attacks, a client has legitimate credentials but breaks the trust relationship that comes with them. These attacks can't be prevented by cryptographic means, so they need to be considered in the design of the protocol itself. ๐ง
17.3.1. DoS against TURN Server ๐
A client that wants to disrupt service to other clients might obtain an allocation and then flood it with traffic, trying to swamp the server so it can't serve other legitimate clients. This is mitigated by the recommendation that the server limit the amount of bandwidth it will relay for a given username. That won't stop a client from sending a huge amount of traffic, but it lets the server immediately discard the excess. ๐๏ธ
Also, each allocation uses a port number on the IP address of the TURN server, so the number of allocations on a server is finite. An attacker might try to eat all of them by requesting a large number of allocations. This is prevented by the recommendation that the server impose a limit on the number of allocations active at a time for a given username. ๐
17.3.2. Anonymous Relaying of Malicious Traffic ๐ฆ
okay so TURN servers give you a lil bit of anonymity ๐ A client can send data to its peers without revealing its own IP address. That means TURN servers may become super attractive to attackers who want to hit a target without getting caught ๐ It's even possible to chain several TURN servers together, so there can be any number of relays before a target gets a packet ๐
Admins who are worried about this can keep logs that capture the client's actual source IP and port, and maybe even every permission that client installs ๐ If it ever turns out an attack is being relayed through a TURN server, that log trail lets them do forensic tracing to find the original source ๐
17.3.3. Manipulating Other Allocations ๐ญ
Another sneaky move: an attacker might try to disrupt service for other TURN users by sending Refresh requests or CreatePermission requests that (through source address spoofing) look like they're coming from someone else ๐ TURN stops this by requiring that the credentials used in CreatePermission, Refresh, and ChannelBind messages match the ones used to create the initial allocation. So the attacker's fake requests get rejected ๐ซ
17.4. Other Considerations ๐
Any relay addresses learned through an Allocate request won't operate properly with IPsec Authentication Header (AH) [RFC4302] in transport or tunnel mode ๐ญ However, tunnel-mode IPsec Encapsulating Security Payload (ESP) [RFC4303] should still operate โจ
18. IANA Considerations ๐ธ
Since TURN is an extension to STUN [RFC5389], the methods, attributes, and error codes defined in this specification are new methods, attributes, and error codes for STUN. IANA has added these new protocol elements to the IANA registry of STUN protocol elements.
The codepoints for the new STUN methods defined in this specification are listed in Section 13.
The codepoints for the new STUN attributes defined in this specification are listed in Section 14.
The codepoints for the new STUN error codes defined in this specification are listed in Section 15.
IANA has allocated the SRV service name of "turn" for TURN over UDP or TCP, and the service name of "turns" for TURN over TLS.
IANA has created a registry for TURN channel numbers, initially populated as follows:
0x0000 through 0x3FFF: Reserved and not available for use, since
they conflict with the STUN header.
0x4000 through 0x7FFF: A TURN implementation is free to use
channel numbers in this range.
0x8000 through 0xFFFF: Unassigned.
Any change to this registry must be made through an IETF Standards Action ๐
19. IAB Considerations ๐ง
The IAB has studied the problem of "Unilateral Self Address Fixing" (UNSAF), which is the general process by which a client tries to figure out its address in another realm on the other side of a NAT through a collaborative protocol-reflection mechanism [RFC3424]. TURN is an example of a protocol that does this kind of job. The IAB has mandated that any protocols developed for this purpose document a specific set of considerations, and the responses for TURN are written up in this section ๐
Consideration 1: Precise definition of a specific, limited-scope problem that is to be solved with the UNSAF proposal. A short-term fix should not be generalized to solve other problems. Such generalizations lead to the prolonged dependence on and usage of the supposed short-term fix -- meaning that it is no longer accurate to call it "short-term".
Response: TURN is a protocol for communication between a relay (= TURN server) and its client. It lets a client that's behind a NAT obtain and use a public IP address on the relay. As a convenience to the client, TURN also lets the client determine its server-reflexive transport address ๐โโ๏ธ
Consideration 2: Description of an exit strategy/transition plan. The better short-term fixes are the ones that will naturally see less and less use as the appropriate technology is deployed.
Response: TURN will no longer be needed once there are no longer any NATs. Unfortunately, as of the date of publication of this document, it no longer seems very likely that NATs will go away any time soon ๐ฉ However, the need for TURN will also decrease as the number of NATs with the mapping property of Endpoint-Independent Mapping [RFC4787] increases ๐
Consideration 3: Discussion of specific issues that may render systems more "brittle". For example, approaches that involve using data at multiple network layers create more dependencies, increase debugging challenges, and make it harder to transition.
Response: TURN is "brittle" in that it requires the NAT bindings between the client and the server to be maintained unchanged for the lifetime of the allocation. This is typically done using keep-alives. If this is not done, then the client will lose its allocation and can no longer exchange data with its peers ๐
Consideration 4: Identify requirements for longer-term, sound technical solutions; contribute to the process of finding the right longer-term solution.
Response: The need for TURN will be reduced once NATs implement the recommendations for NAT UDP behavior documented in [RFC4787]. Applications are also strongly urged to use ICE [RFC5245] to communicate with peers. Though ICE uses TURN, it does so only as a last resort, and uses it in a controlled manner ๐ซถ
Consideration 5: Discussion of the impact of the noted practical issues with existing deployed NATs and experience reports.
Response: Some NATs deployed today exhibit a mapping behavior other than Endpoint-Independent mapping. These NATs are difficult to work with, since they make it difficult or impossible for protocols like ICE to use server-reflexive transport addresses on those NATs. A client behind such a NAT is often forced to use a relay protocol like TURN because "UDP hole punching" techniques [RFC5128] do not work ๐ฅฒ
20. Acknowledgements ๐
The authors would like to thank the various participants in the BEHAVE working group for their many comments on this document. Marc Petit-Huguenin, Remi Denis-Courmont, Jason Fischl, Derek MacDonald, Scott Godin, Cullen Jennings, Lars Eggert, Magnus Westerlund, Benny Prijono, and Eric Rescorla have been particularly helpful. Eric suggested the channel allocation mechanism, Cullen suggested an earlier version of the EVEN-PORT mechanism, and Marc spent many hours implementing the preliminary versions to look for problems ๐ Christian Huitema was an early contributor to this document and was a co-author on the first few versions. Finally, the authors would like to thank Dan Wing for both his contributions to the text and his huge help in restarting progress on this document after work had stalled ๐ฅน
21. References ๐
21.1. Normative References
[RFC5389] Rosenberg, J., Mahy, R., Matthews, P., and D.
Wing, "Session Traversal Utilities for NAT
(STUN)", RFC 5389, October 2008.
[RFC2119] Bradner, S., "Key words for use in RFCs to
Indicate Requirement Levels", BCP 14, RFC 2119,
March 1997.
[RFC2474] Nichols, K., Blake, S., Baker, F., and D. Black,
"Definition of the Differentiated Services Field
(DS Field) in the IPv4 and IPv6 Headers",
RFC 2474, December 1998.
[RFC3168] Ramakrishnan, K., Floyd, S., and D. Black, "The
Addition of Explicit Congestion Notification
(ECN) to IP", RFC 3168, September 2001.
[RFC1122] Braden, R., "Requirements for Internet Hosts -
Communication Layers", STD 3, RFC 1122,
October 1989.
21.2. Informative References ๐
okay so these are the extra reads that aren't strictly required but are super useful for context, bestie ๐โจ every entry is listed below exactly as it's written, so nothing gets lost ๐
[RFC1191] Mogul, J. and S. Deering, "Path MTU discovery",
RFC 1191, November 1990.
[RFC0791] Postel, J., "Internet Protocol", STD 5, RFC 791,
September 1981.
[RFC1918] Rekhter, Y., Moskowitz, R., Karrenberg, D.,
Groot, G., and E. Lear, "Address Allocation for
Private Internets", BCP 5, RFC 1918,
February 1996.
[RFC3424] Daigle, L. and IAB, "IAB Considerations for
UNilateral Self-Address Fixing (UNSAF) Across
Network Address Translation", RFC 3424,
November 2002.
[RFC4787] Audet, F. and C. Jennings, "Network Address
Translation (NAT) Behavioral Requirements for
Unicast UDP", BCP 127, RFC 4787, January 2007.
[RFC5245] Rosenberg, J., "Interactive Connectivity
Establishment (ICE): A Protocol for Network
Address Translator (NAT) Traversal for
Offer/Answer Protocols", RFC 5245, April 2010.
[TURN-TCP] Perreault, S. and J. Rosenberg, "Traversal Using
Relays around NAT (TURN) Extensions for TCP
Allocations", Work in Progress, March 2010.
[TURN-IPv6] Perreault, S., Camarillo, G., and O. Novo,
"Traversal Using Relays around NAT (TURN)
Extension for IPv6", Work in Progress, March
2010.
[TSVWG-PORT] Larsen, M. and F. Gont, "Port Randomization",
Work in Progress, April 2010.
[RFC5128] Srisuresh, P., Ford, B., and D. Kegel, "State of
Peer-to-Peer (P2P) Communication across Network
Address Translators (NATs)", RFC 5128,
March 2008.
[RFC1928] Leech, M., Ganis, M., Lee, Y., Kuris, R.,
Koblas, D., and L. Jones, "SOCKS Protocol
Version 5", RFC 1928, March 1996.
[RFC3550] Schulzrinne, H., Casner, S., Frederick, R., and
V. Jacobson, "RTP: A Transport Protocol for
Real-Time Applications", STD 64, RFC 3550,
July 2003.
[RFC3711] Baugher, M., McGrew, D., Naslund, M., Carrara,
E., and K. Norrman, "The Secure Real-time
Transport Protocol (SRTP)", RFC 3711,
March 2004.
[RFC4302] Kent, S., "IP Authentication Header", RFC 4302,
December 2005.
[RFC4303] Kent, S., "IP Encapsulating Security Payload
(ESP)", RFC 4303, December 2005.
[RFC4821] Mathis, M. and J. Heffner, "Packetization Layer
Path MTU Discovery", RFC 4821, March 2007.
[RFC3261] Rosenberg, J., Schulzrinne, H., Camarillo, G.,
Johnston, A., Peterson, J., Sparks, R., Handley,
M., and E. Schooler, "SIP: Session Initiation
Protocol", RFC 3261, June 2002.
[MMUSIC-ICE-NONSIP] Rosenberg, J., "Guidelines for Usage of
Interactive Connectivity Establishment (ICE) by
non Session Initiation Protocol (SIP)
Protocols", Work in Progress, July 2008.
[RFC4086] Eastlake, D., Schiller, J., and S. Crocker,
"Randomness Requirements for Security", BCP 106,
RFC 4086, June 2005.
[Frag-Harmful] Kent and Mogul, "Fragmentation Considered
Harmful". Proc. SIGCOMM '87, vol. 17, No. 5,
October 1987
[Port-Numbers] "IANA Port Numbers Registry",
<http://www.iana.org>.
[Protocol-Numbers] "IANA Protocol Numbers Registry", 2005,
<http://www.iana.org>.
Authors' Addresses ๐
the squad behind this spec, with their contact details ๐ซถ
Rohan Mahy
Unaffiliated
EMail: rohan@ekabal.com
Philip Matthews
Alcatel-Lucent
600 March Road
Ottawa, Ontario
Canada
EMail: philip_matthews@magma.ca
Jonathan Rosenberg
jdrosen.net
Monmouth, NJ
USA
EMail: jdrosen@jdrosen.net
URI: http://www.jdrosen.net
๐ฆ where this page comes from
- ietf: rfc5766 2010-04
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms