RFC 5245

Interactive Connectivity Establishment (ICE): A Protocol for Network Address Translator (NAT) Traversal for Offer/Answer Protocols

๐Ÿ“… published April 2010, from ietf

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 (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:

  1. Sort the candidate pairs in priority order.
  2. Send checks on each candidate pair in priority order.
  3. 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 c line of the SDP, and the port is in the m line. For the RTCP component, it is in the rtcp attribute when present. When not present, the IP address is in the c line and 1 plus the port is in the m line. 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 ๐Ÿข

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 ๐Ÿ™ƒ:

  1. The agent MUST follow the rules of Section 10, which describe keepalive procedures for all agents ๐Ÿ’“.
  2. If the agent isn't going with ICE because there were a=candidate attributes but none matched the default destination of the media stream, the agent MUST include an a=ice-mismatch attribute in its answer ๐Ÿšฉ.
  3. 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 ๐Ÿง

  1. The agent sets all of the pairs in each check list to the Frozen state.
  2. 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:

  1. 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.
  2. 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 ๐Ÿ’–.

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-CONTROLLING nor ICE-CONTROLLED is 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-CONTROLLING is 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 an ERROR-CODE attribute 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 is in the controlled role, and ICE-CONTROLLED is 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 an ERROR-CODE attribute with a value of 487 (Role Conflict) but keeps its role ๐Ÿ™….
  • If the agent is in the controlled role and ICE-CONTROLLING was present in the request, or the agent was in the controlling role and ICE-CONTROLLED was 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 PRIORITY attribute 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-candidates attribute ๐Ÿ“.
    • 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-ufrag and a=ice-pwd attributes 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 m and c lines for RTP, and the a=rtcp attribute for RTCP)
  • Set the local candidate equal to the transport address for that same component in the a=remote-candidates attribute 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-candidates attribute 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 m and c lines for RTP, and the a=rtcp attribute for RTCP).
  • Setting the local candidate equal to the transport address for that same component in the a=remote-candidates attribute 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 the ICE-CONTROLLING or ICE-CONTROLLED attribute, 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