RFC 5245
Interactive Connectivity Establishment (ICE): A Protocol for Network Address Translator (NAT) Traversal for Offer/Answer Protocols
Interactive Connectivity Establishment (ICE) ๐ฆ
bestie, this is RFC 5245 ๐ It's a Standards Track doc from the IETF, written by J. Rosenberg (jdrosen.net), and it's dated April 2010. It's officially titled "Interactive Connectivity Establishment (ICE): A Protocol for Network Address Translator (NAT) Traversal for Offer/Answer Protocols" ๐ It obsoletes RFC 4091 and RFC 4092, so if you're reading those, this is the newer version. The ISSN is 2070-1721 โจ
Abstract ๐ธ
okay so this doc describes a way for UDP-based multimedia sessions to get through a Network Address Translator (NAT) ๐ง Those sessions use the offer/answer model, and the protocol is called Interactive Connectivity Establishment (ICE) ๐ ICE is lowkey a team player: it uses the Session Traversal Utilities for NAT (STUN) protocol and its extension, Traversal Using Relay NAT (TURN). Any protocol that runs on offer/answer can use ICE, and the example they give is the Session Initiation Protocol (SIP) ๐ซถ
Status of This Memo ๐ฆ
this is an Internet Standards Track document ๐ฅ
it's a product of the Internet Engineering Task Force (IETF), so it represents what the IETF community agreed on. it got public review and was approved for publication by the Internet Engineering Steering Group (IESG) ๐ for more on Internet Standards, check Section 2 of RFC 5741 ๐
for the current status, any errata, and how to send feedback, head to http://www.rfc-editor.org/info/rfc5245 ๐
Copyright Notice ๐
Copyright (c) 2010 IETF Trust and the persons identified as the document authors. All rights reserved. ๐
this doc 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 it was published. please read those carefully, because they spell out your rights and restrictions for this document ๐ Code Components pulled out of this doc must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, as described in the Simplified BSD License โญ
Table of Contents ๐ง
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 6
2. Overview of ICE . . . . . . . . . . . . . . . . . . . . . . . 7
2.1. Gathering Candidate Addresses . . . . . . . . . . . . . . 9
2.2. Connectivity Checks . . . . . . . . . . . . . . . . . . . 11
2.3. Sorting Candidates . . . . . . . . . . . . . . . . . . . 12
2.4. Frozen Candidates . . . . . . . . . . . . . . . . . . . . 13
2.5. Security for Checks . . . . . . . . . . . . . . . . . . . 14
2.6. Concluding ICE . . . . . . . . . . . . . . . . . . . . . 14
2.7. Lite Implementations . . . . . . . . . . . . . . . . . . 16
3. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 16
4. Sending the Initial Offer . . . . . . . . . . . . . . . . . . 19
4.1. Full Implementation Requirements . . . . . . . . . . . . 19
4.1.1. Gathering Candidates . . . . . . . . . . . . . . . . 19
4.1.1.1. Host Candidates . . . . . . . . . . . . . . . . . 20
4.1.1.2. Server Reflexive and Relayed Candidates . . . . . 20
4.1.1.3. Computing Foundations . . . . . . . . . . . . . . 22
4.1.1.4. Keeping Candidates Alive . . . . . . . . . . . . 22
4.1.2. Prioritizing Candidates . . . . . . . . . . . . . . . 22
4.1.2.1. Recommended Formula . . . . . . . . . . . . . . . 23
4.1.2.2. Guidelines for Choosing Type and Local
Preferences . . . . . . . . . . . . . . . . . . . 23
4.1.3. Eliminating Redundant Candidates . . . . . . . . . . 25
4.1.4. Choosing Default Candidates . . . . . . . . . . . . . 25
4.2. Lite Implementation Requirements . . . . . . . . . . . . 25
4.3. Encoding the SDP . . . . . . . . . . . . . . . . . . . . 26
5. Receiving the Initial Offer . . . . . . . . . . . . . . . . . 28
5.1. Verifying ICE Support . . . . . . . . . . . . . . . . . . 28
5.2. Determining Role . . . . . . . . . . . . . . . . . . . . 29
5.3. Gathering Candidates . . . . . . . . . . . . . . . . . . 30
5.4. Prioritizing Candidates . . . . . . . . . . . . . . . . . 30
5.5. Choosing Default Candidates . . . . . . . . . . . . . . . 31
5.6. Encoding the SDP . . . . . . . . . . . . . . . . . . . . 31
5.7. Forming the Check Lists . . . . . . . . . . . . . . . . . 31
5.7.1. Forming Candidate Pairs . . . . . . . . . . . . . . . 31
5.7.2. Computing Pair Priority and Ordering Pairs . . . . . 34
5.7.3. Pruning the Pairs . . . . . . . . . . . . . . . . . . 34
5.7.4. Computing States . . . . . . . . . . . . . . . . . . 34
5.8. Scheduling Checks . . . . . . . . . . . . . . . . . . . . 37
6. Receipt of the Initial Answer . . . . . . . . . . . . . . . . 39
6.1. Verifying ICE Support . . . . . . . . . . . . . . . . . . 39
6.2. Determining Role . . . . . . . . . . . . . . . . . . . . 39
6.3. Forming the Check List . . . . . . . . . . . . . . . . . 40
6.4. Performing Ordinary Checks . . . . . . . . . . . . . . . 40
7. Performing Connectivity Checks . . . . . . . . . . . . . . . 40
7.1. STUN Client Procedures . . . . . . . . . . . . . . . . . 40
7.1.1. Creating Permissions for Relayed Candidates . . . . . 40
7.1.2. Sending the Request . . . . . . . . . . . . . . . . . 40
7.1.2.1. PRIORITY and USE-CANDIDATE . . . . . . . . . . . 41
7.1.2.2. ICE-CONTROLLED and ICE-CONTROLLING . . . . . . . 41
7.1.2.3. Forming Credentials . . . . . . . . . . . . . . . 41
7.1.2.4. DiffServ Treatment . . . . . . . . . . . . . . . 42
7.1.3. Processing the Response . . . . . . . . . . . . . . . 42
7.1.3.1. Failure Cases . . . . . . . . . . . . . . . . . . 42
7.1.3.2. Success Cases . . . . . . . . . . . . . . . . . . 43
7.1.3.2.1. Discovering Peer Reflexive Candidates . . . . 43
7.1.3.2.2. Constructing a Valid Pair . . . . . . . . . . 44
7.1.3.2.3. Updating Pair States . . . . . . . . . . . . 45
7.1.3.2.4. Updating the Nominated Flag . . . . . . . . . 46
7.1.3.3. Check List and Timer State Updates . . . . . . . 46
7.2. STUN Server Procedures . . . . . . . . . . . . . . . . . 46
7.2.1. Additional Procedures for Full Implementations . . . 47
7.2.1.1. Detecting and Repairing Role Conflicts . . . . . 47
7.2.1.2. Computing Mapped Address . . . . . . . . . . . . 48
7.2.1.3. Learning Peer Reflexive Candidates . . . . . . . 49
7.2.1.4. Triggered Checks . . . . . . . . . . . . . . . . 49
7.2.1.5. Updating the Nominated Flag . . . . . . . . . . . 50
7.2.2. Additional Procedures for Lite Implementations . . . 51
8. Concluding ICE Processing . . . . . . . . . . . . . . . . . . 51
8.1. Procedures for Full Implementations . . . . . . . . . . . 51
8.1.1. Nominating Pairs . . . . . . . . . . . . . . . . . . 51
8.1.1.1. Regular Nomination . . . . . . . . . . . . . . . 52
8.1.1.2. Aggressive Nomination . . . . . . . . . . . . . . 52
8.1.2. Updating States . . . . . . . . . . . . . . . . . . . 53
8.2. Procedures for Lite Implementations . . . . . . . . . . . 54
8.2.1. Peer Is Full . . . . . . . . . . . . . . . . . . . . 54
8.2.2. Peer Is Lite . . . . . . . . . . . . . . . . . . . . 55
8.3. Freeing Candidates . . . . . . . . . . . . . . . . . . . 56
8.3.1. Full Implementation Procedures . . . . . . . . . . . 56
8.3.2. Lite Implementation Procedures . . . . . . . . . . . 56
1. Introduction โจ
okay so bestie, RFC 3264 [RFC3264] defines a two-phase exchange of Session Description Protocol (SDP) messages [RFC4566] to set up multimedia sessions ๐ this offer/answer mechanism is what protocols like the Session Initiation Protocol (SIP) [RFC3261] use ๐
Here's the tea ๐ซ: protocols that use offer/answer are lowkey hard to run through Network Address Translators (NATs) ๐ญ they're literally built to set up a flow of media packets, so they tend to stuff the IP addresses and ports of media sources and sinks right into their messages, and that's known to be a problem through NAT [RFC3235]. they also want media to flow directly between participants, with no application layer middleman in the way โจ that's how you get less media latency, less packet loss, and lower operational costs for the app ๐ but doing that through NAT is so hard. why exactly? that's a whole story that's beyond the scope of this spec, so we're skipping it ๐
There have been a bunch of solutions for getting these protocols through NAT ๐ฆ like Application Layer Gateways (ALGs), the Middlebox Control Protocol [RFC3303], the original Simple Traversal of UDP Through NAT (STUN) [RFC3489] spec, and Realm Specific IP [RFC3102] [RFC3103], plus session description extensions needed to make them work, like the SDP [RFC4566] attribute for the Real Time Control Protocol (RTCP) [RFC3605]. But ngl, every single one of these has pros and cons ๐ which make each one great in some network topologies and a total flop in others. so admins and implementors end up making assumptions about the topologies of the networks their solutions will land in ๐ฅบ that adds complexity and brittleness to the whole system. what we actually need is one solution that's flexible enough to work well in every situation ๐
This spec defines Interactive Connectivity Establishment (ICE) ๐ as a NAT traversal technique for UDP-based media streams, set up through the offer/answer model. ICE can also be extended to handle other transport protocols, like TCP [ICE-TCP], but UDP is the main focus here ๐ฅ ICE is an extension to the offer/answer model. it works by putting a whole bunch of IP addresses and ports into SDP offers and answers, and then testing which ones actually connect with peer-to-peer connectivity checks ๐ซถ those checks, along with the IP addresses and ports in the SDP, use the revised STUN spec [RFC5389], now renamed to Session Traversal Utilities for NAT. the new name and spec are a glow-up ๐ฆ because STUN is now a tool that gets used alongside other NAT traversal techniques (ICE, namely), rather than being a standalone NAT traversal solution like the original was. ICE also uses Traversal Using Relays around NAT (TURN) [RFC5766], which is an extension to STUN ๐ง
And here's the iconic part ๐โจ: since ICE swaps out a whole multiplicity of IP addresses and ports for each media stream, it also handles address selection for multihomed and dual-stack hosts ๐ which is why it deprecates RFC 4091 [RFC4091] and [RFC4092] ๐ญ bye bye, old drafts ๐
2. Overview of ICE โจ
okay so bestie, picture this: two endpoints (called AGENTS in RFC 3264 terms) want to talk to each other ๐ฆ They can chat indirectly through some signaling protocol like SIP, and while they're chatting they do an offer/answer exchange of SDP [RFC3264] messages. ngl ICE is NOT meant to handle NAT traversal for SIP itself. That's handled by a different mechanism [RFC5626] ๐ At the very start, the agents have zero idea what their network setup looks like. Like, they might be behind a NAT (or several NATs stacked up ๐ญ), or they might not. ICE is the thing that helps them figure out enough about their topology to find one or more paths that actually work ๐ซถ
Figure 1 shows the typical setup ๐ The two endpoints are called L and R (left and right, which helps you picture the call flow). Both of them are behind their own NATs, even if they don't know it ๐ The NAT type and its properties are also a total mystery. L and R can do an offer/answer exchange of SDP messages to set up a media session between them, and this usually happens through a SIP server ๐ง
+-------+
| SIP |
+-------+ | Srvr | +-------+
| STUN | | | | STUN |
| Srvr | +-------+ | Srvr |
| | / \ | |
+-------+ / \ +-------+
/ \
/ \
/ \
/ \
/ <- Signaling -> \
/ \
/ \
+--------+ +--------+
| NAT | | NAT |
+--------+ +--------+
/ \
/ \
/ \
+-------+ +-------+
| Agent | | Agent |
| L | | R |
| | | |
+-------+ +-------+
Figure 1: ICE Deployment Scenario
So the whole scene is: a SIP server, NATs, agents, and ICE usually teaming up with STUN or TURN servers in the network ๐ Each agent can have its own STUN or TURN server, or they can share one. Your call ๐ธ
The basic idea behind ICE is giving every agent a bunch of CANDIDATE TRANSPORT ADDRESSES. These are combos of IP address and port for a particular transport protocol, which in this spec is always UDP)) ๐ Here's what that list might look like:
- A transport address on a directly attached network interface
- A translated transport address on the public side of a NAT (a "server reflexive" address)
- A transport address allocated from a TURN server (a "relayed address").
Technically, any of L's candidates could talk to any of R's candidates. In practice though, lots of those pairs won't work ๐ For example, if L and R are both behind NATs, their directly attached interface addresses are very unlikely to reach each other directly. (This is literally why ICE exists, bestie! ๐ญ) The whole purpose of ICE is to figure out which pairs of addresses will work. And the way it does that is by systematically trying every possible pair, in a carefully sorted order, until it finds one or more that work โจ
2.1. Gathering Candidate Addresses
Before ICE can run, an agent has to identify ALL of its address candidates ๐ A CANDIDATE is a transport address (IP address plus port, for a particular transport protocol, and only UDP is specified here). This document defines three types of candidates. Some come from physical or logical network interfaces, and others are discovered through STUN and TURN ๐ The obvious one is a transport address pulled straight from a local interface. That's called a HOST CANDIDATE ๐ The local interface could be ethernet or WiFi, or it could come through a tunnel mechanism like a Virtual Private Network (VPN) or Mobile IP (MIP). Either way, the interface looks like a local interface to the agent, and ports (and therefore candidates) can be allocated from it ๐ฆ
If an agent is multihomed, it gets a candidate from each IP address. Which of those addresses the PEER (the other agent in the session) can reach depends on where the peer sits on the IP network relative to the agent, so the agent may be reachable through one or more of them ๐ซถ Picture an agent with a local IP address on a private net 10 network (I1), plus a second connection to the public Internet (I2). A candidate from I1 is directly reachable by a peer on that same private net 10 network. A candidate from I2 is directly reachable by a peer on the public Internet. So instead of guessing which IP address will work before sending an offer, the offering agent just includes both candidates in its offer. No guessing needed, periodt ๐
Next up, the agent uses STUN or TURN to grab even more candidates. These come in two flavors: translated addresses on the public side of a NAT (SERVER REFLEXIVE CANDIDATES) and addresses on TURN servers (RELAYED CANDIDATES) ๐ธ When TURN servers are in use, both types of candidates come from the TURN server. If only STUN servers are in use, the agent only gets server reflexive candidates from them. Figure 2 shows how these candidates relate to the host candidate. In this figure, both types are discovered using TURN. (In the figure, X:x means IP address X and UDP port x ๐ง)
To Internet
|
|
| /------------ Relayed
Y:y | / Address
+--------+
| |
| TURN |
| Server |
| |
+--------+
|
|
| /------------ Server
X1':x1'|/ Reflexive
+------------+ Address
| NAT |
+------------+
|
| /------------ Local
X:x |/ Address
+--------+
| |
| Agent |
| |
+--------+
Figure 2: Candidate Relationships
Here's the chain of events ๐ When the agent sends the TURN Allocate request from IP address and port X:x, the NAT (if there is one) creates a binding X1':x1', which maps this server reflexive candidate to the host candidate X:x. Packets going out from the host candidate get translated by the NAT into the server reflexive candidate. Packets coming in to the server reflexive candidate get translated back to the host candidate and forwarded to the agent. The host candidate linked to a particular server reflexive candidate is called the BASE ๐
Note: "Base" refers to the address an agent sends from for a particular candidate. Thus, as a degenerate case host candidates also have a base, but it's the same as the host candidate.
Now, if there are multiple NATs between the agent and the TURN server, the TURN request creates a binding on each NAT. But the agent only discovers the outermost server reflexive candidate (the one closest to the TURN server) ๐ If the agent isn't behind a NAT at all, the base candidate is the same as the server reflexive candidate. Then the server reflexive candidate is redundant and gets eliminated ๐
The Allocate request then arrives at the TURN server. The TURN server allocates a port y from its local IP address Y and generates an Allocate response that tells the agent about this relayed candidate ๐ The TURN server also tells the agent about the server reflexive candidate X1':x1' by copying the source transport address of the Allocate request into the Allocate response. The TURN server acts as a packet relay, forwarding traffic between L and R. So for traffic to reach L, R sends to the TURN server at Y:y, the TURN server forwards it to X1':x1', the NAT maps it to X:x, and it gets delivered to L ๐ฆ
When only STUN servers are in use, the agent sends a STUN Binding request [RFC5389] to its STUN server. The STUN server tells the agent about the server reflexive candidate X1':x1' by copying the source transport address of the Binding request into the Binding response โจ
2.2. Connectivity Checks โจ
okay so once L has gathered ALL of its candidates ๐ , it sorts them from highest priority to lowest and sends the whole list to R over the signaling channel. The candidates ride along in attributes inside the SDP offer. When R gets the offer, R runs the same gathering process and sends back its own list. By the end, each agent has a complete list of its own candidates AND its peer's candidates ๐ซถ. Then they pair everything up, and those pairs are called CANDIDATE PAIRS. To figure out which pairs actually work, each agent schedules a bunch of CHECKS. Each check is a STUN request/response transaction. The client runs it on one candidate pair by sending a STUN request from the local candidate to the remote candidate ๐ฆ.
The basic idea of the connectivity checks is simple, bestie:
- Sort the candidate pairs in priority order.
- Send checks on each candidate pair in priority order.
- Acknowledge checks received from the other agent.
When both agents check the same pair, you get a 4-way handshake, and it's giving whole choreography ๐ฉฐ:
L R
- -
STUN request -> \ L's
<- STUN response / check
<- STUN request \ R's
STUN response -> / check
Figure 3: Basic Connectivity Check
Here's the important part ๐ธ. The STUN requests go to and from the EXACT same IP addresses and ports that will carry the media (like RTP and RTCP). That means agents have to tell STUN apart from RTP/RTCP by looking at the contents of the packets, NOT by which port they arrived on. Lucky for us, this is easy to do, especially for RTP and RTCP โจ.
Since a STUN Binding request is what runs the connectivity check, the STUN Binding response will include the agent's translated transport address on the public side of any NATs between it and its peer. If that address is different from the candidates the agent already knows about, it's a brand new candidate called a PEER REFLEXIVE CANDIDATE. ICE then tests it just like any other candidate. no cap, that's main character energy ๐.
Now here's an optimization ๐ฅ. As soon as R receives L's check, R schedules a connectivity check of its own to send back to L on that same candidate pair. This speeds up finding a valid candidate. It's called a TRIGGERED CHECK.
When this handshake is done, both L and R know they can send (and receive) messages end-to-end in both directions. iconic ๐.
2.3. Sorting Candidates
Because the algorithm above searches through ALL the candidate pairs, a working pair will eventually be found no matter what order the candidates get tried in ๐ง. But to get faster (and better) results, the candidates are sorted in a specified order. That sorted list of candidate pairs is called the CHECK LIST. The algorithm is in Section 4.1.2, but it's built on two main principles:
- Each agent gives its candidates a numeric priority, and that priority is sent along with the candidate to the peer.
- The local and remote priorities are combined so that each agent ends up with the same ordering for the candidate pairs.
The second one is a big deal when there are NATs in front of L and R ๐. NATs often won't let packets in from a host until the agent behind the NAT has sent a packet toward that host. So ICE checks in each direction won't succeed until BOTH sides have sent a check through their own NATs. ngl that's a lot of waiting around, but it's how it works.
The agent works through the check list by sending a STUN request for the next candidate pair on the list, periodically. These are called ORDINARY CHECKS.
In general, the priority algorithm is designed so that candidates of a similar type get similar priorities. It also prefers more direct routes (ones through fewer media relays and fewer NATs) over indirect ones (ones with more media relays and more NATs) ๐. Within those guidelines, though, agents get a fair amount of say in how they tune their algorithms.
2.4. Frozen Candidates
So far we've only covered the case where the agents want a media session with one COMPONENT (a piece of a media stream that needs a single transport address). A media stream can need multiple components, and EACH one has to work for the stream as a whole to be working. Typically (for example, with RTP and RTCP), the agents need connectivity for more than one flow at a time ๐.
The network properties are probably very similar for each component, especially since RTP and RTCP get sent and received from the same IP address. So it's usually possible to use info from one media component to figure out the best candidates for another. ICE does this with a mechanism called "frozen candidates" ๐ง.
Every candidate has a property called its FOUNDATION. Two candidates share a foundation when they're "similar": same type, obtained from the same host candidate and STUN server, using the same protocol. Otherwise, their foundations are different. A candidate pair has a foundation too, which is just the two candidate foundations joined together. At first, only the candidate pairs with unique foundations get tested. The rest are marked "frozen" ๐ฅถ. Once the connectivity checks for a candidate pair succeed, the other candidate pairs with the same foundation get unfrozen. This stops the agents from repeatedly checking things that look more attractive on the surface but are likely to fail anyway ๐.
I described "frozen" here as its own mechanism to make it easier to explain, but honestly it's an integral part of ICE, and the ICE prioritization algorithm automatically makes sure the right candidates get unfrozen and checked in the right order ๐.
2.5. Security for Checks
ICE is what figures out which addresses can be used to send media between two agents. So it's really important that nobody can hijack that process and send media to the wrong place ๐ค. Each STUN connectivity check is covered by a message authentication code (MAC), computed with a key that was exchanged over the signaling channel. This MAC provides message integrity and data origin authentication, which stops an attacker from forging or modifying connectivity check messages. Full stop, no exceptions ๐.
There's also a forking case ๐. If the SIP [RFC3261] caller is using ICE and their call forks, the ICE exchanges happen independently with each forked recipient. In that situation, the keys exchanged in the signaling help tie each ICE exchange to the right forked recipient.
2.6. Concluding ICE
ICE checks run in a specific sequence, so high-priority candidate pairs get checked first, followed by lower-priority ones ๐โโ๏ธ. One way to wrap up ICE is to declare victory as soon as a check for each component of each media stream succeeds. Honestly, that's a reasonable algorithm, and the details for it are below. BUT a packet loss could make a higher-priority check take longer to finish. In that case, letting ICE run a little longer might give better results โณ.
More fundamentally, though, the prioritization in this specification may not give "optimal" results. For example, if the goal is to pick low-latency media paths, using a relay is a hint that latencies might be higher, but that's all it is: a hint ๐คทโโ๏ธ. An actual round-trip time (RTT) measurement could be made, and it might show that a lower-priority pair is actually better than a higher-priority one. plot twist ๐ญ.
So ICE assigns one agent the role of CONTROLLING AGENT and the other the role of CONTROLLED AGENT. The controlling agent gets to nominate which candidate pairs will be used for media, picking from the ones that are valid. It can do this in one of two ways: REGULAR NOMINATION or AGGRESSIVE NOMINATION ๐.
With regular nomination, the controlling agent lets the checks keep going until at least one valid candidate pair for each media stream is found. Then it picks among the valid ones and sends a second STUN request on its NOMINATED candidate pair. This time the request has a flag set, which tells the peer that this pair has been nominated for use. Here's what that looks like in Figure 4 ๐ :
L R
- -
STUN request -> \ L's
<- STUN response / check
<- STUN request \ R's
STUN response -> / check
STUN request + flag -> \ L's
<- STUN response / check
Figure 4: Regular Nomination
Once the STUN transaction with the flag completes, both sides cancel any future checks for that media stream. ICE will now send media using this pair. The pair an ICE agent is actually using for media is called the SELECTED PAIR โจ.
With aggressive nomination, the controlling agent puts the flag in EVERY STUN request it sends ๐ฅ. That way, as soon as the first check succeeds, ICE processing is complete for that media stream, and the controlling agent doesn't have to send a second STUN request. The selected pair will be the highest-priority valid pair whose check succeeded. Aggressive nomination is faster than regular nomination, but it gives less flexibility. Figure 5 shows it:
L R
- -
STUN request + flag -> \ L's
<- STUN response / check
<- STUN request \ R's
STUN response -> / check
Figure 5: Aggressive Nomination
Once all of the media streams are complete, the controlling endpoint sends an updated offer if the candidates in the m and c lines for the media stream (called the DEFAULT CANDIDATES) don't match ICE's SELECTED CANDIDATES ๐ธ.
ICE can be restarted at any time after it has concluded, for one or all of the media streams, by either agent. To do that, the agent sends an updated offer that indicates a restart ๐.
2.7. Lite Implementations
For ICE to be used in a call, BOTH agents need to support it ๐ซถ. But some agents are always connected to the public Internet and have a public IP address where they can receive packets from any correspondent. To make ICE easier for these devices to support, ICE defines a special type of implementation called LITE (as opposed to the normal FULL implementation) ๐ฆ. A lite implementation doesn't gather candidates. It only includes host candidates for any media stream. Lite agents don't generate connectivity checks or run the state machines, but they do need to be able to respond to connectivity checks ๐ง.
When a lite implementation connects with a full implementation, the full agent takes the controlling role and the lite agent takes the controlled role. When two lite implementations connect, no checks get sent at all ๐.
For guidance on when a lite implementation is appropriate, check the discussion in Appendix A ๐.
One more thing, and it's important ๐. The lite implementation was added to this specification as a stepping stone toward a full implementation. Even for devices that are always connected to the public Internet, a full implementation is preferable if it's achievable ๐.
3. Terminology โจ
Okay bestie, let's get our vocab straight ๐ 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 should be familiar with the terminology defined in the offer/answer model [RFC3264], STUN [RFC5389], and NAT Behavioral requirements for UDP [RFC4787]. No cap, these are the foundations ๐ง
This specification makes use of the following additional terminology:
- Agent: As defined in RFC 3264, an agent is the protocol implementation involved in the offer/answer exchange. There are two agents involved in an offer/answer exchange ๐ซถ
- Peer: From the perspective of one of the agents in a session, its peer is the other agent. Specifically, from the perspective of the offerer, the peer is the answerer. From the perspective of the answerer, the peer is the offerer.
- Transport Address: The combination of an IP address and transport protocol (such as UDP or TCP) port.
- Candidate: A transport address that is a potential point of contact for receipt of media. Candidates also have properties -- their type (server reflexive, relayed or host), priority, foundation, and base. Main character energy โจ
- Component: A component is a piece of a media stream requiring a single transport address. A media stream may require multiple components, and each of them has to work for the media stream as a whole to work. For media streams based on RTP, there are two components per media stream -- one for RTP, and one for RTCP ๐
- Host Candidate: A candidate obtained by binding to a specific port from an IP address on the host. This includes IP addresses on physical interfaces and logical ones, such as ones obtained through Virtual Private Networks (VPNs) and Realm Specific IP (RSIP) [RFC3102] (which lives at the operating system level) ๐ป
- Server Reflexive Candidate: A candidate whose IP address and port are a binding allocated by a NAT for an agent when it sent a packet through the NAT to a server. Server reflexive candidates can be learned by STUN servers using the Binding request, or TURN servers, which provides both a relayed and server reflexive candidate ๐ฆ
- Peer Reflexive Candidate: A candidate whose IP address and port are a binding allocated by a NAT for an agent when it sent a STUN Binding request through the NAT to its peer.
- Relayed Candidate: A candidate obtained by sending a TURN Allocate request from a host candidate to a TURN server. The relayed candidate is resident on the TURN server, and the TURN server relays packets back towards the agent ๐
- Base: The base of a server reflexive candidate is the host candidate from which it was derived. A host candidate is also said to have a base, equal to that candidate itself. Similarly, the base of a relayed candidate is that candidate itself.
- Foundation: An arbitrary string that is the same for two candidates that have the same type, base IP address, protocol (UDP, TCP, etc.), and STUN or TURN server. If any of these are different, then the foundation will be different. Two candidate pairs with the same foundation pairs are likely to have similar network characteristics. Foundations are used in the frozen algorithm ๐ธ
- Local Candidate: A candidate that an agent has obtained and included in an offer or answer it sent.
- Remote Candidate: A candidate that an agent received in an offer or answer from its peer.
- Default Destination/Candidate: The default destination for a component of a media stream is the transport address that would be used by an agent that is not ICE aware. For the RTP component, the default IP address is in the
cline of the SDP, and the port is in themline. For the RTCP component, it is in thertcpattribute when present. When not present, the IP address is in thecline and 1 plus the port is in themline. A default candidate for a component is one whose transport address matches the default destination for that component ๐ - Candidate Pair: A pairing containing a local candidate and a remote candidate. Two becomes a duo, bestie ๐ฏ
- Check, Connectivity Check, STUN Check: A STUN Binding request transaction for the purposes of verifying connectivity. A check is sent from the local candidate to the remote candidate of a candidate pair.
- Check List: An ordered set of candidate pairs that an agent will use to generate checks.
- Ordinary Check: A connectivity check generated by an agent as a consequence of a timer that fires periodically, instructing it to send a check.
- Triggered Check: A connectivity check generated as a consequence of the receipt of a connectivity check from the peer. Ask and you shall receive ๐
- Valid List: An ordered set of candidate pairs for a media stream that have been validated by a successful STUN transaction โ
- Full: An ICE implementation that performs the complete set of functionality defined by this specification ๐
- Lite: An ICE implementation that omits certain functions, implementing only as much as is necessary for a peer implementation that is full to gain the benefits of ICE. Lite implementations do not maintain any of the state machines and do not generate connectivity checks. Lite is giving minimalist, but still gets the perks ๐ฟ
- Controlling Agent: The ICE agent that is responsible for selecting the final choice of candidate pairs and signaling them through STUN and an updated offer, if needed. In any session, one agent is always controlling. The other is the controlled agent ๐ฎ
- Controlled Agent: An ICE agent that waits for the controlling agent to select the final choice of candidate pairs. Patience is a virtue, babe ๐
- Regular Nomination: The process of picking a valid candidate pair for media traffic by validating the pair with one STUN request, and then picking it by sending a second STUN request with a flag indicating its nomination.
- Aggressive Nomination: The process of picking a valid candidate pair for media traffic by including a flag in every STUN request, such that the first one to produce a valid candidate pair is used for media. Speed run any% ๐โโ๏ธ
- Nominated: If a valid candidate pair has its nominated flag set, it means that it may be selected by ICE for sending and receiving media.
- Selected Pair, Selected Candidate: The candidate pair selected by ICE for sending and receiving media is called the selected pair, and each of its candidates is called the selected candidate. The winners' circle ๐
4. Sending the Initial Offer
In order to send the initial offer in an offer/answer exchange, an agent must (1) gather candidates, (2) prioritize them, (3) eliminate redundant candidates, (4) choose default candidates, and then (5) formulate and send the SDP offer. All but the last of these five steps differ for full and lite implementations. So there are five steps and a fork in the road depending on which flavour you're running ๐
4.1. Full Implementation Requirements
4.1.1. Gathering Candidates
An agent gathers candidates when it believes that communication is imminent. An offerer can do this based on a user interface cue, or based on an explicit request to initiate a session. Every candidate is a transport address, and it also has a type and a base. Four types are defined and gathered by this specification: host candidates, server reflexive candidates, peer reflexive candidates, and relayed candidates. The server reflexive candidates are gathered using STUN or TURN, and relayed candidates are obtained through TURN. Peer reflexive candidates are obtained in later phases of ICE, as a consequence of connectivity checks. The base of a candidate is the candidate that an agent must send from when using that candidate ๐
4.1.1.1. Host Candidates
The first step is to gather host candidates. Host candidates are obtained by binding to ports (typically ephemeral) on a IP address attached to an interface (physical or virtual, including VPN interfaces) on the host. Step one, glow up started ๐
For each UDP media stream the agent wishes to use, the agent SHOULD obtain a candidate for each component of the media stream on each IP address that the host has. It obtains each candidate by binding to a UDP port on the specific IP address. A host candidate (and indeed every candidate) is always associated with a specific component for which it is a candidate. Each component has an ID assigned to it, called the component ID. For RTP-based media streams, the RTP itself has a component ID of 1, and RTCP a component ID of 2. If an agent is using RTCP, it MUST obtain a candidate for it. If an agent is using both RTP and RTCP, it would end up with 2*K host candidates if an agent has K IP addresses. So the math is 2รK, and yes, that's a lot of candidates, but that's the vibe ๐งฎ
The base for each host candidate is set to the candidate itself. Every host candidate is its own base, which is honestly very self-aware of it ๐ฆ
4.1.1.2. Server Reflexive and Relayed Candidates
Okay bestie, now we get into the public-facing stuff ๐โจ Agents SHOULD obtain relayed candidates and SHOULD obtain server reflexive candidates ๐ These requirements are at SHOULD strength on purpose, so providers have room to vary ๐
Real talk, STUN and TURN servers might be unnecessary in closed networks where agents never connect to the public Internet or to endpoints outside that closed network ๐ In that case, a full implementation would be used for agents that are dual stack or multihomed, to select a host candidate ๐ง TURN servers are pricey ๐ธ, and with ICE they only get used when both endpoints sit behind NATs that do address and port dependent mapping. So some deployments might consider this use case marginal and choose not to use TURN servers ๐ฅฑ If an agent isn't gathering server reflexive or relayed candidates, it is RECOMMENDED that the functionality still be implemented and just disabled through configuration, so it can be switched back on if conditions change later ๐
So, if an agent is gathering both relayed and server reflexive candidates, it uses a TURN server. If it's only gathering server reflexive candidates, it uses a STUN server ๐
Next, the agent pairs each host candidate with the STUN or TURN server it's configured with or has discovered some other way ๐งฉ If a STUN or TURN server is configured, it is RECOMMENDED that a domain name be configured too. Then the DNS procedures in [RFC5389] (using SRV records with the "stun" service) can find the STUN server, and the DNS procedures in [RFC5766] (using SRV records with the "turn" service) can find the TURN server ๐บ๏ธ
Quick scope check: this spec only considers using a single STUN or TURN server ๐ฏ When there are multiple choices for that one server (like when DNS returns multiple results), an agent SHOULD use a single STUN or TURN server, chosen based on its IP address, for all candidates in a particular session. This improves the performance of ICE ๐ The result is a set of pairs of host candidates with STUN or TURN servers. The agent then chooses one pair and sends a Binding or Allocate request to the server from that host candidate ๐จ Binding requests to a STUN server aren't authenticated, and any ALTERNATE-SERVER attribute in a response is ignored ๐
Agents MUST support the backwards compatibility mode for the Binding request defined in [RFC5389]. Allocate requests SHOULD be authenticated using a long-term credential the client got through some other means ๐
Every Ta milliseconds after that, the agent can kick off another new STUN or TURN transaction โฑ๏ธ That can either be a retry of an earlier transaction that failed with a recoverable error (like an authentication failure), or a transaction for a new host candidate and STUN or TURN server pair. The agent SHOULD NOT generate transactions more often than once every Ta milliseconds. Check out Section 16 for guidance on setting Ta and the STUN retransmit timer, RTO ๐
Now the agent waits for a Binding or Allocate response ๐ A successful Allocate response gives the agent a server reflexive candidate (from the mapped address) plus a relayed candidate in the XOR-RELAYED-ADDRESS attribute. If the Allocate request is rejected because the server lacks the resources to fulfill it, the agent SHOULD instead send a Binding request to get a server reflexive candidate. A Binding response only gives the agent a server reflexive candidate (also from the mapped address). The base of a server reflexive candidate is the host candidate the Allocate or Binding request was sent from. The base of a relayed candidate is that candidate itself. If a relayed candidate is identical to a host candidate (which can happen in rare cases), the relayed candidate MUST be discarded ๐๏ธ
4.1.1.3. Computing Foundations
Finally, the agent gives each candidate a foundation ๐ท๏ธ The foundation is an identifier, scoped within a session. Two candidates MUST have the same foundation ID when all of the following are true:
- they are of the same type (host, relayed, server reflexive, or peer reflexive).
- their bases have the same IP address (the ports can be different).
- for reflexive and relayed candidates, the STUN or TURN servers used to obtain them have the same IP address.
- they were obtained using the same transport protocol (TCP, UDP, etc.).
Similarly, two candidates MUST have different foundations if their types are different, their bases have different IP addresses, the STUN or TURN servers used to obtain them have different IP addresses, or their transport protocols are different ๐
4.1.1.4. Keeping Candidates Alive
Once server reflexive and relayed candidates are allocated, they MUST be kept alive until ICE processing has completed, as described in Section 8.3 โณ For server reflexive candidates learned through a Binding request, the bindings MUST be kept alive by additional Binding requests to the server. Refreshes for allocations are done using the Refresh transaction, as described in [RFC5766]. The Refresh requests will also refresh the server reflexive candidate ๐
4.1.2. Prioritizing Candidates
The prioritization process gives every candidate a priority ๐ Each candidate for a media stream MUST have a unique priority, and that priority MUST be a positive integer between 1 and (2**31 - 1). ICE uses this priority to decide the order of the connectivity checks and how much each candidate is preferred ๐
An agent SHOULD compute this priority using the formula in Section 4.1.2.1 and choose its parameters using the guidelines in Section 4.1.2.2. If an agent elects to use a different formula, ICE will take longer to converge, since both agents won't be coordinated in their checks ๐ข
4.1.2.1. Recommended Formula
When using the formula, an agent computes the priority by picking a preference for each candidate type (server reflexive, peer reflexive, relayed, and host) and, when the agent is multihomed, a preference for its IP addresses. These two preferences get combined to compute the candidate's priority, using this formula:
priority = (2^24)*(type preference) +
(2^8)*(local preference) +
(2^0)*(256 - component ID)
The type preference MUST be an integer from 0 to 126 inclusive, and it represents the preference for the candidate's type (the types being local, server reflexive, peer reflexive, and relayed). 126 is the highest preference and 0 is the lowest. Setting it to 0 means candidates of that type will only be used as a last resort ๐ซ The type preference MUST be identical for all candidates of the same type and MUST be different for candidates of different types. The type preference for peer reflexive candidates MUST be higher than that of server reflexive candidates. Heads up: candidates gathered through the procedures of Section 4.1.1 will never be peer reflexive candidates, since those are learned from the connectivity checks ICE performs ๐ต๏ธ
The local preference MUST be an integer from 0 to 65535 inclusive. It represents a preference for the particular IP address the candidate was obtained from, in cases where an agent is multihomed. 65535 is the highest preference and zero is the lowest. When there's only a single IP address, this value SHOULD be set to 65535. More generally, if there are multiple candidates for a particular component of a particular media stream that have the same type, the local preference MUST be unique for each one. In this specification, that only happens for multihomed hosts. If a host is multihomed because it's dual stack, the local preference SHOULD be set equal to the precedence value for IP addresses described in RFC 3484 [RFC3484] ๐
The component ID is the candidate's component ID, and it MUST be between 1 and 256 inclusive โจ
4.1.2.2. Guidelines for Choosing Type and Local Preferences
One criterion for choosing the type and local preference values is whether a media intermediary is involved, like a TURN server, VPN server, or NAT. With a media intermediary, media sent to that candidate first transits the intermediary before it's received. Relayed candidates are one type that involves a media intermediary, and host candidates obtained from a VPN interface are another ๐ฐ๏ธ Transiting media through an intermediary can increase the latency between transmission and reception. It can increase packet losses, because of the additional router hops that may be taken. It may also increase the cost of providing service, since media gets routed in and right back out of an intermediary run by a provider ๐ธ If these concerns are important, the type preference for relayed candidates SHOULD be lower than for host candidates. The RECOMMENDED values are 126 for host candidates, 100 for server reflexive candidates, 110 for peer reflexive candidates, and 0 for relayed candidates. Furthermore, if an agent is multihomed and has multiple IP addresses, the local preference for host candidates from a VPN interface SHOULD have a priority of 0 ๐
Another criterion is IP address family. ICE works with both IPv4 and IPv6, so it offers a transition mechanism that lets dual-stack hosts prefer connectivity over IPv6, but fall back to IPv4 if the v6 networks get disconnected (for example, due to a failure in a 6to4 relay) [RFC3056] ๐ It can also help hosts that have both a native IPv6 address and a 6to4 address. In that case, higher local preferences could go to the v6 addresses, followed by the 6to4 addresses, followed by the v4 addresses. That way a site can start using native v6 addresses right away, yet still fall back to 6to4 addresses when talking to agents in other sites that don't have native v6 connectivity yet ๐
Another criterion is security ๐ If a user is a telecommuter, connected to both a corporate network and a local home network, they might want their voice traffic routed over the VPN to keep it on the corporate network when talking within the enterprise, but use the local network when talking to people outside it. In that case, a VPN address would have a higher local preference than any other address ๐
Last but not least, topological awareness ๐บ๏ธ This is most useful for candidates that make use of intermediaries. If an agent has preconfigured or dynamically discovered knowledge of how close its intermediaries are to it, it can use that to assign higher local preferences to candidates obtained from closer intermediaries ๐ง
4.1.3. Eliminating Redundant Candidates โจ
okay next up, we get rid of the copycats ๐๐ . A candidate is redundant if its transport address matches another candidate's AND its base matches that other candidate's base too. Lowkey tricky: two candidates can share a transport address and still have different bases, and those are NOT redundant, so don't yeet them ๐ โโ๏ธ. Highkey, a server reflexive candidate and a host candidate often end up redundant when you're not behind a NAT, which makes sense because the public address is just your own address in disguise ๐ฆ. The agent SHOULD eliminate the redundant candidate with the lower priority. Keep the winner, bestie ๐.
4.1.4. Choosing Default Candidates ๐
so a candidate is "default" if it's where media from a non-ICE peer would land. That target is called the DEFAULT DESTINATION ๐. Here's the tea: if ICE doesn't pick the default candidates when it's talking to an ICE-aware peer, you'll need an updated offer/answer after ICE finishes, so the SDP can "fix up" the default destination to match what ICE actually chose ๐ง. If ICE happens to pick the defaults, no updated offer/answer is needed, and that's a whole vibe ๐ธ.
An agent MUST choose a set of candidates, one for each component of each in-use media stream, to be default. A media stream is in-use if it does NOT have a port of zero (that's how RFC 3264 rejects a stream ๐ซ). That means a stream is still in-use even if it's marked a=inactive [RFC4566] or has a bandwidth value of zero, which is giving "it's still here, just chilling" ๐ง.
It is RECOMMENDED that default candidates be picked based on how likely they are to work with the peer being contacted ๐. It is RECOMMENDED that the default candidates are relayed candidates (if relayed ones exist), then server reflexive candidates (if those exist), and finally host candidates. Relayed first, host last, periodt ๐ โจ.
4.2. Lite Implementation Requirements ๐
lite implementations only use host candidates, so they keep it simple ๐งธ. A lite implementation MUST, for each component of each media stream, allocate zero or one IPv4 candidates. It MAY allocate zero or more IPv6 candidates, but no more than one per IPv6 address the host is using. Since there can be no more than one IPv4 candidate per component of each media stream, if an agent has multiple IPv4 addresses, it MUST choose one for allocating the candidate ๐ฏ. If a host is dual stack, it is RECOMMENDED that it allocate one IPv4 candidate and one global IPv6 address ๐. With a lite implementation, ICE can't dynamically pick between candidates, so including more than one candidate from a particular scope is NOT RECOMMENDED. Only a connectivity check can truly tell which address to use, and lite doesn't do checks, so no 2-for-1 deals here ๐.
Every component gets an ID called the component ID ๐ชช. For RTP-based media streams, RTP itself has component ID 1, and RTCP has component ID 2. If an agent is using RTCP, it MUST obtain candidates for it ๐ซถ.
Every candidate gets a foundation ๐ง. The foundation MUST be different for two candidates allocated from different IP addresses, and MUST be the same otherwise. A simple integer that goes up for each IP address will do the job โจ. On top of that, each candidate MUST be assigned a unique priority among all candidates for the same media stream ๐ข. This priority SHOULD be equal to:
priority = (2^24)*(126) +
(2^8)*(IP precedence) +
(2^0)*(256 - component ID)
If a host is v4-only, it SHOULD set the IP precedence to 65535 ๐. If a host is v6 or dual stack, the IP precedence SHOULD be the precedence value for IP addresses described in RFC 3484 [RFC3484] ๐ซ.
Next up, the agent picks a default candidate for each component of each media stream ๐. If a host is IPv4 only, there's only one candidate per component of each media stream, so that one is automatically the default, easy peasy ๐. If a host is IPv6 or dual stack, picking the default is up to local policy ๐บ๏ธ. This default SHOULD be the candidate most likely to be used with a peer. For IPv6-only hosts, that's typically a globally scoped IPv6 address ๐. For dual-stack hosts, the IPv4 address is RECOMMENDED ๐.
4.3. Encoding the SDP ๐
Encoding the SDP is exactly the same for full and lite implementations, no drama ๐ซถ.
The agent includes an m line for each media stream it wants to use ๐ป. The ORDER of media streams in the SDP matters for ICE. ICE runs its connectivity checks on the first m line first, so media can start flowing for that stream first ๐. Agents SHOULD place their most important media stream, if there is one, first in the SDP ๐.
There's a candidate attribute for every candidate of a particular media stream ๐ฆ. Section 15 has the detailed rules for building this attribute. It carries the IP address, port, and transport protocol for the candidate, plus the stuff the peer needs to know for ICE to work: the priority, foundation, and component ID ๐. It also carries info that's handy for diagnostics and other functions: the candidate's type and its related transport addresses ๐.
STUN connectivity checks between agents are authenticated with the short-term credential mechanism defined for STUN [RFC5389] ๐. This mechanism relies on a username and password exchanged through protocol machinery between client and server. With ICE, the offer/answer exchange is what carries them ๐ฌ. The username part of the credential is made by concatenating a username fragment from each agent, separated by a colon. Each agent also gives a password, which is used to compute the message integrity for requests it receives ๐งฉ. The username fragment and password travel in the ice-ufrag and ice-pwd attributes, respectively. Beyond security, the username also handles disambiguation and correlation of checks to media streams, which is iconic ๐ญ. See Appendix B.4 for the motivation.
If an agent is a lite implementation, it MUST include an a=ice-lite session-level attribute in its SDP ๐ช. If an agent is a full implementation, it MUST NOT include this attribute. Full implementations, don't do it, you're not lite ๐
.
The default candidates get added to the SDP as the default destination for media ๐ฌ. For RTP-based streams, you put the IP address and port of the RTP candidate into the c and m lines, respectively. If the agent is using RTCP, it MUST encode the RTCP candidate using the a=rtcp attribute as defined in RFC 3605 [RFC3605] ๐ก. If RTCP is NOT in use, the agent MUST signal that using b=RS:0 and b=RR:0 as defined in RFC 3556 [RFC3556]. Either way, somebody has to say what's up ๐โโ๏ธ.
The transport addresses that will be the default destination for media when talking to non-ICE peers MUST also be present as candidates in one or more a=candidate lines ๐. No ghosting the defaults, bestie ๐ป.
ICE is extensible ๐งฉ. An offer or answer can contain a series of tokens that identify the ICE extensions that agent uses. If an agent supports an ICE extension, it MUST include the token defined for that extension in the ice-options attribute ๐ธ.
Here's an example SDP message with ICE attributes. The lines are folded so they fit on the screen ๐ฑ:
v=0
o=jdoe 2890844526 2890842807 IN IP4 10.0.1.1
s=
c=IN IP4 192.0.2.3
t=0 0
a=ice-pwd:asd88fgpdd777uzjYhagZg
a=ice-ufrag:8hhY
m=audio 45664 RTP/AVP 0
b=RS:0
b=RR:0
a=rtpmap:0 PCMU/8000
a=candidate:1 1 UDP 2130706431 10.0.1.1 8998 typ host
a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr
10.0.1.1 rport 8998
Once an agent has sent its offer or answer, that agent MUST be ready to receive both STUN and media packets on each candidate ๐ฅ. Heads up: as Section 11.1 explains, media packets can be sent to a candidate before it shows up as the default destination in an offer or answer, so stay alert ๐๐.
5. Receiving the Initial Offer ๐ฉ
when an agent gets an initial offer, it checks whether the offerer supports ICE, works out its own role, gathers candidates, prioritizes them, picks default candidates, encodes and sends an answer, and, for full implementations, forms the check lists and starts connectivity checks ๐โโ๏ธ๐จ.
5.1. Verifying ICE Support ๐
The agent goes ahead with the ICE procedures in this spec if, for each media stream in the SDP it got, the default destination for each component of that media stream shows up in a candidate attribute. For RTP, that means the IP address and port in the c and m lines appear in a candidate attribute, and the value in the rtcp attribute appears in a candidate attribute too ๐งพ.
If this condition is NOT met, the agent MUST process the SDP using normal RFC 3264 procedures, without any of the ICE mechanisms described in the rest of this specification, with these exceptions ๐:
- The agent MUST follow the rules of Section 10, which describe keepalive procedures for all agents ๐.
- If the agent isn't going with ICE because there were
a=candidateattributes but none matched the default destination of the media stream, the agent MUST include ana=ice-mismatchattribute in its answer ๐ฉ. - If the default candidates were relayed candidates learned through a TURN server, the agent MUST create permissions in the TURN server for the IP addresses it learned from its peer in the SDP it just received. If this is NOT done, initial packets in the media stream from the peer may get lost, and nobody wants that plot twist ๐ฑ๐ซ .
5.2. Determining Role โจ
okay so every session has two roles, and they're giving "lead" vs "follow" ๐๐ซถ. There's the controlling agent, which is basically the one who picks the final candidate pairs for talking. If you're a full agent, that means nominating the pairs ICE can use for each media stream, and generating an updated offer based on what ICE picked when that's needed. If you're a lite implementation, being controlling means choosing a pair from the ones in the offer and answer (for IPv4 there's only ever one pair ๐ฆ), and then generating an updated offer for that choice when it's needed (which is never needed for an IPv4-only host ๐ ). The controlled agent just gets told which pairs to use for each media stream, and it doesn't generate an updated offer to tell anyone about it. The sections below go deep on what controlling and controlled nodes actually do ๐ธ.
Here's how roles get decided:
Both agents are full: The agent that generated the offer that kicked off ICE processing MUST take the controlling role, and the other MUST take the controlled role. Both agents form check lists, run the ICE state machines, and generate connectivity checks ๐ฅ. The controlling agent runs the logic in Section 8.1 to nominate the pairs ICE will select, and then both agents end ICE the way Section 8.1.2 describes โจ. Now, in unusual cases (described in Appendix B.11) both agents can mistakenly think they're controlled or controlling ๐. To fix that, each agent MUST select a random number called the tie-breaker, uniformly distributed between 0 and (2**64) - 1 (so basically a 64-bit positive integer). That number shows up in connectivity checks to detect and repair the mix-up, as explained in Section 7.1.2.2 ๐.
One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role. The full agent forms check lists, runs the ICE state machines, and generates connectivity checks ๐. It uses the logic in Section 8.1 to nominate pairs for ICE to select, and the logic in Section 8.1.2 to end ICE. The lite implementation just listens for connectivity checks, receives them, responds to them, and then wraps up ICE as described in Section 8.2 ๐ง. For the lite side, the state of ICE processing for each media stream is considered to be Running, and the state of ICE overall is Running too ๐.
Both lite: The agent that generated the offer that started ICE processing MUST take the controlling role, and the other MUST take the controlled role. In this case no connectivity checks are ever sent ๐ โโ๏ธ. Instead, once the offer/answer exchange finishes, each agent does the processing described in Section 8 without any checks โจ. It's possible that both agents think they're controlled or controlling. If that happens, the conflict gets sorted out by the glare detection stuff in the signaling protocol carrying the offer/answer exchange ๐. The state of ICE processing for each media stream is considered to be Running, and the state of ICE overall is Running as well ๐.
Once roles are set for a session, they stick around unless ICE gets restarted. An ICE restart (Section 9.1) causes a brand new role selection and new tie-breakers, so it's a whole fresh start ๐ฆ๐.
5.3. Gathering Candidates
The answerer gathers candidates the exact same way the offerer does, as described in Section 4.1.1 for full implementations and Section 4.2 for lite implementations ๐ซถ. It is RECOMMENDED that this begin right away when the offer shows up, before the user gets alerted ๐. Gathering MAY also start as soon as an agent starts up ๐ธ.
5.4. Prioritizing Candidates
Prioritizing candidates at the answerer is literally the same as what the offerer does. Check Section 4.1.2 for full implementations and Section 4.2 for lite ones ๐ .
5.5. Choosing Default Candidates
Picking default candidates on the answerer side is identical to the offerer's process. See Section 4.1.4 for full implementations and Section 4.2 for lite ones ๐.
5.6. Encoding the SDP
Encoding the SDP at the answerer works exactly like it does for the offerer, for both full and lite implementations. Just go check Section 4.3 โจ.
5.7. Forming the Check Lists
Forming check lists is something only full implementations do, so lite implementations MUST skip the steps in this section ๐ โโ๏ธ. There's one check list for each in-use media stream that comes out of the offer/answer exchange. To build the check list for a media stream, the agent forms candidate pairs, computes a pair priority, orders the pairs by that priority, prunes them, and sets their states ๐ง . Those steps are all covered in this section ๐.
5.7.1. Forming Candidate Pairs
First, the agent takes each of its candidates for a media stream (called LOCAL CANDIDATES) and pairs each one with the candidates it got from its peer for that media stream (called REMOTE CANDIDATES) ๐ฆ. To guard against the attacks described in Section 18.5.2, agents MAY cap how many candidates they'll accept in an offer or answer ๐ก๏ธ. A local candidate gets paired with a remote candidate if and only if the two have the same component ID and the same IP address version ๐. It's totally possible that some local candidates won't find a remote partner, and some remote candidates won't find a local one. That happens when one agent doesn't include candidates for all the components of a media stream. When it does, the number of components for that media stream is effectively reduced, and it's treated as equal to the minimum, across both agents, of the maximum component ID each agent provides across all components for that media stream ๐.
For RTP, this shows up when one agent provides candidates for RTCP and the other doesn't ๐ง. Another example: the offerer can multiplex RTP and RTCP on the same port, and it says so in the SDP through an SDP attribute [RFC5761]. But since the offerer doesn't know whether the answerer can do that multiplexing, it includes candidates for RTP and RTCP on separate ports, so the offer has two components per media stream ๐. If the answerer can multiplex, it includes just a single component for each candidate, meaning the combined RTP/RTCP mux. ICE then ends up acting as if there's only a single component for that candidate โจ.
The candidate pairs where both the local and remote candidates are the default candidates for a particular component are, unsurprisingly, called the default candidate pair for that component ๐. This is the pair that would carry media if neither agent were ICE aware ๐ .
To help it all click, Figure 6 shows how the key concepts relate: transport addresses, candidates, candidate pairs, and check lists, plus the main properties of candidates and candidate pairs ๐.
+------------------------------------------+
| |
| +---------------------+ |
| |+----+ +----+ +----+ | +Type |
| || IP | |Port| |Tran| | +Priority |
| ||Addr| | | | | | +Foundation |
| |+----+ +----+ +----+ | +ComponentiD |
| | Transport | +RelatedAddr |
| | Addr | |
| +---------------------+ +Base |
| Candidate |
+------------------------------------------+
* *
* *************************************
* *
+-------------------------------+
.| |
| Local Remote |
| +----+ +----+ +default? |
| |Cand| |Cand| +valid? |
| +----+ +----+ +nominated?|
| +State |
| |
| |
| Candidate Pair |
+-------------------------------+
* *
* ************
* *
+------------------+
| Candidate Pair |
+------------------+
+------------------+
| Candidate Pair |
+------------------+
+------------------+
| Candidate Pair |
+------------------+
Check
List
Figure 6: Conceptual Diagram of a Check List
5.7.2. Computing Pair Priority and Ordering Pairs
Once the pairs exist, each one gets a candidate pair priority ๐ข. Let G be the priority for the candidate from the controlling agent, and let D be the priority for the candidate from the controlled agent ๐. The pair priority is computed like this:
pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
Where G>D?1:0 is an expression that equals 1 if G is bigger than D, and 0 otherwise ๐งฎ. After the priority is assigned, the agent sorts the pairs in decreasing order of priority ๐. If two pairs have the exact same priority, the order between them is arbitrary ๐คทโโ๏ธ.
5.7.3. Pruning the Pairs
This sorted list is what decides the order of connectivity checks that get performed ๐โโ๏ธ. Each check sends a request from a local candidate to a remote candidate. An agent can't send requests straight from a reflexive candidate, only from its base, so the agent goes through the sorted list next ๐. For every pair whose local candidate is server reflexive, the server reflexive candidate MUST be replaced by its base ๐. Once that's done, the agent MUST prune the list ๐ช. Pruning means removing a pair if its local and remote candidates match the local and remote candidates of a pair higher up on the priority list โ๏ธ. What's left is an ordered sequence of candidate pairs, called the check list for that media stream ๐.
To limit the attacks described in Section 18.5.2, an agent MUST limit the total number of connectivity checks it performs across all check lists to a specific value, and this value MUST be configurable ๐. A default of 100 is RECOMMENDED ๐ฏ. The limit is enforced by discarding the lower-priority candidate pairs until there are fewer than 100 ๐๏ธ. It is RECOMMENDED that a lower value gets used when possible, set to the maximum number of plausible checks that might show up in an actual deployment configuration ๐ธ. The configuration requirement exists so that, if this value turns out to be a problem once deployed, there's a tool to fix it in the field ๐ ๏ธโจ.
5.7.4. Computing States ๐
okay so every candidate pair in the check list has a foundation and a state ๐ฆ The foundation is basically the combo of the foundations of the local and remote candidates in the pair. The state gets assigned once the check list for each media stream has been computed. There are five possible vibes for a pair's state:
- Waiting: no check has been done for this pair yet, and it can go as soon as it's the highest-priority Waiting pair on the check list ๐ธ
- In-Progress: a check has been sent for this pair and the transaction is still going ๐
- Succeeded: a check for this pair already happened and it worked โจ
- Failed: a check already happened and flopped, either no response ever came back or it got an unrecoverable failure response ๐
- Frozen: no check has happened yet, and it can't happen until some other check succeeds. Then the pair unfreezes and moves into Waiting ๐ง
As ICE runs, the pairs move between these states like Figure 7 shows ๐
+-----------+
| |
| |
| Frozen |
| |
| |
+-----------+
|
|unfreeze
|
V
+-----------+ +-----------+
| | | |
| | perform | |
| Waiting |-------->|In-Progress|
| | | |
| | | |
+-----------+ +-----------+
/ |
// |
// |
// |
/ |
// |
failure // |success
// |
/ |
// |
// |
// |
V V
+-----------+ +-----------+
| | | |
| | | |
| Failed | | Succeeded |
| | | |
| | | |
+-----------+ +-----------+
Figure 7: Pair State FSM
The initial states for each pair in a check list get computed by following this sequence of steps ๐ง
- The agent sets all of the pairs in each check list to the Frozen state.
- The agent looks at the check list for the first media stream (a media stream is the first media stream when it's described by the first m line in the SDP offer and answer). For that media stream:
- For all pairs with the same foundation, it sets the state of the pair with the lowest component ID to Waiting. If there is more than one such pair, the one with the highest priority is used.
Once that's done, one of the check lists will have some pairs in Waiting, and the other check lists will have all of their pairs Frozen ๐ A check list with at least one Waiting pair is called an active check list, and a check list with all pairs Frozen is called a frozen check list.
The check list itself also has a state that tracks how ICE checks are going for that media stream. There are three of them:
- Running: ICE checks are still in progress for this media stream ๐โโ๏ธ
- Completed: ICE checks have produced nominated pairs for each component of the media stream. So ICE has succeeded and media can be sent ๐
- Failed: the ICE checks haven't completed successfully for this media stream ๐ญ
When a check list is first built because of an offer/answer exchange, it's placed in the Running state.
ICE processing across all media streams has a state too. It's Running while ICE processing is under way, Completed when ICE processing is complete, and Failed if it failed without success. The rules for moving between these states are described below ๐ซถ
5.8. Scheduling Checks ๐๏ธ
Checks are generated only by full implementations. Lite implementations MUST skip the steps described in this section ๐
An agent does two kinds of checks: ordinary checks and triggered checks. Both are governed by a timer that fires periodically for each media stream. The agent keeps a FIFO queue called the triggered check queue, which holds candidate pairs that should get checks sent at the next available opportunity. When the timer fires, the agent pulls the top pair off the triggered check queue, runs a connectivity check on that pair, and sets the state of the candidate pair to In-Progress. If the triggered check queue is empty, an ordinary check gets sent instead ๐
Once the agent has computed the check lists as described in Section 5.7, it sets a timer for each active check list. The timer fires every Ta*N seconds, where N is the number of active check lists (at first, there's only one active check list). Implementations MAY set the timer to fire less often than this. Implementations SHOULD take care to spread out these timers so they don't all fire at the same moment for each media stream. Ta and the retransmit timer RTO are computed as described in Section 16. Multiplying by N lets this total check throughput get split between all the active check lists. The first timer fires immediately, so the agent runs a connectivity check the moment the offer/answer exchange is done, and then the next check comes Ta seconds later (since there's only one active check list) โฑ๏ธ
When the timer fires and there's no triggered check to send, the agent MUST choose an ordinary check like this:
- Find the highest-priority pair in that check list that is in the Waiting state.
- If there is such a pair:
- Send a STUN check from the local candidate of that pair to the remote candidate of that pair. The procedures for forming the STUN request for this purpose are described in Section 7.1.2.
- Set the state of the candidate pair to In-Progress.
- If there is no such pair:
- Find the highest-priority pair in that check list that is in the Frozen state.
- If there is such a pair:
- Unfreeze the pair.
- Perform a check for that pair, which moves its state to In-Progress.
- If there is no such pair:
- Terminate the timer for that check list.
To compute the message integrity for the check, the agent uses the remote username fragment and password it learned from the SDP from its peer. The agent already knows the local username fragment for its own candidate ๐
6. Receipt of the Initial Answer ๐ฌ
This section describes what an agent does when it receives the answer from its peer. It checks that its peer supports ICE, works out its role, and, for full implementations, forms the check list and starts performing ordinary checks ๐
When ICE is used with SIP, forking may result in one offer producing multiple answers. In that case, ICE proceeds completely in parallel and independently for each answer. It treats the combination of its offer and each answer as an independent offer/answer exchange, with its own set of pairs, check lists, states, and so on ๐ The only time one pair's processing affects another is when candidates get freed, which is discussed below in Section 8.3.
6.1. Verifying ICE Support ๐
The logic at the offerer is identical to the answerer's logic described in Section 5.1, except that an offerer would never generate a=ice-mismatch attributes in an SDP.
Sometimes the answer might leave out a=candidate attributes for the media streams and include an a=ice-mismatch attribute for one or more of them instead. This tells the offerer that the answerer supports ICE, but that ICE processing wasn't used for the session. The reason is that a signaling intermediary changed the default destination for media components without changing the matching candidate attributes. See Section 18 for a discussion of cases where this can happen. This specification gives no guidance on how an agent should proceed in such a failure case ๐
6.2. Determining Role ๐
The offerer follows the same procedures described for the answerer in Section 5.2.
6.3. Forming the Check List ๐
Forming check lists is performed only by full implementations. The offerer follows the same procedures described for the answerer in Section 5.7.
6.4. Performing Ordinary Checks โ
Ordinary checks are performed only by full implementations. The offerer follows the same procedures described for the answerer in Section 5.8.
7. Performing Connectivity Checks โก
This section describes how connectivity checks are performed. All ICE implementations are required to be compliant to [RFC5389], as opposed to the older [RFC3489]. But a full implementation both generates checks (acting as a STUN client) and receives them (acting as a STUN server), whereas a lite implementation only receives checks, so it only acts as a STUN server ๐ซง
7.1. STUN Client Procedures ๐ก
These procedures define how an agent sends a connectivity check, whether it's an ordinary or a triggered check. They only apply to full implementations.
7.1.1. Creating Permissions for Relayed Candidates ๐ฆ
If the connectivity check is being sent using a relayed local candidate, the client MUST create a permission first, unless it has already created one before. It would have created one before if it had told the TURN server to create a permission for the given relayed candidate towards the IP address of the remote candidate. To create the permission, the agent follows the procedures defined in [RFC5766]. The permission MUST be created towards the IP address of the remote candidate. It is RECOMMENDED that the agent defer creating a TURN channel until ICE completes, in which case permissions for connectivity checks are normally created using a CreatePermission request. Once it's established, the agent MUST keep the permission active until ICE concludes ๐
7.1.2. Sending the Request ๐จ
The check is generated by sending a Binding request from a local candidate to a remote candidate. [RFC5389] describes how Binding requests are constructed and generated. A connectivity check MUST utilize the STUN short-term credential mechanism. Support for backwards compatibility with RFC 3489 MUST NOT be used or assumed with connectivity checks. The FINGERPRINT mechanism MUST be used for connectivity checks ๐
ICE extends STUN by defining several new attributes, including PRIORITY, USE-CANDIDATE, ICE-CONTROLLED, and ICE-CONTROLLING. These new attributes are formally defined in Section 19.1, and their usage is described in the subsections below. These STUN extensions only apply to connectivity checks used for ICE ๐ธ
7.1.2.1. PRIORITY and USE-CANDIDATE
Okay bestie, first things first: every agent MUST include the PRIORITY attribute in its Binding request ๐. That value MUST be set equal to the priority a peer reflexive candidate would get from the algorithm in Section 4.1.2, if one gets learned from this check (peek Section 7.1.3.2.1 for how peer reflexive candidates get learned โจ). It's computed exactly the same way as the local candidate's priority for the pair, except the type preference is set to the peer reflexive value ๐
.
The controlling agent MAY include the USE-CANDIDATE attribute in the Binding request ๐ฆ. The controlled agent MUST NOT include it in its Binding request ๐. This one basically says "okay, we're done checking this component, let's use the pair from this check" ๐. Section 8.1.1 has guidance on when to include it ๐ธ.
7.1.2.2. ICE-CONTROLLED and ICE-CONTROLLING
The agent MUST include the ICE-CONTROLLED attribute in the request if it's in the controlled role, and MUST include the ICE-CONTROLLING attribute if it's in the controlling role ๐. The content of either attribute MUST be the tie-breaker determined in Section 5.2 ๐ซถ. Full details live in Section 19.1 โจ.
7.1.2.3. Forming Credentials
A Binding request serving as a connectivity check MUST utilize the STUN short-term credential mechanism ๐. The username is formed by concatenating the username fragment the peer gave you with your own username fragment, separated by a colon (:) ๐ง. The password is just the password the peer gave you ๐.
Example time! ๐ Say agent L is the offerer and agent R is the answerer. L included a username fragment of LFRAG for its candidates and a password of LPASS. R provided a username fragment of RFRAG and a password of RPASS. So a connectivity check from L to R uses the username RFRAG:LFRAG and a password of RPASS. A check from R to L uses the username LFRAG:RFRAG and a password of LPASS. The responses use the same usernames and passwords as the requests (ngl the USERNAME attribute isn't present in the response, so don't go looking for it ๐).
7.1.2.4. DiffServ Treatment
If the agent is using Diffserv Codepoint markings [RFC2475] in its media packets, it SHOULD apply those same markings to its connectivity checks ๐ธ.
7.1.3. Processing the Response
When a Binding response comes in, it gets correlated to its Binding request using the transaction ID, as defined in [RFC5389] ๐ . That ties it right back to the candidate pair the Binding request was sent for. This section defines extra procedures for processing Binding responses that are specific to this usage of STUN โจ.
7.1.3.1. Failure Cases
Okay, the drama ๐ญ. If the STUN transaction generates a 487 (Role Conflict) error response, the agent checks whether it included the ICE-CONTROLLED or ICE-CONTROLLING attribute in the Binding request. If the request had ICE-CONTROLLED, the agent MUST switch to the controlling role if it hasn't already. If the request had ICE-CONTROLLING, the agent MUST switch to the controlled role if it hasn't already. Once it has switched, the agent MUST enqueue the candidate pair whose check generated the 487 into the triggered check queue. That pair's state is set to Waiting. When the triggered check is sent, it'll contain an ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting its new role. Note, though: the tie-breaker value MUST NOT be reselected ๐ฅ.
A change in roles will require an agent to recompute pair priorities (Section 5.7.2), since those priorities are a function of the controlling and controlled roles. The change in role will also impact whether the agent is responsible for selecting nominated pairs and generating updated offers upon conclusion of ICE ๐.
Agents MAY support receipt of ICMP errors for connectivity checks ๐ฆ. If the STUN transaction generates an ICMP error, the agent sets the state of the pair to Failed. If the STUN transaction generates a STUN error response that is unrecoverable (as defined in [RFC5389]) or times out, the agent sets the state of the pair to Failed.
The agent MUST check that the source IP address and port of the response equal the destination IP address and port to which the Binding request was sent, and that the destination IP address and port of the response match the source IP address and port from which the Binding request was sent. In other words, the source and destination transport addresses in the requests and responses are symmetric ๐ช. If they are not symmetric, the agent sets the state of the pair to Failed ๐.
7.1.3.2. Success Cases
A check is considered a success if all of the following are true ๐:
- The STUN transaction generated a success response.
- The source IP address and port of the response equals the destination IP address and port to which the Binding request was sent.
- The destination IP address and port of the response match the source IP address and port from which the Binding request was sent.
7.1.3.2.1. Discovering Peer Reflexive Candidates
The agent checks the mapped address from the STUN response. If the transport address doesn't match any of the local candidates the agent knows about, the mapped address represents a new candidate, a peer reflexive candidate ๐ฆ. Like other candidates, it has a type, base, priority, and foundation, computed as follows:
- Its type is equal to peer reflexive.
- Its base is set equal to the local candidate of the candidate pair from which the STUN check was sent.
- Its priority is set equal to the value of the PRIORITY attribute in the Binding request.
- Its foundation is selected as described in Section 4.1.1.3.
This peer reflexive candidate is then added to the list of local candidates for the media stream. Its username fragment and password are the same as all the other local candidates for that media stream ๐ง.
However, the peer reflexive candidate is not paired with other remote candidates. This isn't necessary, because a valid pair will be generated from it momentarily based on the procedures in Section 7.1.3.2.2 โจ. If an agent wishes to pair the peer reflexive candidate with other remote candidates besides the one in the valid pair that will be generated, the agent MAY generate an updated offer which includes the peer reflexive candidate. This will cause it to be paired with all other remote candidates ๐ .
7.1.3.2.2. Constructing a Valid Pair
The agent constructs a candidate pair whose local candidate equals the mapped address of the response, and whose remote candidate equals the destination address to which the request was sent. This is called a valid pair, since it has been validated by a STUN connectivity check ๐ซถ. The valid pair may equal the pair that generated the check, may equal a different pair in the check list, or may be a pair not currently on any check list. If the pair equals the pair that generated the check or is on a check list currently, it is also added to the VALID LIST, which is maintained by the agent for each media stream. This list is empty at the start of ICE processing, and fills as checks are performed, resulting in valid candidate pairs ๐.
It will be very common that the pair is not on any check list ๐. Recall that the check list has pairs whose local candidates are never server reflexive. Those pairs had their local candidates converted to the base of the server reflexive candidates, and then pruned if they were redundant. When the response to the STUN check arrives, the mapped address will be reflexive if there is a NAT between the two. In that case, the valid pair will have a local candidate that doesn't match any of the pairs in the check list ๐.
If the pair is not on any check list, the agent computes the priority for the pair based on the priority of each candidate, using the algorithm in Section 5.7. The priority of the local candidate depends on its type. If it is not peer reflexive, it is equal to the priority signaled for that candidate in the SDP. If it is peer reflexive, it is equal to the PRIORITY attribute the agent placed in the Binding request that just completed. The priority of the remote candidate is taken from the SDP of the peer. If the candidate does not appear there, then the check must have been a triggered check to a new remote candidate. In that case, the priority is taken as the value of the PRIORITY attribute in the Binding request that triggered the check that just completed. The pair is then added to the VALID LIST ๐.
7.1.3.2.3. Updating Pair States
The agent sets the state of the pair that generated the check to Succeeded ๐ฅณ. Note that the pair which generated the check may be different than the valid pair constructed in Section 7.1.3.2.2 as a consequence of the response. The success of this check might also cause the state of other checks to change as well ๐ฅ. The agent MUST perform the following two steps:
- The agent changes the states for all other Frozen pairs for the same media stream and same foundation to
Waiting. Typically, but not always, these other pairs will have different component IDs. - If there is a pair in the valid list for every component of this media stream (where this is the actual number of components being used, in cases where the number of components signaled in the SDP differs from offerer to answerer), the success of this check may unfreeze checks for other media streams. Note that this step is followed not just the first time the valid list under consideration has a pair for every component, but every subsequent time a check succeeds and adds yet another pair to that valid list. The agent examines the check list for each other media stream in turn:
- If the check list is active, the agent changes the state of all Frozen pairs in that check list whose foundation matches a pair in the valid list under consideration to
Waiting. - If the check list is frozen, and there is at least one pair in the check list whose foundation matches a pair in the valid list under consideration, the state of all pairs in the check list whose foundation matches a pair in the valid list under consideration is set to
Waiting. This will cause the check list to become active, and ordinary checks will begin for it, as described in Section 5.8 ๐ฆ. - If the check list is frozen, and there are no pairs in the check list whose foundation matches a pair in the valid list under consideration, the agent:
- groups together all of the pairs with the same foundation, and
- for each group, sets the state of the pair with the lowest component ID to
Waiting. If there is more than one such pair, the one with the highest priority is used ๐.
- If the check list is active, the agent changes the state of all Frozen pairs in that check list whose foundation matches a pair in the valid list under consideration to
7.1.3.2.4. Updating the Nominated Flag โจ
okay so if the agent was a controlling agent and it put a USE-CANDIDATE attribute in the Binding request, the valid pair from that check gets its nominated flag set to true ๐. This flag basically says "use this pair for media if it's the highest-priority one with the nominated flag on" ๐
. It might wrap up ICE for this media stream or for all of them, so check out Section 8 ๐ฆ.
If the agent is the controlled agent, the response could come from a triggered check that got sent because of a request that itself had USE-CANDIDATE ๐ซถ. That's covered in Section 7.2.1.5, and it might now set the nominated flag on the pair learned from the original request ๐ฅ.
7.1.3.3. Check List and Timer State Updates ๐ธ
Regardless of whether the check worked or failed, finishing the transaction might mean updating the check list and timer states ๐ง.
If all of the pairs in the check list are now either Failed or Succeeded:
- If there isn't a pair in the valid list for each component of the media stream, the state of the check list gets set to Failed ๐.
- For each frozen check list, the agent:
- groups together all of the pairs with the same foundation, and
- for each group, sets the state of the pair with the lowest component ID to Waiting. If there's more than one such pair, the one with the highest priority is the one that gets used ๐.
If none of the pairs in the check list are in the Waiting or Frozen state, the check list is no longer active and won't count toward N when computing timers for ordinary checks, as described in Section 5.8 โฐ.
7.2. STUN Server Procedures ๐
An agent MUST be ready to receive a Binding request on the base of each candidate it included in its most recent offer or answer ๐. This requirement holds even if the peer is a lite implementation ๐.
The agent MUST use a short-term credential to authenticate the request and perform a message integrity check ๐. The agent MUST consider the username valid if it's two values separated by a colon, where the first value matches the username fragment the agent generated in an offer or answer for a session in progress โจ. It's possible (and honestly very likely ๐) that an offerer gets a Binding request before it receives the answer from its peer. If that happens, the agent MUST immediately generate a response, including computing the mapped address as described in Section 7.2.1.2. The agent has enough info at this point to make the response, so the password from the peer isn't required ๐. Once the answer arrives, it MUST proceed with the remaining steps, namely 7.2.1.3, 7.2.1.4, and 7.2.1.5 for full implementations ๐ . If multiple STUN requests show up before the answer, several pairs might get queued up in the triggered check queue ๐.
An agent MUST NOT use the ALTERNATE-SERVER mechanism, and MUST NOT support the backwards-compatibility mechanisms to RFC 3489 ๐ . It MUST use the FINGERPRINT mechanism ๐.
If the agent is using Diffserv Codepoint markings [RFC2475] in its media packets, it SHOULD apply those same markings to its responses to Binding requests ๐. The same would apply to any layer 2 markings the endpoint might be applying to media packets ๐ฆ.
7.2.1. Additional Procedures for Full Implementations ๐
This subsection defines the extra server procedures that apply to full implementations ๐.
7.2.1.1. Detecting and Repairing Role Conflicts ๐
Normally, the role-selection rules in Section 5.2 mean each agent picks a different role, one controlling and one controlled ๐. But in unusual call flows, typically the ones using third party call control, both agents can end up picking the same role ๐. This section explains how to check for that and fix it ๐ ๏ธ.
An agent MUST examine the Binding request for either ICE-CONTROLLING or ICE-CONTROLLED. It MUST follow these procedures:
- If neither
ICE-CONTROLLINGnorICE-CONTROLLEDis present in the request, the peer agent may have implemented an earlier version of this specification. There may be a conflict, but it can't be detected ๐ต๏ธ. - If the agent is in the controlling role, and
ICE-CONTROLLINGis present in the request:- If the agent's tie-breaker is larger than or equal to the contents of
ICE-CONTROLLING, the agent generates a Binding error response and includes anERROR-CODEattribute with a value of 487 (Role Conflict) but keeps its role ๐ . - If the agent's tie-breaker is less than the contents of
ICE-CONTROLLING, the agent switches to the controlled role ๐.
- If the agent's tie-breaker is larger than or equal to the contents of
- If the agent is in the controlled role, and
ICE-CONTROLLEDis present in the request:- If the agent's tie-breaker is larger than or equal to the contents of
ICE-CONTROLLED, the agent switches to the controlling role ๐. - If the agent's tie-breaker is less than the contents of
ICE-CONTROLLED, the agent generates a Binding error response and includes anERROR-CODEattribute with a value of 487 (Role Conflict) but keeps its role ๐ .
- If the agent's tie-breaker is larger than or equal to the contents of
- If the agent is in the controlled role and
ICE-CONTROLLINGwas present in the request, or the agent was in the controlling role andICE-CONTROLLEDwas present in the request, there is no conflict โจ.
A change in roles means the agent has to recompute pair priorities (Section 5.7.2), since those priorities depend on the controlling and controlled roles ๐งฎ. The role change also affects whether the agent is responsible for selecting nominated pairs and generating updated offers when ICE concludes ๐ .
The remaining sections in Section 7.2.1 are followed if the server generated a successful response to the Binding request, even if the agent changed roles ๐ซถ.
7.2.1.2. Computing Mapped Address ๐บ๏ธ
For requests received on a relayed candidate, the source transport address used for STUN processing (namely, generating the XOR-MAPPED-ADDRESS attribute) is the transport address as seen by the TURN server ๐ฐ๏ธ. That source transport address will be present in the XOR-PEER-ADDRESS attribute of a Data Indication message, if the Binding request was delivered through a Data Indication ๐ฌ. If the Binding request was delivered through a ChannelData message, the source transport address is the one that was bound to the channel ๐.
7.2.1.3. Learning Peer Reflexive Candidates ๐ต๏ธโโ๏ธ
If the source transport address of the request doesn't match any existing remote candidates, it represents a brand new peer reflexive remote candidate ๐. This candidate gets built like this:
- The priority of the candidate is set to the
PRIORITYattribute from the request. - The type of the candidate is set to peer reflexive.
- The foundation of the candidate is set to an arbitrary value, different from the foundation for all other remote candidates. If any later offer/answer exchanges contain this peer reflexive candidate in the SDP, it will signal the actual foundation for the candidate ๐.
- The component ID of this candidate is set to the component ID for the local candidate to which the request was sent ๐งฉ.
This candidate is added to the list of remote candidates. However, the agent does not pair this candidate with any local candidates ๐ .
7.2.1.4. Triggered Checks ๐ซ
Next, the agent constructs a pair whose local candidate is equal to the transport address on which the STUN request was received, and a remote candidate equal to the source transport address where the request came from (which may be the peer reflexive remote candidate that was just learned) ๐ฏ. The local candidate will either be a host candidate (for cases where the request was not received through a relay) or a relayed candidate (for cases where it is received through a relay) ๐. The local candidate can never be a server reflexive candidate ๐ซ. Since both candidates are known to the agent, it can obtain their priorities and compute the candidate pair priority ๐งฎ. This pair is then looked up in the check list. There can be one of several outcomes:
- If the pair is already on the check list:
- If the state of that pair is Waiting or Frozen, a check for that pair is enqueued into the triggered check queue if it isn't already present โณ.
- If the state of that pair is In-Progress, the agent cancels the in-progress transaction. Cancellation means that the agent will not retransmit the request, will not treat the lack of response to be a failure, but will wait the duration of the transaction timeout for a response. In addition, the agent MUST create a new connectivity check for that pair (representing a new STUN Binding request transaction) by enqueueing the pair in the triggered check queue. The state of the pair is then changed to Waiting ๐.
- If the state of the pair is Failed, it is changed to Waiting and the agent MUST create a new connectivity check for that pair (representing a new STUN Binding request transaction), by enqueueing the pair in the triggered check queue ๐.
- If the state of that pair is Succeeded, nothing further is done โ .
These steps are there to help ICE finish quickly when both agents are behind NAT ๐งฑ.
- If the pair is not already on the check list:
- The pair is inserted into the check list based on its priority ๐.
- Its state is set to Waiting.
- The pair is enqueued into the triggered check queue.
When a triggered check is to be sent, it is constructed and processed as described in Section 7.1.2 ๐ ๏ธ. These procedures require the agent to know the transport address, username fragment, and password for the peer ๐. The username fragment for the remote candidate is equal to the part after the colon of the USERNAME in the Binding request that was just received. Using that username fragment, the agent can check the SDP messages received from its peer (there may be more than one in cases of forking) and find this username fragment ๐ด. The corresponding password is then selected ๐.
7.2.1.5. Updating the Nominated Flag ๐ฉ
If the Binding request received by the agent had the USE-CANDIDATE attribute set, and the agent is in the controlled role, the agent looks at the state of the pair computed in Section 7.2.1.4 ๐:
- If the state of this pair is Succeeded, it means that the check generated by this pair produced a successful response. That would have caused the agent to construct a valid pair when that success response was received (see Section 7.1.3.2.2) ๐. The agent now sets the nominated flag in the valid pair to true. This may end ICE processing for this media stream; see Section 8 ๐.
- If the state of this pair is In-Progress, if its check produces a successful result, the resulting valid pair has its nominated flag set when the response arrives. This may end ICE processing for this media stream when it arrives; see Section 8 โฐ.
7.2.2. Additional Procedures for Lite Implementations ๐ฆ
okay so if the check that just came in had a USE-CANDIDATE attribute, the agent builds a candidate pair where the local candidate is the transport address the request was received on, and the remote candidate is the source transport address of that request ๐. this pair gets an arbitrary priority and goes into a list of valid candidates called the valid list ๐. the agent sets the nominated flag for that pair to true โจ. ICE processing is considered complete for a media stream if the valid list has a candidate pair for each component ๐.
8. Concluding ICE Processing โจ
this section is all about how an agent wraps ICE up ๐.
8.1. Procedures for Full Implementations ๐
concluding ICE is about the controlling agent nominating pairs and updating its state machinery ๐ฅ.
8.1.1. Nominating Pairs
the controlling agent nominates pairs to be selected by ICE using one of two techniques: regular nomination or aggressive nomination ๐. if its peer has a lite implementation, an agent MUST use a regular nomination algorithm ๐. if its peer is using ICE options (present in an ice-options attribute from the peer) that the agent doesn't understand, the agent MUST use a regular nomination algorithm ๐. if its peer is a full implementation and isn't using any ICE options, or is using ICE options understood by the agent, the agent MAY use either the aggressive or the regular nomination algorithm โจ. however, the regular algorithm is RECOMMENDED since it provides greater stability ๐ซถ.
8.1.1.1. Regular Nomination
with regular nomination, the agent lets some number of checks complete, and each of those checks omits the USE-CANDIDATE attribute ๐ธ. once one or more checks complete successfully for a component of a media stream, valid pairs are generated and added to the valid list ๐ฆ. the agent lets the checks continue until some stopping criterion is met, and then picks amongst the valid pairs based on an evaluation criterion ๐ง. the criteria for stopping the checks and for evaluating the valid pairs is entirely a matter of local optimization ๐
.
when the controlling agent selects the valid pair, it repeats the check that produced this valid pair (by enqueuing the pair that generated the check into the triggered check queue), this time with the USE-CANDIDATE attribute ๐ฅ. this check should succeed (since the previous one did), causing the nominated flag of that and only that pair to be set โจ. consequently, there will be only a single nominated pair in the valid list for each component, and when the state of the check list moves to Completed, that exact pair is selected by ICE for sending and receiving media for that component ๐.
regular nomination provides the most flexibility, since the agent has control over the stopping and selection criteria for checks ๐. the only requirement is that the agent MUST eventually pick one and only one candidate pair and generate a check for that pair with the USE-CANDIDATE attribute present ๐. regular nomination also improves ICE's resilience to variations in implementation (see Section 14) ๐ก๏ธ. regular nomination is also more stable, allowing both agents to converge on a single pair for media without any transient selections, which can happen with the aggressive algorithm ๐ซถ. the drawback of regular nomination is that it is guaranteed to increase latencies because it requires an additional check to be done ๐ญ.
8.1.1.2. Aggressive Nomination
with aggressive nomination, the controlling agent includes the USE-CANDIDATE attribute in every check it sends ๐ฅ. once the first check for a component succeeds, it will be added to the valid list and have its nominated flag set ๐
. when all components have a nominated pair in the valid list, media can begin to flow using the highest priority nominated pair ๐. however, because the agent included the USE-CANDIDATE attribute in all of its checks, another check may yet complete, causing another valid pair to have its nominated flag set ๐. ICE always selects the highest-priority nominated candidate pair from the valid list as the one used for media โจ. consequently, the selected pair may actually change briefly as ICE checks complete, resulting in a set of transient selections until it stabilizes ๐.
8.1.2. Updating States
for both controlling and controlled agents, the state of ICE processing depends on the presence of nominated candidate pairs in the valid list and on the state of the check list ๐ง . note that, at any time, more than one of the following cases can apply ๐:
- If there are no nominated pairs in the valid list for a media stream and the state of the check list is Running, ICE processing continues ๐โโ๏ธ.
- If there is at least one nominated pair in the valid list for a media stream and the state of the check list is Running:
- The agent MUST remove all Waiting and Frozen pairs in the check list and triggered check queue for the same component as the nominated pairs for that media stream ๐๏ธ.
- If an In-Progress pair in the check list is for the same component as a nominated pair, the agent SHOULD cease retransmissions for its check if its pair priority is lower than the lowest-priority nominated pair for that component ๐.
- Once there is at least one nominated pair in the valid list for every component of at least one media stream and the state of the check list is Running:
- The agent MUST change the state of processing for its check list for that media stream to Completed ๐.
- The agent MUST continue to respond to any checks it may still receive for that media stream, and MUST perform triggered checks if required by the processing of Section 7.2 ๐ซถ.
- The agent MUST continue retransmitting any In-Progress checks for that check list ๐.
- The agent MAY begin transmitting media for this media stream as described in Section 11.1 ๐ถ.
- Once the state of each check list is Completed:
- The agent sets the state of ICE processing overall to Completed ๐.
- If an agent is controlling, it examines the highest-priority nominated candidate pair for each component of each media stream. If any of those candidate pairs differ from the default candidate pairs in the most recent offer/answer exchange, the controlling agent MUST generate an updated offer as described in Section 9 ๐จ. If the controlling agent is using an aggressive nomination algorithm, this may result in several updated offers as the pairs selected for media change ๐. An agent MAY delay sending the offer for a brief interval (one second is RECOMMENDED) in order to allow the selected pairs to stabilize โณ.
- If the state of the check list is Failed, ICE has not been able to complete for this media stream. The correct behavior depends on the state of the check lists for other media streams ๐ค:
- If all check lists are Failed, ICE processing overall is considered to be in the Failed state, and the agent SHOULD consider the session a failure, SHOULD NOT restart ICE, and the controlling agent SHOULD terminate the entire session ๐.
- If at least one of the check lists for other media streams is Completed, the controlling agent SHOULD remove the failed media stream from the session in its updated offer ๐๏ธ.
- If none of the check lists for other media streams are Completed, but at least one is Running, the agent SHOULD let ICE continue ๐โโ๏ธ.
8.2. Procedures for Lite Implementations ๐ธ
concluding ICE for a lite implementation is relatively straightforward ๐ . there are two cases to consider:
- The implementation is lite, and its peer is full.
- The implementation is lite, and its peer is lite.
the effect of ICE concluding is that the agent can free any allocated host candidates that were not utilized by ICE, as described in Section 8.3 ๐งน.
8.2.1. Peer Is Full
in this case, the agent will receive connectivity checks from its peer ๐ฌ. when an agent has received a connectivity check that includes the USE-CANDIDATE attribute for each component of a media stream, the state of ICE processing for that media stream moves from Running to Completed ๐. when the state of ICE processing for all media streams is Completed, the state of ICE processing overall is Completed ๐.
the lite implementation will never itself determine that ICE processing has failed for a media stream; rather, the full peer will make that determination and then remove or restart the failed media stream in a subsequent offer ๐ โโ๏ธ.
8.2.2. Peer Is Lite
once the offer/answer exchange has completed, both agents examine their candidates and those of its peer ๐. for each media stream, each agent pairs up its own candidates with the candidates of its peer for that media stream ๐ซ. two candidates are paired up when they are for the same component, utilize the same transport protocol (UDP in this specification), and are from the same IP address family (IPv4 or IPv6) ๐.
- If there is a single pair per component, that pair is added to the Valid list. If all of the components for a media stream had one pair, the state of ICE processing for that media stream is set to Completed. If all media streams are Completed, the state of ICE processing is set to Completed overall. This will always be the case for implementations that are IPv4 only ๐.
- If there is more than one pair per component:
- The agent MUST select a pair based on local policy. Since this case only arises for IPv6, it is RECOMMENDED that an agent follow the procedures of RFC 3484 [RFC3484] to select a single pair ๐.
- The agent adds the selected pair for each component to the valid list. As described in Section 11.1, this will permit media to begin flowing. However, it is possible (and in fact likely) that both agents have chosen different pairs ๐ฌ.
- To reconcile this, the controlling agent MUST send an updated offer as described in Section 9.1.3, which will include the
remote-candidatesattribute ๐. - The agent MUST NOT update the state of ICE processing when the offer is sent. If this subsequent offer completes, the controlling agent MUST change the state of ICE processing to Completed for all media streams, and the state of ICE processing overall to Completed. The states for the controlled agent are set based on the logic in Section 9.2.3 ๐.
8.3. Freeing Candidates ๐ฆ
8.3.1. Full Implementation Procedures โจ
okay so the procedures in Section 8 require that an agent keeps listening for STUN requests and keeps generating triggered checks for a media stream, even after processing for that stream finishes ๐. The rules in this section explain when it's safe for an agent to stop sending or receiving checks on a candidate ICE didn't select, and then free that candidate ๐งน.
When ICE is used with SIP and an offer gets forked to multiple recipients, ICE runs in parallel and independently with each answerer, all using the same local candidates ๐ซ. Once ICE processing has hit the Completed state for all peers for media streams using those candidates, the agent SHOULD wait an additional three seconds โณ, and then it MAY stop responding to checks or generating triggered checks on that candidate. It MAY free the candidate at that point. Freeing server reflexive candidates is never explicit. It just happens by lack of a keepalive ๐ฅ. The three-second delay is there for when aggressive nomination is used, because the selected pairs can switch up quickly after ICE has completed ๐โโ๏ธ.
8.3.2. Lite Implementation Procedures
A lite implementation MAY free candidates not selected by ICE as soon as ICE processing has reached the Completed state for all peers for all media streams using those candidates ๐ธ.
9. Subsequent Offer/Answer Exchanges ๐
Either agent MAY generate a subsequent offer at any time allowed by RFC 3264 [RFC3264]. The rules in Section 8 will cause the controlling agent to send an updated offer at the end of ICE processing when ICE has picked different candidate pairs than the default pairs ๐ . This section defines the rules for building subsequent offers and answers.
If a subsequent offer gets rejected, ICE processing carries on as though that offer had never been made ๐.
9.1. Generating the Offer
9.1.1. Procedures for All Implementations
9.1.1.1. ICE Restarts
An agent MAY restart ICE processing for an existing media stream. An ICE restart, as the name suggests, flushes all previous states of ICE processing and starts checks over from scratch ๐. The only difference between an ICE restart and a brand new media session is that, during the restart, media can keep being sent to the previously validated pair ๐ฆ.
An agent MUST restart ICE for a media stream if:
- The offer is being generated for the purposes of changing the target of the media stream. In other words, if an agent wants to generate an updated offer that, had ICE not been in use, would result in a new value for the destination of a media component.
- An agent is changing its implementation level. This typically only happens in third party call control use cases, where the entity doing the signaling is not the entity receiving the media, and it has changed the target of media mid-session to another entity that has a different ICE implementation.
These rules imply that setting the IP address in the c line to 0.0.0.0 will cause an ICE restart ๐. Because of that, ICE implementations MUST NOT use this mechanism for call hold, and instead MUST use a=inactive and a=sendonly as described in [RFC3264].
To restart ICE, an agent MUST change both the ice-pwd and the ice-ufrag for the media stream in an offer. It's totally allowed to use a session-level attribute in one offer and then give the same ice-pwd or ice-ufrag as a media-level attribute in a later offer. That's not a change in password, just a change in how it's written down, so it does not cause an ICE restart ๐.
An agent sets the rest of the fields in the SDP for this media stream as it would in an initial offer of this media stream (see Section 4.3). So the set of candidates MAY include some, none, or all of the previous candidates for that stream, and MAY also include a totally new set of candidates gathered as described in Section 4.1.1 โจ.
9.1.1.2. Removing a Media Stream
If an agent removes a media stream by setting its port to zero, it MUST NOT include any candidate attributes for that media stream and SHOULD NOT include any other ICE-related attributes defined in Section 15 for that media stream ๐๏ธ.
9.1.1.3. Adding a Media Stream
If an agent wants to add a new media stream, it sets the fields in the SDP for this media stream as if this was an initial offer for that media stream (see Section 4.3). This kicks off ICE processing for this media stream ๐.
9.1.2. Procedures for Full Implementations
This section covers extra procedures for full implementations, specifically for existing media streams ๐.
The username fragments, password, and implementation level MUST remain the same as used previously. If an agent needs to change one of these, it MUST restart ICE for that media stream ๐.
What else you do depends on the state ICE processing is in for that media stream.
9.1.2.1. Existing Media Streams with ICE Running
If an agent generates an updated offer that includes a media stream that was previously established, and for which ICE checks are in the Running state, the agent follows the procedures defined here ๐โโ๏ธ.
An agent MUST include candidate attributes for all local candidates it had signaled previously for that media stream. The properties of that candidate as signaled in SDP (the priority, foundation, type, and related transport address) SHOULD remain the same. The IP address, port, and transport protocol, which fundamentally identify that candidate, MUST remain the same (if they change, it would be a new candidate). The component ID MUST remain the same. The agent MAY include additional candidates it did not offer previously, but which it has gathered since the last offer/answer exchange, including peer reflexive candidates ๐งฉ.
The agent MAY change the default destination for media. As with initial offers, there MUST be a set of candidate attributes in the offer matching this default destination ๐ฏ.
9.1.2.2. Existing Media Streams with ICE Completed
If an agent generates an updated offer that includes a media stream that was previously established, and for which ICE checks are in the Completed state, the agent follows the procedures defined here ๐.
The default destination for media (i.e., the values of the IP addresses and ports in the m and c lines used for that media stream) MUST be the local candidate from the highest-priority nominated pair in the valid list for each component. This "fixes" the default destination for media to equal the destination ICE has selected for media ๐.
The agent MUST include candidate attributes for candidates matching the default destination for each component of the media stream, and MUST NOT include any other candidates ๐ซถ.
Also, if the agent is controlling, it MUST include the a=remote-candidates attribute for each media stream whose check list is in the Completed state. The attribute contains the remote candidates from the highest-priority nominated pair in the valid list for each component of that media stream. It's needed to avoid a race condition where the controlling agent chooses its pairs, but the updated offer beats the connectivity checks to the controlled agent, which doesn't even know these pairs are valid, let alone selected ๐ญ. See Appendix B.6 for elaboration on this race condition ๐.
9.1.3. Procedures for Lite Implementations
9.1.3.1. Existing Media Streams with ICE Running
This section describes procedures for lite implementations for existing streams where ICE is running ๐.
A lite implementation MUST include all of its candidates for each component of each media stream in an a=candidate attribute in any subsequent offer. These candidates are formed identically to the procedures for initial offers, as described in Section 4.2 ๐.
A lite implementation MUST NOT add additional host candidates in a subsequent offer. If an agent needs to offer additional candidates, it MUST restart ICE ๐ซ.
The username fragments, password, and implementation level MUST remain the same as used previously. If an agent needs to change one of these, it MUST restart ICE for that media stream ๐.
9.1.3.2. Existing Media Streams with ICE Completed
If ICE has completed for a media stream, the default destination for that media stream MUST be set to the remote candidate of the candidate pair for that component in the valid list. For a lite implementation, there is always just a single candidate pair in the valid list for each component of a media stream. Additionally, the agent MUST include a candidate attribute for each default destination ๐.
Additionally, if the agent is controlling (which only happens when both agents are lite), the agent MUST include the a=remote-candidates attribute for each media stream. The attribute contains the remote candidates from the candidate pairs in the valid list (one pair for each component of each media stream) ๐ง.
9.2. Receiving the Offer and Generating an Answer
9.2.1. Procedures for All Implementations
When receiving a subsequent offer within an existing session, an agent MUST reapply the verification procedures in Section 5.1 without regard to the results of verification from any previous offer/answer exchanges. Honestly, a previous exchange might have ended with ICE not being used, and then it gets used because of a later exchange, so this step matters ๐.
9.2.1.1. Detecting ICE Restart
If the offer contained a change in the a=ice-ufrag or a=ice-pwd attributes compared to the previous SDP from the peer, it means ICE is restarting for this media stream. If all media streams are restarting, then ICE is restarting overall ๐.
If ICE is restarting for a media stream:
- The agent MUST change the
a=ice-ufraganda=ice-pwdattributes in the answer. - The agent MAY change its implementation level in the answer.
An agent sets the rest of the fields in the SDP for this media stream as it would in an initial answer to this media stream (see Section 4.3). So the set of candidates MAY include some, none, or all of the previous candidates for that stream, and MAY also include a totally new set of candidates gathered as described in Section 4.1.1 ๐.
9.2.1.2. New Media Stream
If the offer contains a new media stream, the agent sets the fields in the answer as if it had received an initial offer containing that media stream (see Section 4.3). This kicks off ICE processing for this media stream ๐.
9.2.1.3. Removed Media Stream
If an offer contains a media stream whose port is zero, the agent MUST NOT include any candidate attributes for that media stream in its answer, and SHOULD NOT include any other ICE-related attributes defined in Section 15 for that media stream ๐ฅน.
9.2.2. Procedures for Full Implementations
Unless the agent has detected an ICE restart from the offer, the username fragments, password, and implementation level MUST remain the same as used previously. If an agent needs to change one of these, it MUST restart ICE for that media stream by generating an offer. ICE cannot be restarted in an answer ๐ โโ๏ธ.
Additional behaviors depend on the state of ICE processing for that media stream ๐ .
9.2.2.1. Existing Media Streams with ICE Running and no remote-candidates
If ICE is running for a media stream, and the offer for that media stream lacked the remote-candidates attribute, the rules for building the answer are identical to those for the offerer, as described in Section 9.1.2.1 ๐ฆ.
9.2.2.2. Existing Media Streams with ICE Completed and no remote-candidates
Okay so if ICE is Completed for a media stream AND the offer for that stream didn't have the remote-candidates attribute, the answer gets built exactly the way the offerer builds it in Section 9.1.2.2 ๐. The one difference: the answerer MUST NOT include the a=remote-candidates attribute in the answer. No cap, that's a hard no ๐
9.2.2.3. Existing Media Streams and remote-candidates
Okay this one is lowkey the drama ๐ญ A controlled agent gets an offer with the a=remote-candidates attribute for a media stream when its peer has wrapped up ICE processing for that stream. The attribute is in the offer to handle a race condition ๐ between two things: the offer showing up, and the Binding response that tells the answerer which candidate ICE is going to pick. Check out Appendix B.6 if you want the full tea on that race. So processing an offer with this attribute totally depends on who wins ๐
The agent builds a candidate pair for each component of the media stream like this:
- Set the remote candidate equal to the offerer's default destination for that component (e.g., the contents of the
mandclines for RTP, and thea=rtcpattribute for RTCP) - Set the local candidate equal to the transport address for that same component in the
a=remote-candidatesattribute in the offer.
Then the agent checks whether each of those pairs is in the valid list ๐. If a pair isn't there, the check has "lost" the race, and we call that pair a "losing pair" ๐.
Next, the agent finds all the pairs in the check list whose remote candidates equal the remote candidate in the losing pair:
- If none of those pairs are In-Progress, and at least one is Failed, it's most likely that a network failure, like a network partition or serious packet loss, has happened ๐ง๏ธ. The agent SHOULD generate an answer for this media stream as if the
remote-candidatesattribute had not been present, and then restart ICE for this stream. - If at least one of those pairs is In-Progress, the agent SHOULD wait for those checks to complete. As each one completes, redo the processing in this section until there are no losing pairs.
Once there are no losing pairs, the agent can finally generate the answer โจ. It MUST set the default destination for media to the candidates in the remote-candidates attribute from the offer (each of which will now be the local candidate of a candidate pair in the valid list). It MUST include a candidate attribute in the answer for each candidate in the remote-candidates attribute in the offer.
9.2.3. Procedures for Lite Implementations
Lite agents, you're up ๐ If the received offer contains the remote-candidates attribute for a media stream, the agent forms a candidate pair for each component of the media stream by:
- Setting the remote candidate equal to the offerer's default destination for that component (e.g., the contents of the
mandclines for RTP, and thea=rtcpattribute for RTCP). - Setting the local candidate equal to the transport address for that same component in the
a=remote-candidatesattribute in the offer.
Then it drops those candidates into the Valid list for the media stream, and the state of ICE processing for that stream is set to Completed ๐.
Now for the plot twist ๐ If the agent believed it was controlling, but the offer contained the remote-candidates attribute, then both agents believe they're controlling. In this case, both would have sent updated offers around the same time. BUT the signaling protocol carrying the offer/answer exchanges resolves this glare condition, so one agent is always the 'winner' by having its offer received before its peer has sent an offer. The winner takes the role of controlled, so the loser (the answerer under consideration in this section) MUST change its role to controlled ๐ซถ. Consequently, if the agent was going to send an updated offer because, based on the rules in Section 8.2.2, it was controlling, it no longer needs to ๐.
Besides the potential role change, the change in the Valid list, and the state changes, building the answer goes exactly like building an offer as described in Section 9.1.3 ๐ .
9.3. Updating the Check and Valid Lists ๐
9.3.1. Procedures for Full Implementations
9.3.1.1. ICE Restarts
The agent MUST remember the highest-priority nominated pairs in the Valid list for each component of the media stream, called the previous selected pairs, prior to the restart ๐. The agent will keep sending media using these pairs, as described in Section 11.1. Once these destinations are noted, the agent MUST flush the valid and check lists, and then recompute the check list and its states as described in Section 5.7 ๐งน.
9.3.1.2. New Media Stream
If the offer/answer exchange added a new media stream, the agent MUST create a new check list for it (and an empty Valid list to start of course ๐), as described in Section 5.7 ๐ฑ.
9.3.1.3. Removed Media Stream
If the offer/answer exchange removed a media stream, or an answer rejected an offered media stream, an agent MUST flush the Valid list for that media stream. It MUST terminate any STUN transactions in progress for that media stream. An agent MUST remove the check list for that media stream and cancel any pending ordinary checks for it. Bye bye ๐
9.3.1.4. ICE Continuing for Existing Media Stream
The valid list is not affected by an updated offer/answer exchange unless ICE is restarting ๐
If an agent is in the Running state for that media stream, the check list is updated (the check list is irrelevant if the state is completed ๐คท). To do that, the agent recomputes the check list using the procedures described in Section 5.7. If a pair on the new check list was also on the previous check list, and its state was Waiting, In-Progress, Succeeded, or Failed, its state is copied over. Otherwise, its state is set to Frozen ๐ง.
If none of the check lists are active (meaning that the pairs in each check list are Frozen), the full-mode agent sets the first pair in the check list for the first media stream to Waiting, and then sets the state of all other pairs in that check list for the same component ID and with the same foundation to Waiting as well ๐โโ๏ธ.
Next, the agent goes through each check list, starting with the highest-priority pair. If a pair has a state of Succeeded, and it has a component ID of 1, then all Frozen pairs in the same check list with the same foundation whose component IDs are not 1 have their state set to Waiting. If, for a particular check list, there are pairs for each component of that media stream in the Succeeded state, the agent moves the state of all Frozen pairs for the first component of all other media streams (and thus in different check lists) with the same foundation to Waiting ๐ธ.
9.3.2. Procedures for Lite Implementations
If ICE is restarting for a media stream, the agent MUST start a new Valid list for that media stream. It MUST remember the pairs in the previous Valid list for each component of the media stream, called the previous selected pairs, and continue to send media there as described in Section 11.1. The state of ICE processing for each media stream MUST change to Running, and the state of ICE processing MUST change to Running ๐
10. Keepalives ๐
All endpoints MUST send keepalives for each media session. These keepalives serve the purpose of keeping NAT bindings alive for the media session ๐. These keepalives MUST be sent regardless of whether the media stream is currently inactive, sendonly, recvonly, or sendrecv, and regardless of the presence or value of the bandwidth attribute. These keepalives MUST be sent even if ICE is not being utilized for the session at all ๐. The keepalive SHOULD be sent using a format that is supported by its peer. ICE endpoints allow for STUN-based keepalives for UDP streams, and as such, STUN keepalives MUST be used when an agent is a full ICE implementation and is communicating with a peer that supports ICE (lite or full). An agent can determine that its peer supports ICE by the presence of a=candidate attributes for each media session. If the peer does not support ICE, the choice of a packet format for keepalives is a matter of local implementation ๐ ๏ธ. A format that allows packets to easily be sent in the absence of actual media content is RECOMMENDED. Examples of formats that readily meet this goal are RTP No-Op [NO-OP-RTP], and in cases where both sides support it, RTP comfort noise [RFC3389]. If the peer doesn't support any formats that are particularly well suited for keepalives, an agent SHOULD send RTP packets with an incorrect version number, or some other form of error that would cause them to be discarded by the peer ๐
If there has been no packet sent on the candidate pair ICE is using for a media component for Tr seconds (where packets include those defined for the component (RTP or RTCP) and previous keepalives), an agent MUST generate a keepalive on that pair โฐ. Tr SHOULD be configurable and SHOULD have a default of 15 seconds. Tr MUST NOT be configured to less than 15 seconds. Alternatively, if an agent has a dynamic way to discover the binding lifetimes of the intervening NATs, it can use that value to determine Tr. Administrators deploying ICE in more controlled networking environments SHOULD set Tr to the longest duration possible in their environment.
If STUN is being used for keepalives, a STUN Binding Indication is used [RFC5389]. The Indication MUST NOT utilize any authentication mechanism. It SHOULD contain the FINGERPRINT attribute to aid in demultiplexing, but SHOULD NOT contain any other attributes. It is used solely to keep the NAT bindings alive. The Binding Indication is sent using the same local and remote candidates that are being used for media. Though Binding Indications are used for keepalives, an agent MUST be prepared to receive a connectivity check as well. If a connectivity check is received, a response is generated as discussed in [RFC5389], but there is no impact on ICE processing otherwise ๐ฆ
An agent MUST begin the keepalive processing once ICE has selected candidates for usage with media, or media begins to flow, whichever happens first. Keepalives end once the session terminates or the media stream is removed ๐
11. Media Handling ๐ฌ
11.1. Sending Media
Procedures for sending media differ for full and lite implementations ๐
11.1.1. Procedures for Full Implementations โจ
Okay so, agents always send media using a candidate pair, called the selected candidate pair ๐ The agent sends to the remote candidate in that pair (it sets the destination address and port of the packet to match that remote candidate) and sends from the local candidate of the pair ๐ฆ If the local candidate is server or peer reflexive, the media is originated from the base ๐ธ Media sent from a relayed candidate goes out from the base, through that TURN server, using the procedures in [RFC5766] ๐ง
If the local candidate is a relayed candidate, it is RECOMMENDED that an agent create a channel on the TURN server toward the remote candidate. This is done with the channel creation procedures in Section 11 of [RFC5766] ๐
The selected pair for a component of a media stream is:
- empty if the state of the check list for that media stream is Running, and there is no previous selected pair for that component due to an ICE restart
- equal to the previous selected pair for a component of a media stream if the state of the check list for that media stream is Running, and there was a previous selected pair for that component due to an ICE restart
- equal to the highest-priority nominated pair for that component in the valid list if the state of the check list is Completed
If the selected pair for at least one component of a media stream is empty, an agent MUST NOT send media for any component of that media stream. If the selected pair for each component of a media stream has a value, an agent MAY send media for all components of that media stream. ๐ So no half-sending, periodt ๐
Note that the selected pair for a component of a media stream may not equal the default pair for that same component from the most recent offer/answer exchange. When this happens, the selected pair is used for media, not the default pair ๐ When ICE first completes, if the selected pairs aren't a match for the default pairs, the controlling agent sends an updated offer/answer exchange to fix the mismatch. Until that updated offer arrives, there will not be a match. Furthermore, in very unusual cases, the default candidates in the updated offer/answer will not be a match either ๐ญ
11.1.2. Procedures for Lite Implementations ๐ซถ
A lite implementation MUST NOT send media until it has a Valid list that contains a candidate pair for each component of that media stream. Once that happens, the agent MAY begin sending media packets. To do that, it sends media to the remote candidate in the pair (setting the destination address and port of the packet equal to that remote candidate), and sends it from the local candidate ๐
11.1.3. Procedures for All Implementations ๐
ICE has interactions with jitter buffer adaptation mechanisms. An RTP stream can begin using one candidate and switch to another one, though this happens rarely with ICE. The newer candidate may result in RTP packets taking a different path through the network, one with different delay characteristics. As discussed below, agents are encouraged to re-adjust jitter buffers when there are changes in source or destination address of media packets. Also, many audio codecs use the marker bit to signal the beginning of a talkspurt, for the purposes of jitter buffer adaptation. For such codecs, it is RECOMMENDED that the sender set the marker bit [RFC3550] when an agent switches transmission of media from one candidate pair to another ๐งโจ
11.2. Receiving Media ๐
ICE implementations MUST be prepared to receive media on each component on any candidates provided for that component in the most recent offer/answer exchange (in the case of RTP, this would include both RTP and RTCP if candidates were provided for both). Main character energy for sure ๐
It is RECOMMENDED that, when an agent receives an RTP packet with a new source or destination IP address for a particular media stream, the agent re-adjusts its jitter buffers ๐ฆ
RFC 3550 [RFC3550] describes an algorithm in Section 8.2 for detecting synchronization source (SSRC) collisions and loops. These algorithms are based, in part, on seeing different source transport addresses with the same SSRC. However, when ICE is used, such changes will sometimes occur as the media streams switch between candidates. An agent will be able to determine that a media stream is from the same peer as a consequence of the STUN exchange that precedes media transmission. Thus, if there is a change in source transport address, but the media packets come from the same peer agent, this SHOULD NOT be treated as an SSRC collision. It's giving "not a collision, just vibes" ๐๐
12. Usage with SIP ๐
12.1. Latency Guidelines โฐ
ICE requires a series of STUN-based connectivity checks to take place between endpoints. These checks start from the answerer on generation of its answer, and start from the offerer when it receives the answer. These checks can take time to complete, so the selection of messages to use with offers and answers can affect perceived user latency. Two latency figures are of particular interest: the post-pickup delay and the post-dial delay. The post-pickup delay refers to the time between when a user "answers the phone" and when any speech they utter can be delivered to the caller. The post-dial delay refers to the time between when a user enters the destination address for the user and ringback begins as a consequence of having successfully started ringing the phone of the called party ๐ฑ
Two cases can be considered: one where the offer is present in the initial INVITE, and one where it is in a response ๐
12.1.1. Offer in INVITE ๐
To reduce post-dial delays, it is RECOMMENDED that the caller begin gathering candidates prior to actually sending its initial INVITE. This can start upon user interface cues that a call is pending, such as activity on a keypad or the phone going offhook ๐ฅ
If an offer is received in an INVITE request, the answerer SHOULD begin to gather its candidates on receipt of the offer, and then generate an answer in a provisional response once it has completed that process. ICE requires that a provisional response with an SDP be transmitted reliably. This can be done through the existing Provisional Response Acknowledgment (PRACK) mechanism [RFC3262] or through an optimization that is specific to ICE. With this optimization, provisional responses containing an SDP answer that begins ICE processing for one or more media streams can be sent reliably without RFC 3262. To do this, the agent retransmits the provisional response with the exponential backoff timers described in RFC 3262. Retransmits MUST cease on receipt of a STUN Binding request for one of the media streams signaled in that SDP (because receipt of a Binding request indicates the offerer has received the answer), or on transmission of the answer in a 2xx response. If the peer agent is lite, there will never be a STUN Binding request. In such a case, the agent MUST cease retransmitting the 18x after sending it four times (ICE will actually work even if the peer never receives the 18x; however, experience has shown that sending it is important for middleboxes and firewall traversal). If no Binding request is received prior to the last retransmit, the agent does not consider the session terminated ๐ซ
Despite the fact that the provisional response will be delivered reliably, the rules for when an agent can send an updated offer or answer do not change from those specified in RFC 3262. Specifically, if the INVITE contained an offer, the same answer appears in all of the 1xx and in the 2xx response to the INVITE. Only after that 2xx has been sent can an updated offer/answer exchange occur. This optimization SHOULD NOT be used if both agents support PRACK. Note that the optimization is very specific to provisional responses carrying answers that start ICE processing; it is not a general technique for 1xx reliability ๐ง
Alternatively, an agent MAY delay sending an answer until the 200 OK; however, this results in a poor user experience and is NOT RECOMMENDED ๐ โโ๏ธ
Once the answer has been sent, the agent SHOULD begin its connectivity checks. Once candidate pairs for each component of a media stream enter the valid list, the answerer can begin sending media on that media stream โจ
However, prior to this point, any media that needs to be sent towards the caller (such as SIP early media [RFC3960]) MUST NOT be transmitted. For this reason, implementations SHOULD delay alerting the called party until candidates for each component of each media stream have entered the valid list. In the case of a PSTN gateway, this means the setup message into the PSTN is delayed until this point. Doing this increases the post-dial delay, but has the effect of eliminating 'ghost rings'. Ghost rings are cases where the called party hears the phone ring, picks up, but hears nothing and cannot be heard ๐ป This technique works without requiring support for, or usage of, preconditions [RFC3312], since it's a localized decision. It also has the benefit of guaranteeing that not a single packet of media will get clipped, so that post-pickup delay is zero. If an agent chooses to delay local alerting in this way, it SHOULD generate a 180 response once alerting begins ๐
12.1.2. Offer in Response ๐
In addition to uses where the offer is in an INVITE and the answer is in the provisional and/or 200 OK response, ICE works with cases where the offer appears in the response. In such cases, which are common in third party call control [RFC3725], ICE agents SHOULD generate their offers in a reliable provisional response (which MUST utilize RFC 3262), and not alert the user on receipt of the INVITE. The answer will arrive in a PRACK. This allows ICE processing to take place prior to alerting, so there is no post-pickup delay, at the expense of increased call setup delays. Once ICE completes, the callee can alert the user and then generate a 200 OK when they answer. The 200 OK would contain no SDP, since the offer/answer exchange has completed ๐ฅฐ
Alternatively, agents MAY place the offer in a 2xx instead (in which case the answer comes in the ACK). When this happens, the callee will alert the user on receipt of the INVITE, and the ICE exchanges will take place only after the user answers. This reduces call setup delay, but can cause substantial post-pickup delays and media clipping ๐ฌ
12.2. SIP Option Tags and Media Feature Tags ๐ท๏ธ
[RFC5768] specifies a SIP option tag and media feature tag for usage with ICE. ICE implementations using SIP SHOULD support this specification, which uses a feature tag in registrations to facilitate interoperability through signaling intermediaries ๐ซง
12.3. Interactions with Forking ๐ด
ICE interacts very well with forking. Indeed, ICE fixes some of the problems associated with forking. Without ICE, when a call forks and the caller receives multiple incoming media streams, it cannot determine which media stream corresponds to which callee ๐
With ICE, this problem is resolved. The connectivity checks which occur prior to transmission of media carry username fragments, which in turn are correlated to a specific callee. Subsequent media packets that arrive on the same candidate pair as the connectivity check will be associated with that same callee. Thus, the caller can perform this correlation as long as it has received an answer ๐งฉ
12.4. Interactions with Preconditions โจ
okay so QoS preconditions (the ones from [RFC3312] and [RFC4032], RFC 3312 [RFC3312] and RFC 4032 [RFC4032] if we're being formal ๐ ) only care about the transport addresses listed as the default targets for media in an offer/answer. that's the whole scope, bestie ๐
if ICE switches up the transport address where media gets received, that change shows up in an updated offer that moves the default destination to match what ICE picked ๐ฆ from the outside it looks like any other re-INVITE, so the rules in RFCs 3312 and 4032 handle it completely. they don't care that the destination is changing because ICE is doing its thing "in the background" ๐
and here's the spicy bit: an agent SHOULD NOT indicate that QoS preconditions have been met until the checks have finished and picked the candidate pairs that'll actually carry media. no calling it done early, periodt ๐
ICE also has (on purpose!) interactions with connectivity preconditions [SDP-PRECON] ๐ง those are explained over there, so go check them out. and fyi, the procedures in Section 12.1 come with their own kind of "preconditions" too, but they're way less powerful than the explicit ones in [SDP-PRECON] ๐
12.5. Interactions with Third Party Call Control ๐
ICE gets along with Flows I, III, and IV from [RFC3725] ๐ธ Flow I works even if the controller has no idea ICE exists. Flow IV works as long as the controller passes the ICE attributes along without messing with them. Flow II is fundamentally incompatible with ICE, though ๐ญ each agent thinks it's the answerer, so neither one ever sends a re-INVITE. not a vibe.
the flows for continued operation, the ones in Section 7 of RFC 3725, need some extra ICE behavior to actually work ๐ฅ specifically, if an agent gets a mid-dialog re-INVITE that has no offer in it, it MUST restart ICE for each media stream and go gather new candidates all over again. and the list of candidates SHOULD include the ones that are currently being used for media, because obviously you want to keep those in the mix โจ
13. Relationship with ANAT ๐ฆ
okay ANAT is giving "dual stack, but make it old school" ๐ [RFC4091] (the Alternative Network Address Types (ANAT) Semantics for the SDP grouping framework) and [RFC4092] (its usage with SIP) let an agent say it can do both IPv4 and IPv6 for a media stream. how? by including two m lines, one for v4 and one for v6 ๐
this is kinda similar to ICE, which lets an agent list multiple transport addresses using the candidate attribute. the big difference is that ANAT picks between its options with static selection, while ICE uses a dynamic connectivity check ๐ง
this specification deprecates RFC 4091 and RFC 4092, so if you want dual stack now, the move is to use ICE ๐
14. Extensibility Considerations ๐งฉ
this spec makes very specific choices about how both agents in a session work together to land on the candidate pairs that get selected for media ๐ and future specs will definitely want to change those algorithms. could be tiny stuff like timer tweaks or bigger stuff like a whole revamp of the priority algorithm ๐ whenever that happens, keeping the two agents interoperable is critical, no cap ๐ญ
first up, ICE gives us the a=ice-options SDP attribute. every extension or change to ICE gets its own token. when an agent that supports one of those generates an offer or an answer, it MUST include that token in this attribute, so each side knows what the other side is up to ๐ซถ and this attribute MUST NOT be present if the agent doesn't support any ICE extensions or changes. no random tokens, bestie.
right now there's no IANA registry or registration procedure for these option tags. at the time of writing, it's still unclear whether ICE changes and extensions will be common enough to need a registry ๐คทโโ๏ธ
here's the complicated part ๐ ICE depends on a distributed algorithm running on both agents to converge on an agreed set of candidate pairs. if the two agents run different algorithms, it gets hard to guarantee they'll converge on the same pairs. the regular nomination procedure in Section 8 cuts down on that tight coordination by handing the whole selection algorithm to the controlling agent ๐ so when a controlling agent is talking to a peer that supports options it doesn't know about, the agent MUST run a regular nomination algorithm. with regular nomination, ICE converges just fine even when the two agents use different pair prioritization algorithms โจ a big part of that convergence is triggered checks, which make sure the nominated pair gets validated by both agents. so any future ICE enhancements MUST preserve triggered checks. non-negotiable ๐
ICE is also extensible beyond RTP media streams, and beyond UDP as a transport ๐ฆ extensions for non-RTP media streams need to say how many components they use, and assign component IDs to them, starting at 1 for the most important component ๐ and new transport protocols need to define how (if at all) the various ICE processing steps differ from UDP ๐ธ
15. Grammar ๐
this spec defines seven new SDP attributes: "candidate", "remote-candidates", "ice-lite", "ice-mismatch", "ice-ufrag", "ice-pwd", and "ice-options" ๐ we're about to go through the first two in detail ๐
15.1. "candidate" Attribute ๐ง
the candidate attribute is a media-level attribute only ๐ it holds a transport address for a candidate that can be used for connectivity checks.
the syntax is written in Augmented BNF, as defined in [RFC5234] ๐
candidate-attribute = "candidate" ":" foundation SP component-id SP
transport SP
priority SP
connection-address SP ;from RFC 4566
port ;port from RFC 4566
SP cand-type
[SP rel-addr]
[SP rel-port]
*(SP extension-att-name SP
extension-att-value)
foundation = 1*32ice-char
component-id = 1*5DIGIT
transport = "UDP" / transport-extension
transport-extension = token ; from RFC 3261
priority = 1*10DIGIT
cand-type = "typ" SP candidate-types
candidate-types = "host" / "srflx" / "prflx" / "relay" / token
rel-addr = "raddr" SP connection-address
rel-port = "rport" SP port
extension-att-name = byte-string ;from RFC 4566
extension-att-value = byte-string
ice-char = ALPHA / DIGIT / "+" / "/"
this grammar packs in the main info about a candidate: its IP address, port, and transport protocol, plus its properties: the foundation, component ID, priority, type, and related transport address ๐
<connection-address>: comes from [RFC4566] ๐ธ it's the IP address of the candidate, and it can be an IPv4 address, an IPv6 address, or a fully qualified domain name (FQDN). to tell IPv4 from IPv6 when parsing, just look for a colon: if there's one, it's IPv6 โจ an agent MUST ignore candidate lines that include candidates with IP address versions it doesn't support or recognize. an IP address SHOULD be used, but an FQDN MAY be used in its place. if that's the case, then when an offer or answer has an FQDN in an a=candidate attribute, the FQDN gets looked up in DNS first using an AAAA record (assuming the agent supports IPv6), and if nothing comes back or the agent only does IPv4, it uses an A record instead. if the DNS query returns more than one IP address, one gets chosen and then used for the rest of ICE processing ๐ง<port>: also comes from [RFC4566] ๐ it's the port of the candidate.<transport>: tells you which transport protocol the candidate uses ๐ฆ this spec only defines UDP. but it's extensible, so future transport protocols can plug into ICE, like TCP or the Datagram Congestion Control Protocol (DCCP) [RFC4340] ๐<foundation>: is made of 1 to 32<ice-char>s ๐ it's an identifier that's the same for two candidates that are the same type, share the same base, and come from the same STUN server. it's used to optimize ICE performance in the Frozen algorithm ๐<component-id>: is a positive integer between 1 and 256 that identifies which specific component of the media stream this candidate is for ๐ it MUST start at 1 and MUST increment by 1 for each component of a particular candidate. for RTP-based media streams, candidates for the actual RTP media MUST have a component ID of 1, and candidates for RTCP MUST have a component ID of 2 ๐ธ other types of media streams that need multiple components MUST develop specifications that define how components map to component IDs. see Section 14 for more on extending ICE to new media streams ๐<priority>: is a positive integer between 1 and (2**31 - 1) โจ<cand-type>: encodes the type of candidate ๐ฆ this spec defines "host", "srflx", "prflx", and "relay" for host, server reflexive, peer reflexive, and relayed candidates, in that order. the set of candidate types can grow in the future ๐ธ<rel-addr>and<rel-port>: carry transport addresses related to the candidate, which are useful for diagnostics and other stuff ๐ซถ<rel-addr>and<rel-port>MUST be present for server reflexive, peer reflexive, and relayed candidates. if a candidate is server or peer reflexive, then<rel-addr>and<rel-port>are equal to the base for that server or peer reflexive candidate. if the candidate is relayed, then<rel-addr>and<rel-port>equal the mapped address in the Allocate response that gave the client that relayed candidate (see Appendix B.3 for why that matters) ๐ if the candidate is a host candidate,<rel-addr>and<rel-port>MUST be omitted ๐
the candidate attribute can be extended too ๐งฉ the grammar lets new name/value pairs get tacked onto the end of the attribute. an implementation MUST ignore any name/value pairs it doesn't understand ๐
15.2. "remote-candidates" Attribute ๐
the syntax of the "remote-candidates" attribute is also written in Augmented BNF, as defined in [RFC5234] ๐ and it's a media-level attribute only โจ
remote-candidate-att = "remote-candidates" ":" remote-candidate
0*(SP remote-candidate)
remote-candidate = component-ID SP connection-address SP port
the attribute holds a connection-address and port for each component ๐ง the order of the components doesn't matter. BUT a value MUST be present for each component of a media stream ๐ and this attribute MUST be included in an offer from a controlling agent for a media stream that is Completed, and MUST NOT be included in any other case ๐ซถ
15.3. "ice-lite" and "ice-mismatch" Attributes ๐
okay so these two are basically flags ๐ฉ, meaning just the word and no value needed.
ice-lite = "ice-lite"
ice-mismatch = "ice-mismatch"
"ice-lite" is session-level only and tells you the agent is a lite implementation ๐ชถ. "ice-mismatch" is media-level only. When it shows up in an answer, it's basically saying the offer came in with a default destination for a media component that had no matching candidate attribute ๐ (main character plot twist, iykyk).
15.4. "ice-ufrag" and "ice-pwd" Attributes ๐
these two are the username fragment and password ICE uses for message integrity, so they're kind of a big deal ๐. Here's the grammar:
ice-pwd-att = "ice-pwd" ":" password
ice-ufrag-att = "ice-ufrag" ":" ufrag
password = 22*256ice-char
ufrag = 4*256ice-char
They can show up at the session-level or the media-level. If both are there, the media-level one wins ๐. So the session-level value is basically a default that covers every media stream, unless a media-level value overrides it. Either way, there MUST be an ice-pwd and ice-ufrag attribute for each media stream. And if two media streams have identical ice-ufrag's, they MUST have identical ice-pwd's. No mixing and matching, bestie ๐ซถ.
The ice-ufrag and ice-pwd attributes MUST be chosen randomly at the beginning of a session. ice-ufrag MUST contain at least 24 bits of randomness, and ice-pwd MUST contain at least 128 bits of randomness. Because the grammar lets each character carry 6 bits of randomness, that works out to ice-ufrag being at least 4 characters long and ice-pwd at least 22 characters long โจ. The attributes MAY be longer than 4 and 22 characters, of course, up to 256 characters. That upper limit is there so implementations can size their buffers ๐, and the big ceiling leaves room to add more randomness over time ๐.
15.5. "ice-options" Attribute ๐ธ
"ice-options" is a session-level attribute, and it's giving list energy ๐. It holds a bunch of tokens that name the options the agent supports. Grammar:
ice-options = "ice-options" ":" ice-option-tag
0*(SP ice-option-tag)
ice-option-tag = 1*ice-char
16. Setting Ta and RTO โฑ๏ธ
okay this part is all about pacing ๐ . During the gathering phase of ICE (Section 4.1.1) and while ICE is doing connectivity checks (Section 7), an agent sends STUN and TURN transactions. These go out at a rate of one every Ta milliseconds and use a specific RTO. This section explains how Ta and RTO get computed. The math depends on whether ICE is running with a real-time media stream (like RTP) or something else. If ICE is used for a stream with a known maximum bandwidth, Section 16.1 MAY be followed to rate-control the ICE exchanges. For all other streams, the computation in Section 16.2 MUST be followed ๐ง.
16.1. RTP Media Streams ๐ถ
Ta and RTO change over the lifetime of ICE processing. One set of values covers the gathering phase, and another covers the connectivity checks ๐.
The value of Ta SHOULD be configurable, and SHOULD have a default of:
For each media stream i:
Ta_i = (stun_packet_size / rtp_packet_size) * rtp_ptime
1
Ta = MAX (20ms, ------------------- )
k
----
\ 1
> ------
/ Ta_i
----
i=1
where k is the number of media streams. During the gathering phase, Ta is computed from the number of media streams the agent listed in its offer or answer, and the RTP packet size and RTP ptime come from the most preferred codec for each media stream ๐ง. Once an offer and answer have been exchanged, the agent recomputes Ta to pace the connectivity checks. This time Ta is based on the number of media streams that will actually be used in the session, and the RTP packet size and RTP ptime come from the most preferred codec the agent will send with โจ.
On top of that, the retransmission timer for STUN transactions, RTO, is defined in [RFC5389]. During the gathering phase it SHOULD be configurable and SHOULD have a default of:
RTO = MAX (100ms, Ta * (number of pairs))
where the number of pairs means the number of pairs of candidates with STUN or TURN servers.
For connectivity checks, RTO SHOULD be configurable and SHOULD have a default of:
RTO = MAX (100ms, Ta*N * (Num-Waiting + Num-In-Progress))
where Num-Waiting is the number of checks in the check list that are in the Waiting state, and Num-In-Progress is the number of checks in the In-Progress state. Heads up: RTO will be different for each transaction as those counts change ๐.
These formulas are built so that STUN transactions go out at the same rate as the media ๐ฏ. That makes sure ICE works under the same network conditions needed for the media to work too. See Appendix B.1 for more discussion and motivation. Because of this pacing, it takes a while to collect all the server reflexive and relayed candidates โณ. Implementations should be aware of that time, and if the application has a time budget, they should limit how many candidates get gathered.
Also, the formulas mean an agent sends its first packet for every single connectivity check before it does any retransmit ๐. You can see this in the RTO formulas, since they scale with N, the number of checks to be performed. The upside is that ICE keeps a nicely constant rate, but the downside is it gets more sensitive to packet loss ๐. Losing that first packet for any connectivity check will probably make that pair take ages to validate, so a lower-priority check (one that didn't lose a packet) is much more likely to finish first. The result is ICE performing sub-optimally, picking lower-priority pairs over higher-priority ones. Implementors should be aware of this consequence, but still should utilize the timer values described here ๐ค.
16.2. Non-RTP Sessions ๐
When ICE is used to set up a session that isn't real time, and there's no known fixed rate that's been shown to work on the network where ICE is deployed, Ta and RTO fall back to more conservative values ๐ข. Ta SHOULD be configurable, SHOULD have a default of 500 ms, and MUST NOT be configurable to be less than 500 ms.
RTO SHOULD be configurable too. During the gathering phase it SHOULD have a default of:
RTO = MAX (500ms, Ta * (number of pairs))
where the number of pairs again means the number of pairs of candidates with STUN or TURN servers.
For connectivity checks, RTO SHOULD be configurable and SHOULD have a default of:
RTO = MAX (500ms, Ta*N * (Num-Waiting + Num-In-Progress))
17. Example ๐บ๏ธ
This example is based on the simplified topology in Figure 8 โจ.
+-----+
| |
|STUN |
| Srvr|
+-----+
|
+---------------------+
| |
| Internet |
| |
| |
+---------------------+
| |
| |
+---------+ |
| NAT | |
+---------+ |
| |
| |
| |
+-----+ +-----+
| | | |
| L | | R |
| | | |
+-----+ +-----+
Figure 8: Example Topology
Two agents, L and R, are using ICE ๐ฆ. Both are full-mode ICE implementations, and both use aggressive nomination when they're controlling ๐. Each agent has a single IPv4 address. Agent L has 10.0.1.1 from private address space [RFC1918], and agent R has 192.0.2.1 on the public Internet ๐. Both are configured with the same STUN server (we're using the same one here just for simplicity, although in practice the agents don't need to share a STUN server). That server listens for STUN Binding requests at IP address 192.0.2.2 and port 3478. No TURN servers are used in this example ๐ โโ๏ธ. Agent L is behind a NAT, and agent R is on the public Internet. The NAT has an endpoint independent mapping property and an address dependent filtering property. Its public side has IP address 192.0.2.3 ๐.
To make things easier to follow, transport addresses are written as variables with mnemonic names ๐ง . The name format is entity-type-seqno. The entity says whose IP address the transport address is on, and it's one of "L", "R", "STUN", or "NAT". The type is either "PUB" for public transport addresses or "PRIV" for private ones. Last, seq-no is a sequence number that's different for each transport address of the same type on a particular entity ๐ข. Each variable has an IP address and port, written as varname.IP and varname.PORT, where varname is the variable's name.
The STUN server has advertised transport address STUN-PUB-1, which is 192.0.2.2:3478 ๐ฃ.
In the call flow, STUN messages get annotated with a few attributes. "S=" is the source transport address of the message. "D=" is the destination transport address. "MA=" shows up in STUN Binding response messages and refers to the mapped address. "USE-CAND" means the USE-CANDIDATE attribute is present ๐.
The call flow examples leave out STUN authentication operations and RTCP, and focus on RTP for a single media stream between two full implementations ๐.
Okay so bestie, this is where the whole thing gets visual ๐ฆ Here's the example flow, with agent L (behind a NAT) on the left and agent R (out in the open) on the right, plus the STUN server in the middle doing its thing ๐
L NAT STUN R
|RTP STUN alloc. | |
|(1) STUN Req | | |
|S=$L-PRIV-1 | | |
|D=$STUN-PUB-1 | | |
|------------->| | |
| |(2) STUN Req | |
| |S=$NAT-PUB-1 | |
| |D=$STUN-PUB-1 | |
| |------------->| |
| |(3) STUN Res | |
| |S=$STUN-PUB-1 | |
| |D=$NAT-PUB-1 | |
| |MA=$NAT-PUB-1 | |
| |<-------------| |
|(4) STUN Res | | |
|S=$STUN-PUB-1 | | |
|D=$L-PRIV-1 | | |
|MA=$NAT-PUB-1 | | |
|<-------------| | |
|(5) Offer | | |
|------------------------------------------->|
| | | |RTP STUN
alloc.
| | |(6) STUN Req |
| | |S=$R-PUB-1 |
| | |D=$STUN-PUB-1 |
| | |<-------------|
| | |(7) STUN Res |
| | |S=$STUN-PUB-1 |
| | |D=$R-PUB-1 |
| | |MA=$R-PUB-1 |
| | |------------->|
|(8) answer | | |
|<-------------------------------------------|
| |(9) Bind Req | |Begin
| |S=$R-PUB-1 | |Connectivity
| |D=L-PRIV-1 | |Checks
| |<----------------------------|
| |Dropped | |
|(10) Bind Req | | |
|S=$L-PRIV-1 | | |
|D=$R-PUB-1 | | |
|USE-CAND | | |
|------------->| | |
| |(11) Bind Req | |
| |S=$NAT-PUB-1 | |
| |D=$R-PUB-1 | |
| |USE-CAND | |
| |---------------------------->|
| |(12) Bind Res | |
| |S=$R-PUB-1 | |
| |D=$NAT-PUB-1 | |
| |MA=$NAT-PUB-1 | |
| |<----------------------------|
|(13) Bind Res | | |
|S=$R-PUB-1 | | |
|D=$L-PRIV-1 | | |
|MA=$NAT-PUB-1 | | |
|<-------------| | |
|RTP flows | | |
| |(14) Bind Req | |
| |S=$R-PUB-1 | |
| |D=$NAT-PUB-1 | |
| |<----------------------------|
|(15) Bind Req | | |
|S=$R-PUB-1 | | |
|D=$L-PRIV-1 | | |
|<-------------| | |
|(16) Bind Res | | |
|S=$L-PRIV-1 | | |
|D=$R-PUB-1 | | |
|MA=$R-PUB-1 | | |
|------------->| | |
| |(17) Bind Res | |
| |S=$NAT-PUB-1 | |
| |D=$R-PUB-1 | |
| |MA=$R-PUB-1 | |
| |---------------------------->|
| | | |RTP flows
Figure 9: Example Flow
First up, agent L grabs a host candidate from its local IP address (not drawn here, sorry ๐), then uses that to send a STUN Binding request to the STUN server and get a server reflexive candidate (messages 1-4) โจ Remember that the NAT has the address and port independent mapping property? So it makes a binding of NAT-PUB-1 for this UDP request, and that becomes the server reflexive candidate for RTP. Iconic ๐
Agent L gives the host candidate a type preference of 126 and the server reflexive one a type preference of 100. The local preference is 65535. From that, the host candidate's priority comes out to 2130706431, and the server reflexive candidate's priority is 1694498815 ๐งฎ The host candidate gets a foundation of 1 and the server reflexive gets a foundation of 2. Agent L picks its server reflexive candidate as the default candidate and puts it into the m and c lines. The offer (message 5) looks like this (lines folded for clarity):
v=0
o=jdoe 2890844526 2890842807 IN IP4 $L-PRIV-1.IP
s=
c=IN IP4 $NAT-PUB-1.IP
t=0 0
a=ice-pwd:asd88fgpdd777uzjYhagZg
a=ice-ufrag:8hhY
m=audio $NAT-PUB-1.PORT RTP/AVP 0
b=RS:0
b=RR:0
a=rtpmap:0 PCMU/8000
a=candidate:1 1 UDP 2130706431 $L-PRIV-1.IP $L-PRIV-1.PORT typ
host
a=candidate:2 1 UDP 1694498815 $NAT-PUB-1.IP $NAT-PUB-1.PORT typ
srflx raddr $L-PRIV-1.IP rport $L-PRIV-1.PORT
Here's that same offer with the variables swapped for their actual values (lines folded for clarity):
v=0
o=jdoe 2890844526 2890842807 IN IP4 10.0.1.1
s=
c=IN IP4 192.0.2.3
t=0 0
a=ice-pwd:asd88fgpdd777uzjYhagZg
a=ice-ufrag:8hhY
m=audio 45664 RTP/AVP 0
b=RS:0
b=RR:0
a=rtpmap:0 PCMU/8000
a=candidate:1 1 UDP 2130706431 10.0.1.1 8998 typ host
a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr
10.0.1.1 rport 8998
Now the offer lands at agent R ๐ฌ R gets its own host candidate, then uses it to get a server reflexive candidate (messages 6-7). Since R isn't behind a NAT, that server reflexive candidate is identical to its host candidate, and they share the same base. So R tosses the redundant one and ends up with just a single host candidate. Same type and local preferences as L, so its priority is 2130706431, and it picks a foundation of 1 for that lonely candidate ๐ฅฐ Its answer looks like this:
v=0
o=bob 2808844564 2808844564 IN IP4 $R-PUB-1.IP
s=
c=IN IP4 $R-PUB-1.IP
t=0 0
a=ice-pwd:YH75Fviy6338Vbrhrlp8Yh
a=ice-ufrag:9uB6
m=audio $R-PUB-1.PORT RTP/AVP 0
b=RS:0
b=RR:0
a=rtpmap:0 PCMU/8000
a=candidate:1 1 UDP 2130706431 $R-PUB-1.IP $R-PUB-1.PORT typ host
And with the variables filled in:
v=0
o=bob 2808844564 2808844564 IN IP4 192.0.2.1
s=
c=IN IP4 192.0.2.1
t=0 0
a=ice-pwd:YH75Fviy6338Vbrhrlp8Yh
a=ice-ufrag:9uB6
m=audio 3478 RTP/AVP 0
b=RS:0
b=RR:0
a=rtpmap:0 PCMU/8000
a=candidate:1 1 UDP 2130706431 192.0.2.1 3478 typ host
Neither side said it's lite, so the agent that sent the offer that kicked off ICE (agent L) becomes the controlling agent ๐
Next, L and R both pair up their candidates. They each start out with two pairs, but agent L prunes the pair holding its server reflexive candidate, so it's down to just one ๐ At agent L, that pair has a local candidate of $L_PRIV_1 and a remote candidate of $R_PUB_1, with a candidate pair priority of 4.57566E+18. (Note that an implementation would represent this as a 64-bit integer so as not to lose precision ๐ค) At agent R there are two pairs. The highest priority one has a local candidate of $R_PUB_1, a remote candidate of $L_PRIV_1, and a priority of 4.57566E+18. The second has a local candidate of $R_PUB_1, a remote candidate of $NAT_PUB_1, and a priority of 3.63891E+18.
Agent R kicks off its connectivity check (message 9) on the first pair, which is between the two host candidates. R is the controlled agent for this session, so the check leaves out the USE-CANDIDATE attribute. Agent L's host candidate is private and sitting behind a NAT, so this check won't succeed, because the packet can't be routed from R to L ๐ญ
When agent L gets the answer, it does its one and only connectivity check (messages 10-13). It implements the aggressive nomination algorithm, so it includes a USE-CANDIDATE attribute in this check. The check succeeds, so agent L creates a new pair. Its local candidate comes from the mapped address in the Binding response (NAT-PUB-1 from message 13), and its remote candidate is the destination of the request (R-PUB-1 from message 10). That pair goes into the valid list. It's also marked as selected, because the Binding request had the USE-CANDIDATE attribute. Since there's a selected candidate in the Valid list for the one component of this media stream, ICE processing for this stream moves into the Completed state. Agent L can now send media if it so chooses ๐
Soon after receiving the STUN Binding request from agent L (message 11), agent R generates its triggered check. This check happens to match the next one on its check list, which goes from its host candidate to agent L's server reflexive candidate. This check (messages 14-17) succeeds. So agent R builds a new candidate pair, using the mapped address from the response as the local candidate (R-PUB-1) and the destination of the request (NAT-PUB-1) as the remote candidate. That pair is added to the Valid list for the media stream. Because the check was generated in the reverse direction of a check that contained the USE-CANDIDATE attribute, the pair is marked as selected. As a result, processing for this stream moves into the Completed state, and agent R can send media too ๐ฆ
18. Security Considerations ๐
There are several types of attacks possible in an ICE system ๐ This section looks at these attacks and their countermeasures. The countermeasures include:
- Using ICE in conjunction with secure signaling techniques, such as SIPS.
- Limiting the total number of connectivity checks to 100, and optionally limiting the number of candidates they'll accept in an offer or answer.
18.1. Attacks on Connectivity Checks ๐ฅ
okay bestie, an attacker might try to mess with the STUN connectivity checks ๐ and honestly, all of these attacks boil down to one thing: tricking an agent into believing something wrong about how the checks went. here are the wrong conclusions an attacker can try to make happen:
- False Invalid: an attacker can convince a pair of agents that a candidate pair is invalid when it actually isn't ๐. this can make an agent prefer a different candidate (like one the attacker injected ๐), or wreck a call by making every candidate fail.
- False Valid: an attacker can convince a pair of agents that a candidate pair is valid when it isn't ๐. the agents go ahead with the session, but then nothing can actually be received as media.
- False Peer Reflexive Candidate: an attacker can make an agent discover a new peer reflexive candidate it never should have found ๐ซ . this can redirect media streams to a Denial-of-Service (DoS) target, or to the attacker, so they can eavesdrop or whatever else they're up to.
- False Valid on False Candidate: the attacker has already convinced an agent that there's a candidate with an address that doesn't actually route to that agent (for example, by injecting a false peer reflexive candidate or a false server reflexive candidate) ๐ง. next, it has to launch an attack that forces the agents to believe this candidate is valid.
If an attacker can cause a false peer reflexive candidate or a false valid on a false candidate, it can launch any of the attacks described in [RFC5389].
to force the false invalid result, the attacker has to wait for the connectivity check from one of the agents to be sent ๐ฌ. when it is, the attacker injects a fake response with an unrecoverable error response, such as a 400 ๐. but the candidate is actually valid, so the original request may still reach the peer agent and get a success response back. the attacker needs to force that packet or its response to be dropped, through a DoS attack, layer 2 network disruption, or some other technique. if it doesn't do this, the success response reaches the originator, which alerts it to a possible attack ๐จ. luckily, this attack is mitigated completely through the STUN short-term credential mechanism ๐. the attacker needs to inject a fake response, and for that response to be processed, the attacker needs the password. if the offer/answer signaling is secured, the attacker won't have the password, and its response gets discarded ๐๏ธ.
forcing the fake valid result works in a similar way โจ. the agent needs to wait for the Binding request from each agent, and then inject a fake success response. the attacker doesn't need to worry about disrupting the real response, since if the candidate isn't valid, it presumably wouldn't be received anyway. but just like the fake invalid attack, this one is mitigated by the STUN short-term credential mechanism in conjunction with a secure offer/answer exchange ๐ซถ.
forcing the false peer reflexive candidate result can be done either with fake requests or responses, or with replays ๐ญ. let's do the fake requests and responses case first. the attacker has to send a Binding request to one agent with a source IP address and port for the false candidate. then the attacker waits for a Binding request from the other agent and generates a fake response with an XOR-MAPPED-ADDRESS attribute containing the false candidate. like the other attacks here, this one is mitigated by the STUN message integrity mechanisms and secure offer/answer exchanges ๐ก๏ธ.
replays are a totally different energy ๐ฌ. the attacker waits until one of the agents sends a check, intercepts that request, and replays it towards the other agent with a faked source IP address. it also has to stop the original request from reaching the remote agent, either by launching a DoS attack to get the packet dropped, or by forcing it to be dropped using layer 2 mechanisms. the replayed packet arrives at the other agent and gets accepted, since the integrity check passes (the integrity check cannot and does not cover the source IP address and port ๐ค). the agent then responds to it. that response contains an XOR-MAPPED-ADDRESS with the false candidate, and it gets sent to that false candidate. the attacker must then receive it and relay it towards the originator.
the other agent will then start a connectivity check towards that false candidate ๐, and this validation needs to succeed. that requires the attacker to force a false valid on a false candidate. injecting fake requests or responses to get there is prevented by the integrity mechanisms of STUN and the offer/answer exchange, so this attack can only be launched through replays ๐. the attacker has to intercept the check towards this false candidate and replay it towards the other agent. then it has to intercept the response and replay that back as well.
this attack is very hard to launch unless the attacker is the one identified by the fake candidate ๐ง . that's because it requires the attacker to intercept and replay packets sent by two different hosts. if both agents are on different networks (for example, across the public Internet), this attack is hard to coordinate, since it has to happen against two different endpoints in different parts of the network at the same time.
if the attacker itself is identified by the fake candidate, the attack gets way easier to coordinate ๐ฌ. however, if SRTP is used [RFC3711], the attacker won't be able to play the media packets, but will only be able to discard them, which effectively disables the media stream for the call. still, this attack requires the agent to disrupt packets in order to block the connectivity check from reaching the target. so if the goal is to disrupt the media stream, it's much easier to just disrupt it with that same mechanism than to attack ICE ๐คทโโ๏ธ.
18.2. Attacks on Server Reflexive Address Gathering ๐
okay so ICE endpoints use STUN Binding requests to gather server reflexive candidates from a STUN server ๐ก. these requests aren't authenticated in any way, which means there are a bunch of techniques an attacker can use to hand the client a false server reflexive candidate ๐ฉ:
- an attacker can compromise the DNS, so DNS queries return a rogue STUN server address ๐ต๏ธ. that server can then give the client fake server reflexive candidates. this attack is mitigated by DNS security, though DNS-SEC is not required to address it.
- an attacker that can observe STUN messages (like an attacker on a shared network segment, such as WiFi ๐ถ) can inject a fake response that is valid and will be accepted by the client.
- an attacker can compromise a STUN server with a virus, and make it send responses with incorrect mapped addresses ๐ฆ .
a false mapped address learned through these attacks will be used as a server reflexive candidate in the ICE exchange. for that candidate to actually be used for media, the attacker also has to attack the connectivity checks, and in particular, force a false valid on a false candidate. this attack is very hard to launch if the false address identifies a fourth party (neither the offerer, the answerer, nor the attacker), since it requires attacking the checks generated by each agent in the session. and it's prevented by SRTP if the false address identifies the attacker themself ๐.
if the attacker decides not to attack the connectivity checks, the worst it can do is stop the server reflexive candidate from being used ๐. however, if the peer agent has at least one candidate that's reachable by the agent under attack, the STUN connectivity checks themselves will provide a peer reflexive candidate that can be used for the exchange of media ๐. peer reflexive candidates are generally preferred over server reflexive candidates, so an attack solely on the STUN address gathering will normally have no impact on a session at all ๐ .
18.3. Attacks on Relayed Candidate Gathering ๐
an attacker might try to disrupt the gathering of relayed candidates, forcing the client to believe it has a false relayed candidate ๐ซฃ. but the exchanges with the TURN server are authenticated using a long-term credential ๐, so injecting fake responses or requests won't work. plus, unlike Binding requests, Allocate requests aren't susceptible to replay attacks with modified source IP addresses and ports, since the source IP address and port aren't used to give the client its relayed candidate ๐ โโ๏ธ.
that said, TURN servers are susceptible to DNS attacks, or to viruses aimed at the TURN server, which can turn it into a zombie or rogue server ๐ง. these attacks can be mitigated by DNS-SEC and through good box and software security on TURN servers ๐.
even if an attacker has caused the client to believe in a false relayed candidate, the connectivity checks only use that candidate if they succeed โ . so the attacker has to launch a false valid on a false candidate, as described above, which is a very difficult attack to coordinate ๐ค.
18.4. Attacks on the Offer/Answer Exchanges ๐จ
an attacker that can modify or disrupt the offer/answer exchanges themselves can easily launch a whole variety of attacks with ICE ๐ฑ. they could direct media to a target of a DoS attack, insert themselves into the media stream, and so on. these are similar to the general security considerations for offer/answer exchanges, and the security considerations in RFC 3264 [RFC3264] apply. these need techniques for message integrity and encryption for offers and answers, which are satisfied by the SIPS mechanism [RFC3261] when SIP is used. so the usage of SIPS with ICE is RECOMMENDED ๐โจ.
18.5. Insider Attacks ๐
in addition to attacks where the attacker is a third party trying to insert fake offers, answers, or STUN messages, there are several attacks possible with ICE when the attacker is an authenticated and valid participant in the ICE exchange ๐ซฃ. yikes, the enemy is inside the group chat.
18.5.1. The Voice Hammer Attack ๐จ
the voice hammer attack is an amplification attack ๐ข. in this attack, the attacker starts sessions with other agents and maliciously includes the IP address and port of a DoS target as the destination for media traffic signaled in the SDP. this causes substantial amplification: a single offer/answer exchange can create a continuing flood of media packets, possibly at high rates (think video sources ๐ฅ). this attack isn't specific to ICE, but ICE can help provide remediation ๐.
specifically, if ICE is used, the agent receiving the malicious SDP will first perform connectivity checks to the target of the media before sending any media there ๐ต๏ธโโ๏ธ. if this target is a third-party host, the checks won't succeed, and media is never sent ๐.
unfortunately, ICE doesn't help if it isn't used, since an attacker could simply send the offer without the ICE parameters ๐ญ. however, in environments where the set of clients is known and limited to ones that support ICE, the server can reject any offers or answers that don't indicate ICE support ๐.
18.5.2. STUN Amplification Attack โจ
okay so the STUN amplification attack is basically the voice hammer's cousin ๐. Instead of voice packets getting blasted at the target, STUN connectivity checks get aimed at them. The attacker sends an offer with a big pile of candidates, like 50 ๐ซ . The answerer gets the offer and starts its checks, which are aimed at the target, so the target never sends back a response. The answerer starts a new connectivity check every Ta ms (say, Ta=20ms). But the retransmission timers are set to a huge number because there are so many candidates, so packets go out one every Ta milliseconds and then at increasing intervals after that. STUN never sends packets faster than media would be sent, and the STUN packets only stick around briefly, until ICE fails for the session. Still, it's an amplification mechanism, no cap ๐ญ.
There's literally no way to get rid of the amplification ๐ฉ, but you can shrink the volume with a bunch of heuristics. Agents SHOULD limit the total number of connectivity checks they perform to 100. Additionally, agents MAY limit the number of candidates they'll accept in an offer or answer.
Usually, protocols that want to dodge this kind of attack make the initiator wait for a response before it sends the next message. With ICE that's not possible ๐ โโ๏ธ. You can't tell these two cases apart:
- There was no response because the initiator is being used to launch a DoS attack against an unsuspecting target that will not respond.
- There was no response because the IP address and port are not reachable by the initiator.
In the second case, another check should be sent at the next opportunity, while in the former case, no further checks should be sent. Big brain problem, tiny brain fix ๐ง .
18.6. Interactions with Application Layer Gateways and SIP ๐ฆ
Application Layer Gateways (ALGs) are functions that live in a NAT device. They inspect the contents of packets and modify them to help application protocols get through NAT. Session Border Controllers (SBCs) are close cousins of ALGs, but they're less transparent, since they actually exist as application layer SIP intermediaries. ICE has interactions with both SBCs and ALGs ๐.
If an ALG is SIP aware but not ICE aware, ICE will work through it as long as the ALG correctly modifies the SDP. A correct ALG implementation behaves like this:
- The ALG does not modify the m and c lines or the rtcp attribute if they contain external addresses.
- If the m and c lines contain internal addresses, the modification depends on the state of the ALG:
- If the ALG already has a binding established that maps an external port to an internal IP address and port matching the values in the m and c lines or rtcp attribute, the ALG uses that binding instead of creating a new one.
- If the ALG does not already have a binding, it creates a new one and modifies the SDP, rewriting the m and c lines and rtcp attribute.
Unfortunately, many ALGs are known to work poorly in these corner cases ๐. ICE does not try to work around broken ALGs, since that's outside its scope. ICE can help diagnose these conditions, which often show up as a mismatch between the set of candidates and the m and c lines and rtcp attributes. The ice-mismatch attribute is used for exactly this purpose ๐.
ICE works best through ALGs when the signaling runs over TLS. That keeps the ALG from manipulating the SDP messages and interfering with ICE operation ๐. Implementations that are expected to be deployed behind ALGs SHOULD provide for TLS transport of the SDP.
If an SBC is SIP aware but not ICE aware, the result depends on how the SBC behaves ๐คทโโ๏ธ. If it's acting as a proper Back-to-Back User Agent (B2BUA), the SBC will remove any SDP attributes it doesn't understand, including the ICE attributes. So the call looks to both endpoints like the other side doesn't support ICE. That means ICE gets disabled, and media flows through the SBC if the SBC has requested it. But if the SBC passes the ICE attributes through without modification, yet changes the default destination for media (in the m and c lines and rtcp attribute), that gets detected as an ICE mismatch, and ICE processing is aborted for the call. ICE isn't meant to act as a tool for "working around" SBCs. If one is present, ICE will not be used and the SBC techniques take precedence ๐.
19. STUN Extensions ๐
19.1. New Attributes
This specification defines four new attributes: PRIORITY, USE-CANDIDATE, ICE-CONTROLLED, and ICE-CONTROLLING. Four new babies ๐ผ.
The PRIORITY attribute indicates the priority that is to be associated with a peer reflexive candidate, should one be discovered by this check. It's a 32-bit unsigned integer, and its attribute value is 0x0024.
The USE-CANDIDATE attribute indicates that the candidate pair resulting from this check should be used for transmission of media. It has no content (the Length field of the attribute is zero), so it's basically just a flag ๐ฉ. Its attribute value is 0x0025.
The ICE-CONTROLLED attribute is present in a Binding request and indicates that the client believes it is currently in the controlled role. Its content is a 64-bit unsigned integer in network byte order, which holds a random number used for tie-breaking of role conflicts ๐ฒ.
The ICE-CONTROLLING attribute is present in a Binding request and indicates that the client believes it is currently in the controlling role. Its content is a 64-bit unsigned integer in network byte order, which holds a random number used for tie-breaking of role conflicts ๐ฒ. Same vibe, opposite role, main character energy ๐.
19.2. New Error Response Codes
This specification defines a single error response code:
487 (Role Conflict): The Binding request contained either theICE-CONTROLLINGorICE-CONTROLLEDattribute, indicating a role that conflicted with the server. The server ran a tie-breaker based on the tie-breaker value in the request and determined that the client needs to switch roles ๐.
20. Operational Considerations ๐
This section talks about issues that matter to network operators who are looking to deploy ICE ๐ซถ.
20.1. NAT and Firewall Types
ICE was designed to work with existing NAT and firewall equipment, so you don't have to replace or reconfigure existing firewall and NAT gear to deploy it ๐. Honestly, ICE was built for environments where the Voice over IP (VoIP) operator has no control over the IP network infrastructure, including firewalls and NAT. Iconic behavior.
That said, ICE works best when the NAT devices are "behave" compliant, meeting the recommendations defined in [RFC4787] and [RFC5766]. In networks with behave-compliant NAT, ICE works without needing a TURN server, which improves voice quality, cuts call setup times, and reduces the bandwidth demands on the network operator ๐ฆโจ.
20.2. Bandwidth Requirements
Deploying ICE can interact with available network capacity in a few ways, and operators should take those into consideration ๐ง.
20.2.1. STUN and TURN Server Capacity Planning
First things first, ICE makes use of TURN and STUN servers, which typically live in the network operator's data centers ๐ข. STUN servers need relatively little bandwidth. For each component of each media stream, there will be one or more STUN transactions from each client to the STUN server. In a basic voice-only IPv4 VoIP deployment, there are four transactions per call (one for RTP and one for RTCP, for both caller and callee). Each transaction is a single request and a single response, with the request being 20 bytes long and the response being 28 bytes. So if a system has N users, and each makes four calls in a busy hour, that's N*1.7bps. For one million users, that's 1.7 Mbps, which is a very small number (relatively speaking) ๐ฅน.
TURN traffic is more substantial ๐ค. The TURN server sees traffic volume equal to the STUN volume (if TURN servers are deployed, there's no need for a separate STUN server), on top of the traffic for the actual media. How many calls need TURN for media relay is highly dependent on network topologies, and it can and will vary over time. In a network with 100% behave-compliant NAT, it's exactly zero. At time of writing, large-scale consumer deployments were seeing between 5 and 10 percent of calls requiring TURN servers. If you consider a voice-only deployment using G.711 (so 80 kbps in each direction), with .2 erlangs during the busy hour, that's N*3.2 kbps. For a population of one million users, that's 3.2 Gbps, assuming a 10% usage of TURN servers ๐.
20.2.2. Gathering and Connectivity Checks
Gathering candidates and running connectivity checks can be bandwidth intensive ๐ถ. ICE was designed to pace both of these processes. The gathering phase and the connectivity check phase are meant to generate traffic at roughly the same bandwidth as the media traffic itself. This was done so that if a network is designed to support a certain type of multimedia traffic (voice, video, or just text), it'll have enough capacity for the ICE checks for that media too. Sure, the ICE checks add a bit to total utilization, but it's typically an extremely small increase ๐ธ.
Congestion from the gathering and check phases has been a real problem in deployments that didn't use pacing ๐ญ. Typically, access links got congested because endpoints flooded the network with checks as fast as they could send them. So network operators should make sure their ICE implementations support the pacing feature. Yes, the pacing does make call setup times longer, but it makes ICE network friendly and easier to deploy ๐ซง.
20.2.3. Keepalives
STUN keepalives (in the form of STUN Binding Indications) are sent in the middle of a media session. They're only sent in the absence of actual media traffic though ๐คซ. In deployments that don't use Voice Activity Detection (VAD), the keepalives are never used and there's no increase in bandwidth usage. When VAD is in use, keepalives get sent during silence periods. That's a single packet every 15-20 seconds, which is way less than the packet every 20-30 ms sent when there's voice. So keepalives don't have any real impact on capacity planning ๐ .
20.3. ICE and ICE-lite
Deployments that mix ICE and ICE-lite interoperate perfectly ๐. They were explicitly designed to do that, without loss of function.
However, ICE-lite can only be deployed in limited use cases ๐ฅบ. Those cases, and the caveats involved in deploying them, are documented in Appendix A.
20.4. Troubleshooting and Performance Management ๐
okay so ICE does its connectivity checks end-to-end, and most of the work happens in the endpoints ๐ that's lowkey a problem for network operators. how do they troubleshoot an ICE deployment? how do they even know if ICE is doing well? ๐ฅบ
good news, ICE has built-in features for exactly this ๐ SIP servers sitting on the signaling path (usually in the operator's data centers) can see the contents of the offer/answer exchanges that carry the ICE parameters. those parameters include the type of each candidate (host, server reflexive, or relayed) plus their related addresses โจ once ICE processing is done, there's an updated offer/answer exchange that signals the selected address and its type. that updated re-INVITE exists purely to educate network equipment, like a diagnostic tool attached to a SIP server, about how ICE turned out ๐ง
so because of that, the logs from the SIP server show what candidate types each call used and which address ICE picked. that's the main info for figuring out how ICE is performing ๐ it's giving transparency, no cap ๐
20.5. Endpoint Configuration โ๏ธ
ICE needs a bunch of data configured into the endpoints ๐ธ that includes timers, credentials for TURN servers, and hostnames for STUN and TURN servers. ICE doesn't have its own mechanism for this, though ๐ it's assumed that this info gets attached to whatever system configures all the other parameters in the endpoint. for SIP phones, standard solutions like the configuration framework [SIP-UA-FRMWK] already exist ๐ฆ
21. IANA Considerations ๐
this spec registers new SDP attributes, four new STUN attributes, and one new STUN error response ๐
21.1. SDP Attributes ๐
this spec defines seven new SDP attributes, following the procedures of Section 8.2.4 of [RFC4566] โจ all the info needed for the registrations is right here.
21.1.1. candidate Attribute
- Contact Name: Jonathan Rosenberg, jdrosen@jdrosen.net.
- Attribute Name:
candidate - Long Form:
candidate - Type of Attribute: media-level
- Charset Considerations: The attribute is not subject to the charset attribute.
- Purpose: This attribute is used with Interactive Connectivity Establishment (ICE). It gives one of many possible candidate addresses for communication, and those addresses get validated with an end-to-end connectivity check using Session Traversal Utilities for NAT (STUN)).
- Appropriate Values: See Section 15 of RFC 5245.
21.1.2. remote-candidates Attribute
- Contact Name: Jonathan Rosenberg, jdrosen@jdrosen.net.
- Attribute Name:
remote-candidates - Long Form:
remote-candidates - Type of Attribute: media-level
- Charset Considerations: The attribute is not subject to the charset attribute.
- Purpose: This attribute is used with Interactive Connectivity Establishment (ICE). It identifies the remote candidates that the offerer wants the answerer to use in its answer ๐ซถ
- Appropriate Values: See Section 15 of RFC 5245.
21.1.3. ice-lite Attribute
- Contact Name: Jonathan Rosenberg, jdrosen@jdrosen.net.
- Attribute Name:
ice-lite - Long Form:
ice-lite - Type of Attribute: session-level
- Charset Considerations: The attribute is not subject to the charset attribute.
- Purpose: This attribute is used with Interactive Connectivity Establishment (ICE). It says an agent has the minimum functionality needed to do ICE inter-operation with a peer that has a full implementation ๐
- Appropriate Values: See Section 15 of RFC 5245.
21.1.4. ice-mismatch Attribute
- Contact Name: Jonathan Rosenberg, jdrosen@jdrosen.net.
- Attribute Name:
ice-mismatch - Long Form:
ice-mismatch - Type of Attribute: session-level
- Charset Considerations: The attribute is not subject to the charset attribute.
- Purpose: This attribute is used with Interactive Connectivity Establishment (ICE). It says an agent is ICE capable but didn't go ahead with ICE, because its candidates didn't match the default destination for media signaled in the SDP ๐ญ
- Appropriate Values: See Section 15 of RFC 5245.
21.1.5. ice-pwd Attribute
- Contact Name: Jonathan Rosenberg, jdrosen@jdrosen.net.
- Attribute Name:
ice-pwd - Long Form:
ice-pwd - Type of Attribute: session- or media-level
- Charset Considerations: The attribute is not subject to the charset attribute.
- Purpose: This attribute is used with Interactive Connectivity Establishment (ICE). It provides the password that protects STUN connectivity checks ๐
- Appropriate Values: See Section 15 of RFC 5245.
21.1.6. ice-ufrag Attribute
- Contact Name: Jonathan Rosenberg, jdrosen@jdrosen.net.
- Attribute Name:
ice-ufrag - Long Form:
ice-ufrag - Type of Attribute: session- or media-level
- Charset Considerations: The attribute is not subject to the charset attribute.
- Purpose: This attribute is used with Interactive Connectivity Establishment (ICE). It provides the fragments used to build the username in STUN connectivity checks ๐งฉ
- Appropriate Values: See Section 15 of RFC 5245.
21.1.7. ice-options Attribute
- Contact Name: Jonathan Rosenberg, jdrosen@jdrosen.net.
- Attribute Name:
ice-options - Long Form:
ice-options - Type of Attribute: session-level
- Charset Considerations: The attribute is not subject to the charset attribute.
- Purpose: This attribute is used with Interactive Connectivity Establishment (ICE). It indicates the ICE options or extensions the agent uses โญ
- Appropriate Values: See Section 15 of RFC 5245.
21.2. STUN Attributes ๐
this section registers four new STUN attributes, following the procedures in [RFC5389] โจ
0x0024 PRIORITY
0x0025 USE-CANDIDATE
0x8029 ICE-CONTROLLED
0x802A ICE-CONTROLLING
21.3. STUN Error Responses ๐จ
this section registers one new STUN error response code, following the procedures in [RFC5389] ๐
487 Role Conflict: The client asserted an ICE role (controlling
or
controlled) that is in conflict with the role of the server.
22. IAB Considerations ๐ง
the IAB has studied "Unilateral Self-Address Fixing," which is the general process where an agent tries to work out its address in another realm on the far side of a NAT, using a collaborative protocol reflection mechanism [RFC3424] ICE is an example of a protocol that does this. the twist is that ICE's process is bilateral, not unilateral, and that difference seriously changes the issues the IAB raised ๐ ICE can be considered a B-SAF (Bilateral Self-Address Fixing) protocol, not an UNSAF protocol โจ either way, the IAB requires that any protocol made for this purpose document a specific set of considerations, and this section does exactly that ๐ซถ
22.1. Problem Definition ๐ฏ
from RFC 3424, any UNSAF proposal must provide:
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; this is why "short-term fixes usually aren't".
the specific problems ICE solves are:
- Provide a means for two peers to determine the set of transport addresses that can be used for communication. ๐
- Provide a means for a agent to determine an address that is reachable by another peer with which it wishes to communicate. ๐ฆ
22.2. Exit Strategy ๐ช
from RFC 3424, any UNSAF proposal must provide:
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.
ICE itself doesn't really get phased out ๐ it's still useful even on a globally connected Internet, for example for detecting whether a router failure has temporarily disrupted connectivity. ICE also helps prevent certain security attacks that have nothing to do with NAT. what ICE really does is help phase out the other UNSAF mechanisms ๐ธ it effectively selects among them, prioritizing the better ones and deprioritizing the worse ones. local IPv6 addresses can be preferred. as NATs start to fade away with IPv6 adoption, server reflexive and relayed candidates (both forms of UNSAF addresses) simply never get used, because higher-priority connectivity exists to the native host candidates. so the servers get used less and less, and can eventually be removed once their usage goes to zero ๐ฅ
ICE can also assist the transition from IPv4 to IPv6 โจ it can determine whether to use IPv6 or IPv4 when two dual-stack hosts communicate with SIP (IPv6 wins ๐). it can also let a network with both 6to4 and native v6 connectivity figure out which address to use when talking to a peer ๐
22.3. Brittleness Introduced by ICE ๐งธ
from RFC 3424, any UNSAF proposal must provide:
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.
ICE actually removes brittleness from existing UNSAF mechanisms ๐ classic STUN (as described in RFC 3489 [RFC3489]) has several brittle points. one is the discovery process where an agent tries to classify the type of NAT it's behind, which is error-prone. ICE skips that discovery process entirely. instead of unilaterally deciding whether an address is valid, ICE checks it dynamically by measuring connectivity to a peer, and that process is very robust ๐ช
another brittle point in classic STUN, and in any other unilateral mechanism, is the absolute reliance on an extra server. ICE uses a server to allocate unilateral addresses, but it lets agents connect directly when possible. so in some cases, if a STUN server fails, a call can still go through with ICE ๐
classic STUN also assumes the STUN server is on the public Internet. with ICE, that's not necessary. there can be lots of STUN servers across different address realms, and ICE finds the one that gives a usable address ๐
the most troubling brittle point in classic STUN is that it doesn't work in every network topology. if there's a shared NAT between each agent and the STUN server, traditional STUN may not work. ICE removes that restriction ๐
classic STUN also brings some security considerations, and ICE mitigates those too ๐ก๏ธ
so overall, ICE repairs the brittleness of classic STUN and doesn't add any new brittleness to the system โจ the tradeoff is that ICE increases session establishment times ๐ฅน
22.4. Requirements for a Long-Term Solution โจ
okay so RFC 3424 has this rule that any UNSAF proposal has to show up with:
... requirements for longer term, sound technical solutions -- contribute to the process of finding the right longer term solution.
so basically, we're not just patching things forever ๐ฉน. Our takeaways from RFC 3489 are still the same, no changes ๐ . BUT ngl, ICE actually helps here, because we think it can be part of the long-term solution ๐ฆ๐ซถ
22.5. Issues with Existing NAPT Boxes ๐ธ
RFC 3424 also says any UNSAF proposal has to bring:
Discussion of the impact of the noted practical issues with existing, deployed NA[P]Ts and experience reports.
bestie, the NAT boxes out there are getting kinda messy ๐ญ. A bunch of them are now shipping with "generic" ALG features that go hunting for IP addresses, whether they're written as text or sitting in binary inside a packet, and then rewrite them if they match a binding. Iconic behaviour for a middlebox, not gonna lie ๐. It messes with classic STUN, BUT the update to STUN [RFC5389] encodes addresses in a way that hides those binary addresses from generic ALGs, so STUN is kinda fighting back here ๐
Then there's the keepalive situation. Existing NAPT boxes have non-deterministic and usually short expiration times for UDP-based bindings ๐. So implementations have to keep sending periodic keepalives to keep those bindings alive. ICE uses a default of 15 s, which is a very conservative estimate ๐ง. Over time, though, as NAT boxes get compliant with [RFC4787], that minimum keepalive is going to become deterministic and well-known, and then the ICE timers can be adjusted โจ. Honestly, having a way to discover and control the minimum keepalive interval would be way better still ๐๐
23. Acknowledgements ๐
The authors want to thank Dan Wing, Eric Rescorla, Flemming Andreasen, Rohan Mahy, Dean Willis, Eric Cooper, Jason Fischl, Douglas Otis, Tim Moore, Jean-Francois Mule, Kevin Johns, Jonathan Lennox, and Francois Audet for their comments and input ๐ซถ. A special shoutout goes to Bill May, who suggested several of the concepts in this spec ๐ฅ, and Philip Matthews, who suggested many of the key performance optimizations here โญ. Eric Rescorla also drafted the text in the introduction, and Magnus Westerlund did several detailed reviews on the various revisions of this specification ๐๐
24. References ๐
24.1. Normative References ๐
These are the references the spec depends on, so they're the must-reads ๐
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC3605] Huitema, C., "Real Time Control Protocol (RTCP) attribute
in Session Description Protocol (SDP)", RFC 3605,
October 2003.
[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.
[RFC3264] Rosenberg, J. and H. Schulzrinne, "An Offer/Answer Model
with Session Description Protocol (SDP)", RFC 3264,
June 2002.
[RFC3556] Casner, S., "Session Description Protocol (SDP) Bandwidth
Modifiers for RTP Control Protocol (RTCP) Bandwidth",
RFC 3556, July 2003.
[RFC3312] Camarillo, G., Marshall, W., and J. Rosenberg,
"Integration of Resource Management and Session Initiation
Protocol (SIP)", RFC 3312, October 2002.
[RFC4032] Camarillo, G. and P. Kyzivat, "Update to the Session
Initiation Protocol (SIP) Preconditions Framework",
RFC 4032, March 2005.
[RFC3262] Rosenberg, J. and H. Schulzrinne, "Reliability of
Provisional Responses in Session Initiation Protocol
(SIP)", RFC 3262, June 2002.
[RFC4566] Handley, M., Jacobson, V., and C. Perkins, "SDP: Session
Description Protocol", RFC 4566, July 2006.
[RFC4091] Camarillo, G. and J. Rosenberg, "The Alternative Network
Address Types (ANAT) Semantics for the Session Description
Protocol (SDP) Grouping Framework", RFC 4091, June 2005.
[RFC4092] Camarillo, G. and J. Rosenberg, "Usage of the Session
Description Protocol (SDP) Alternative Network Address
Types (ANAT) Semantics in the Session Initiation Protocol
(SIP)", RFC 4092, June 2005.
[RFC3484] Draves, R., "Default Address Selection for Internet
Protocol version 6 (IPv6)", RFC 3484, February 2003.
[RFC5234] Crocker, D., Ed., and P. Overell, "Augmented BNF for
Syntax Specifications: ABNF", STD 68, RFC 5234, January
2008.
[RFC5389] Rosenberg, J., Mahy, R., Matthews, P., and D. Wing,
"Session Traversal Utilities for NAT (STUN)", RFC 5389,
October 2008.
[RFC5766] Mahy, R., Matthews, P., and J. Rosenberg, "Traversal Using
Relays around NAT (TURN): Relay Extensions to Session
Traversal Utilities for NAT (STUN)", RFC 5766, April 2010.
[RFC5768] Rosenberg, J., "Indicating Support for Interactive
Connectivity Establishment (ICE) in the Session Initiation
Protocol (SIP)", RFC 5768, April 2010.
24.2. Informative References ๐
These ones are the extra background reading, totally worth it if you're curious ๐ง ๐ท
[RFC3489] Rosenberg, J., Weinberger, J., Huitema, C., and R. Mahy,
"STUN - Simple Traversal of User Datagram Protocol (UDP)
Through Network Address Translators (NATs)", RFC 3489,
March 2003.
[RFC3235] Senie, D., "Network Address Translator (NAT)-Friendly
Application Design Guidelines", RFC 3235, January 2002.
[RFC3303] Srisuresh, P., Kuthan, J., Rosenberg, J., Molitor, A., and
A. Rayhan, "Middlebox communication architecture and
framework", RFC 3303, August 2002.
[RFC3725] Rosenberg, J., Peterson, J., Schulzrinne, H., and G.
Camarillo, "Best Current Practices for Third Party Call
Control (3pcc) in the Session Initiation Protocol (SIP)",
BCP 85, RFC 3725, April 2004.
[RFC3102] Borella, M., Lo, J., Grabelsky, D., and G. Montenegro,
"Realm Specific IP: Framework", RFC 3102, October 2001.
[RFC3103] Borella, M., Grabelsky, D., Lo, J., and K. Taniguchi,
"Realm Specific IP: Protocol Specification", RFC 3103,
October 2001.
[RFC3424] Daigle, L. and IAB, "IAB Considerations for UNilateral
Self-Address Fixing (UNSAF) Across Network Address
Translation", RFC 3424, November 2002.
[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.
[RFC3056] Carpenter, B. and K. Moore, "Connection of IPv6 Domains
via IPv4 Clouds", RFC 3056, February 2001.
[RFC3389] Zopf, R., "Real-time Transport Protocol (RTP) Payload for
Comfort Noise (CN)", RFC 3389, September 2002.
[RFC3960] Camarillo, G. and H. Schulzrinne, "Early Media and Ringing
Tone Generation in the Session Initiation Protocol (SIP)",
RFC 3960, December 2004.
[RFC2475] Blake, S., Black, D., Carlson, M., Davies, E., Wang, Z.,
and W. Weiss, "An Architecture for Differentiated
Services", RFC 2475, December 1998.
[RFC1918] Rekhter, Y., Moskowitz, R., Karrenberg, D., Groot, G., and
E. Lear, "Address Allocation for Private Internets",
BCP 5, RFC 1918, February 1996.
[RFC4787] Audet, F. and C. Jennings, "Network Address Translation
(NAT) Behavioral Requirements for Unicast UDP", BCP 127,
RFC 4787, January 2007.
[SDP-PRECON]
Andreasen, F., Camarillo, G., Oran, D., and D. Wing,
"Connectivity Preconditions for Session Description
Protocol Media Streams", Work in Progress, March 2010.
[NO-OP-RTP]
Andreasen, F., Oran, D., and D. Wing, "A No-Op Payload
Format for RTP", Work in Progress, May 2007.
[RFC5761] Perkins, C. and M. Westerlund, "Multiplexing RTP Data and
Control Packets on a Single Port", RFC 5761, April 2010.
[RFC4340] Kohler, E., Handley, M., and S. Floyd, "Datagram
Congestion Control Protocol (DCCP)", RFC 4340, March 2006.
[RFC4103] Hellstrom, G. and P. Jones, "RTP Payload for Text
Conversation", RFC 4103, June 2005.
[RFC5626] Jennings, C., Mahy, R., and F. Audet, "Managing Client-
Initiated Connections in the Session Initiation Protocol
(SIP)", RFC 5626, October 2009.
[RFC5382] Guha, S., Biswas, K., Ford, B., Sivakumar, S., and P.
Srisuresh, "NAT Behavioral Requirements for TCP", BCP 142,
RFC 5382, October 2008.
[SIP-UA-FRMWK]
Petrie, D. and S. Channabasappa, Ed., "A Framework for
Session Initiation Protocol User Agent Profile Delivery",
Work in Progress, February 2010.
[ICE-TCP] Perreault, S., Ed. and J. Rosenberg, "TCP Candidates with
Interactive Connectivity Establishment (ICE)", Work
in Progress, October 2009.
Appendix A. Lite and Full Implementations โจ
okay so ICE comes in two flavours, bestie ๐ A full implementation can play the controlling or controlled role in a session, AND it can do address gathering. A lite implementation is the minimalist era ๐ฆ It basically just answers STUN checks and that's it.
Here's the tea ๐ต ICE only helps either endpoint if both sides speak it, so rolling it out bit by bit is kinda messy. A lot of calls involve an endpoint that isn't behind a NAT at all and has zero reason to care about NAT traversal. The classic example is a NAT-stuck VoIP hard phone or soft phone calling one of those devices. Even if the phone is fully ICE-capable, ICE just won't kick in if the other device doesn't support it ๐ญ That's where lite comes in. It's a low-cost entry point. Once devices have lite, full implementations can connect to them and get all the ICE goodness ๐
So lite is only appropriate for devices that will always be on the public Internet with a public IP address they can receive packets on from any correspondent ๐ธ ICE will NOT work if a lite implementation sits behind a NAT, so don't even try it, babe.
ICE lets a lite implementation have one IPv4 host candidate and several IPv6 addresses. In that setup, the controlling agent picks candidate pairs with a static algorithm, like the one in RFC 3484, which this spec recommends. BUT static address selection is always kinda error-prone ๐ It can't reflect the real topology and can never really guarantee connectivity. They're always heuristics, not facts. So if an agent is only using ICE to choose between its IPv4 and IPv6 addresses, and none of them are behind a NAT, full ICE is STILL RECOMMENDED to get the most robust address selection possible ๐
Lite was added to this spec as a stepping stone toward full, not as the end goal ๐ง Even if a device has a single public IPv4 address, a full implementation is better whenever you can swing it. Full ICE cuts call setup times because ICE's aggressive mode can be used. Full implementations also get the security perks of ICE that have nothing to do with NAT traversal. The voice hammer attack from Section 18 is prevented ONLY for full implementations, not lite ๐ฑ And here's the kicker: a device with a public address today might be dropped into a network tomorrow where it's behind a NAT. Over a device's whole lifetime, you can't really know it'll always be on the public Internet. Full implementation is what gives you the assurance that communication will always work ๐ซถ
Appendix B. Design Motivations
Okay, so ICE has a bunch of normative behaviours that look simple on the surface, but they come from some complicated or non-obvious thinking and use cases that deserve a proper chat ๐ง Since you don't need any of this to implement ICE, it lives down here in an appendix. This section is non-normative, so chill ๐
B.1. Pacing of STUN Transactions
STUN transactions used to gather candidates and to check connectivity are paced out at an approximate rate of one new transaction every Ta milliseconds โฐ Each transaction also has a retransmission timer, RTO, which is a function of Ta too. So why pace things, and why these formulas? Let's get into it ๐
Sending these STUN requests often creates bindings on NAT devices between the client and the STUN servers. And experience has shown that lots of NAT devices have upper limits on how fast they'll create new bindings ๐ Experiments showed that one every 20 ms is well supported, but going much lower isn't. That's exactly why Ta has a lower bound of 20 ms. Also, sending these packets eats bandwidth, and the agent has to rate limit that. Deployments based on earlier draft versions of this document tended to overload rate-constrained access links and performed poorly overall, on top of hurting the network ๐ญ So the pacing keeps the NAT from getting overloaded and keeps traffic at a reasonable rate. Iconic behaviour, honestly ๐
So what counts as "reasonable"? STUN should not use more bandwidth than the RTP itself will use once media starts flowing โจ The Ta formula is built so that if a STUN packet went out every Ta seconds, it would use the same bandwidth as RTP packets summed across all media streams. STUN also has retransmits, and we want to pace those too ๐ฆ So RTO is set up so that the first retransmit on the first transaction happens right as the first STUN request on the last transaction goes out. Picture it like this:
First Packets Retransmits
| |
| |
-------+------ -------+------
/ \ / \
/ \ / \
+--+ +--+ +--+ +--+ +--+ +--+
|A1| |B1| |C1| |A2| |B2| |C2|
+--+ +--+ +--+ +--+ +--+ +--+
---+-------+-------+-------+-------+-------+------------ Time
0 Ta 2Ta 3Ta 4Ta 5Ta
In this picture, three transactions get sent (say, for candidate gathering, there are three host candidate/STUN server pairs) ๐ They're transactions A, B, and C. The retransmit timer is set so the first retransmission on the first transaction (packet A2) goes out at time 3Ta.
After that first retransmit, the later ones happen even LESS often than every Ta milliseconds, because STUN uses an exponential back-off on its retransmissions ๐ Main character pacing, no cap.
B.2. Candidates with Multiple Bases
Section 4.1.3 talks about getting rid of candidates that have the same transport address and base ๐๏ธ BUT candidates with the same transport address and different bases are not redundant! So when can an agent end up with two candidates that share an IP address and port but have different bases? Let's look at the topology in Figure 10 ๐
+----------+
| STUN Srvr|
+----------+
|
|
-----
// \\
| |
| B:net10 |
| |
\\ //
-----
|
|
+----------+
| NAT |
+----------+
|
|
-----
// \\
| A |
|192.168/16 |
| |
\\ //
-----
|
|
|192.168.1.100 -----
+----------+ // \\ +----------+
| | | | | |
| Offerer |---------| C:net10 |-----------| Answerer |
| |10.0.1.100| | 10.0.1.101 | |
+----------+ \\ // +----------+
-----
Figure 10: Identical Candidates with Different Bases
In this setup, the offerer is multihomed ๐งโโ๏ธ It has one IP address, 10.0.1.100, on network C, which is a net 10 private network, and the answerer is on that same network too. The offerer is also connected to network A, which is 192.168/16, and it has the IP address 192.168.1.100 there. There's a NAT on that network that translates into network B, another net 10 private network that isn't connected to network C. And there's a STUN server on network B ๐ธ
The offerer gets a host candidate on its network C address (10.0.1.100:2498) and another host candidate on its network A address (192.168.1.100:3344). Then it sends a STUN query to its configured STUN server from 192.168.1.100:3344 โจ That query goes through the NAT, which happens to assign the binding 10.0.1.100:2498. The STUN server reflects that back in the STUN Binding response. Now the offerer has a server reflexive candidate whose transport address is identical to a host candidate (10.0.1.100:2498) ๐คฏ But the two are still different candidates, because the server reflexive candidate has a base of 192.168.1.100:3344, while the host candidate has a base of 10.0.1.100:2498. The bases are what make them distinct, bestie.
B.3. Purpose of the <rel-addr> and <rel-port> Attributes
The candidate attribute carries two values that ICE itself never uses: <rel-addr> and <rel-port> ๐ค So why are they even there?
There are two reasons. The first one is diagnostic, and it's honestly so useful ๐ Knowing how the different candidate types relate to each other means an agent can tell which relayed candidate goes with which server reflexive candidate, and which of those goes with a specific host candidate. When checks succeed for some candidates and fail for others, those relationships give you real clues about what's happening in the network.
The second reason is off-path Quality of Service (QoS) mechanisms ๐ When ICE is used in environments like PacketCable 2.0, proxies don't just do normal SIP stuff. They also inspect the SDP in SIP messages and pull out the IP address and port for the media traffic. Then, through policy servers, they can talk to access routers in the network to set up guaranteed QoS for the media flows ๐ That QoS works by classifying the RTP traffic based on its 5-tuple, then either giving it a guaranteed rate or marking its Diffserv codepoints appropriately. Now, when a residential NAT is in the picture and a relayed candidate gets selected for media, that relayed candidate is a transport address on an actual TURN server ๐ญ That address tells you NOTHING about the real transport address in the access router that would be used to classify packets for QoS treatment. What you actually need is the server reflexive candidate towards the TURN server. By carrying that translation in the SDP, the proxy can use that transport address to request QoS from the access router. Literally the whole plot, iykyk ๐ฆ
B.4. Importance of the STUN Username โจ
okay bestie, this one is about the STUN username, and it's lowkey the main reason ICE has to use message integrity ๐. ICE uses STUN's short-term credential feature for integrity. The actual short-term credential gets built by swapping username fragments during the SDP offer/answer exchange. And this isn't just about security ๐. It's actually needed for ICE to work right at all ๐ .
Picture three agents, L, R, and Z. L and R are inside private enterprise 1, which uses 10.0.0.0/8. Z is inside private enterprise 2, which ALSO uses 10.0.0.0/8 ๐. Plot twist: R and Z both have the IP address 10.0.1.1. L sends an offer to Z, and Z answers with its host candidates, 10.0.1.1:8866 and 10.0.1.1:8877. At that exact same moment, R is in a session too, and it's also using 10.0.1.1:8866 and 10.0.1.1:8877 as its host candidates. So R is ready to accept STUN messages on those ports, just like Z.
Now L sends a STUN request to 10.0.1.1:8866 and another to 10.0.1.1:8877, and they don't go to Z like they're supposed to. They go to R instead ๐ญ. If R just replied, L would think it had connectivity to Z, when it's actually talking to a totally different user, R. That's why the STUN short-term credential mechanisms exist ๐ฅ. The username fragments are random enough that R almost certainly won't be using the same values as Z. So R rejects the STUN request because the credentials are invalid. Basically, the STUN username fragments act like transient host identifiers that are tied to one particular offer/answer session ๐ฆ.
Here's the messy part: because IP addresses aren't unique, R might not even be an ICE agent. It could be any host, and the port the STUN packet hits could be any ephemeral port on it. If there's an application listening on that socket and it isn't ready for malformed packets for whatever protocol it speaks, that application could get messed up ๐. Thankfully, the ports exchanged in SDP are ephemeral and usually come from the dynamic or registered range, so the odds are good that the port isn't running a server on host R. It's more likely the agent side of some protocol ๐ธ. Port usage in that range is pretty transient, so the chance of hitting an allocated port goes down. Still, the problem CAN happen, and network deployers should be ready for it โญ. This isn't an ICE-only thing either. Stray packets can show up on any port, for any protocol, at any time, especially on the public Internet ๐. So this whole requirement is really just a reminder of a general design rule for Internet applications: be prepared for unknown packets on any port ๐ซถ.
B.5. The Candidate Pair Priority Formula ๐งฎ
The priority for a candidate pair looks kinda weird, I know ๐. Here it is:
pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
So why is it built like this? When you sort candidate pairs by this value, the sort ends up having the MAX/MIN property โจ. Pairs get sorted first by decreasing value of the minimum of the two priorities. If two pairs have the same minimum, the maximum priority decides the order between them. And if the max and min are both the same, the controlling agent's priority breaks the tie in the last part of the expression ๐. The factor of 2*32 is used because a single candidate's priority is always less than 2*32, so the pair priority is basically a "concatenation" of the two component priorities ๐. That's how you get the MAX/MIN sorting. And the big payoff: for any given agent, a lower-priority candidate never gets used until all the higher-priority candidates have been tried ๐.
B.6. The remote-candidates Attribute ๐ฏ
The a=remote-candidates attribute is there to kill a race condition between the updated offer and the response to the STUN Binding request that moved a candidate into the Valid list ๐
. You can see the race in Figure 11. When agent L gets message 4, it adds a candidate pair to the valid list. If there was only one media stream with one component, L could send an updated offer right then ๐ค. But R's check hasn't generated its response yet, so R gets the updated offer (message 7) BEFORE it gets the response (message 9). So R doesn't know yet that this pair is valid ๐ญ. To fix that, the actual candidates at R that the offerer picked (the remote candidates) get put inside the offer itself, and the answerer holds off on its answer until those pairs validate ๐ซถ.
Agent A Network Agent B
|(1) Offer | |
|------------------------------------------>|
|(2) Answer | |
|<------------------------------------------|
|(3) STUN Req. | |
|------------------------------------------>|
|(4) STUN Res. | |
|<------------------------------------------|
|(5) STUN Req. | |
|<------------------------------------------|
|(6) STUN Res. | |
|-------------------->| |
| |Lost |
|(7) Offer | |
|------------------------------------------>|
|(8) STUN Req. | |
|<------------------------------------------|
|(9) STUN Res. | |
|------------------------------------------>|
|(10) Answer | |
|<------------------------------------------|
Figure 11: Race Condition Flow
B.7. Why Are Keepalives Needed? ๐ค
Once media starts flowing on a candidate pair, the bindings still have to stay alive at the intermediate NATs for the whole session ๐ฆ. Normally the media packets themselves (like RTP) handle that for you. But there are a few cases where that breaks down, so let's chat about them ๐. First, some RTP usages, like SIP, can put media "on hold". That happens with the SDP sendonly or inactive attributes, as defined in RFC 3264 [RFC3264]. RFC 3264 tells implementations to stop sending media in those cases. But stopping can make the NAT bindings time out, and then the media can't come back off hold ๐ฑ.
Second, some RTP payload formats, like the payload format for text conversation [RFC4103], can send packets so rarely that the gap is longer than the NAT binding timeouts ๐.
Third, if silence suppression is on, long stretches of silence can stop media for long enough that the NAT bindings time out ๐.
Because of all that, you can't count on the media packets alone. So ICE defines a simple periodic keepalive that uses STUN Binding indications ๐ฅ. The nice bonus is that this makes the bandwidth requirements super predictable, which means they're easy to work with for QoS reservations ๐.
B.8. Why Prefer Peer Reflexive Candidates? ๐ต๏ธโโ๏ธ
Section 4.1.2 explains how to compute a candidate's priority from its type and local preferences ๐ง . That section says the type preference for peer reflexive candidates has to ALWAYS be higher than for server reflexive ones. Why? It comes down to the security considerations in Section 18 ๐. It's way easier for an attacker to trick an agent into using a fake server reflexive candidate than a fake peer reflexive one. So ICE stops attacks on address gathering with Binding requests by favouring peer reflexive candidates ๐.
B.9. Why Send an Updated Offer? ๐จ
Section 11.1 has the rules for sending media ๐. Both agents can start sending media as soon as the ICE checks finish, without waiting for an updated offer ๐ . The only real job of the updated offer is to "correct" the SDP, so the default destination for media matches where the media is actually going based on ICE (which will be the highest-priority nominated candidate pair) โจ.
So you might wonder, why do we even need the updated offer/answer exchange? ๐ค In a pure offer/answer world, we wouldn't! The offerer and answerer agree on candidates through ICE and can start using them right away. As far as the agents are concerned, the updated offer tells them nothing new ๐ค. BUT in the real world, a bunch of stuff along the signaling path actually reads the SDP ๐. That includes things doing off-path QoS reservations, NAT traversal pieces like ALGs and Session Border Controllers (SBCs), and diagnostic tools that quietly watch the network. For all of that to keep working without changes, the core SDP idea has to stay the same: the addresses used for media, meaning the m and c lines and the rtcp attribute, need to keep their pre-ICE definitions ๐ซถ. That's exactly why an updated offer has to be sent ๐.
B.10. Why Are Binding Indications Used for Keepalives? ๐ชถ
Media keepalives are described in Section 10 ๐ธ. When both endpoints are ICE capable, those keepalives use STUN. But instead of a Binding request transaction (the kind that gets a response), they use an Indication. Why not a request? The main reason is network QoS mechanisms ๐ฅ. Once media starts flowing, network elements assume the stream has a pretty regular shape, with periodic packets at fixed intervals (with maybe a bit of jitter). If an agent is sending media and then gets a Binding request, it would have to send a response packet alongside its media packets ๐ฌ. That bumps up the real bandwidth needs for the 5-tuple carrying the media, and it adds jitter to how those packets get delivered. Analysis has shown this is a real concern in certain layer 2 access networks that use fairly tight packet schedulers for media โก.
There's another perk too: a Binding Indication lets integrity be turned off, which means better performance ๐. That's really useful for large-scale endpoints like PSTN gateways and SBCs ๐.
B.11. Why Is the Conflict Resolution Mechanism Needed? ๐
Okay so when ICE runs between two peers, one agent ends up as controlled and the other as controlling ๐. There are rules that decide who gets which role, and they depend on the implementation type and whether you're the offerer or the answerer. BUT the spec also admits that sometimes both sides might think they're controlling, or both might think they're controlled. How does that even happen?? ๐ญ
The "both think they're controlled" situation shows up in third party call control cases. Check out this flow:
A Controller B
|(1) INV() | |
|<-------------| |
|(2) 200(SDP1) | |
|------------->| |
| |(3) INV() |
| |------------->|
| |(4) 200(SDP2) |
| |<-------------|
|(5) ACK(SDP2) | |
|<-------------| |
| |(6) ACK(SDP1) |
| |------------->|
Figure 12: Role Conflict Flow
This flow is a twist on flow III of RFC 3725 [RFC3725] ๐ฆ. Honestly? It's even better than flow III because it sends fewer messages, iconic ๐ . Here's the tea: the controller sends an offerless INVITE to agent A, and A replies with its offer, SDP1. Then the agent sends an offerless INVITE to agent B, and B replies with its offer, SDP2. The controller uses each agent's offer to generate the answers. When this flow is used, ICE runs between agents A and B, but both of them believe they're in the controlling role ๐. The role conflict resolution procedures are what make this flow work properly when ICE is used, so it's giving "it was designed for this" โจ.
As for the "both think they're controlled" case, there are no documented flows that actually produce it right now ๐ธ. The conflict resolution procedures still allow for it, just in case a flow shows up that fits that category. Protecting for the unknown is such a main character move ๐๐ซถ.
Author's Address ๐ง
Jonathan Rosenberg
jdrosen.net
Monmouth, NJ
US
Email: jdrosen@jdrosen.net
URI: http://www.jdrosen.net
๐ฆ where this page comes from
- ietf: rfc5245 2010-04
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms