RFC 6544

TCP Candidates with Interactive Connectivity Establishment (ICE)

πŸ“… published March 2012, from ietf

Part 1 of 7

RFC 6544: TCP Candidates with Interactive Connectivity Establishment (ICE) πŸ’–

okay bestie, let's get into it πŸŽ€ this is RFC 6544, titled "TCP Candidates with Interactive Connectivity Establishment (ICE)". It's an IETF doc from March 2012, filed under Standards Track (ISSN 2070-1721). The authors are J. Rosenberg (jdrosen.net), A. Keranen (Ericsson), B. B. Lowekamp (Skype), and A. B. Roach (Tekelec). The header doesn't list anything it obsoletes or updates ✨

Abstract 🌸

Interactive Connectivity Establishment (ICE) is a way to do NAT traversal for multimedia protocols that use the offer/answer model of session negotiation. ICE hands out a bunch of candidate transport addresses for each media stream, then checks them with peer-to-peer connectivity checks based on Session Traversal Utilities for NAT (STUN). It gives you a general framework for describing candidates, but it only defines UDP-based media streams 😭 This spec extends ICE to TCP-based media, including the ability to offer a mix of TCP and UDP-based candidates for a single stream πŸ¦‹

Status of This Memo 🧚

This is an Internet Standards Track document.

This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). For more on Internet Standards, check out Section 2 of RFC 5741.

Info about the current status of this doc, any errata, and how to give feedback on it lives at http://www.rfc-editor.org/info/rfc6544.

Copyright (c) 2012 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.

This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of this material may not have granted the IETF Trust the right to allow modifications of such material outside the IETF Standards Process. Without obtaining an adequate license from the person(s) controlling the copyright in such materials, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process, except to format it for publication as an RFC or to translate it into languages other than English.

Table of Contents πŸ“

   1. Introduction ....................................................4
   2. Terminology .....................................................5
   3. Overview of Operation ...........................................5
   4. Sending the Initial Offer .......................................7
      4.1. Gathering Candidates .......................................7
      4.2. Prioritization .............................................8
      4.3. Choosing Default Candidates ...............................10
      4.4. Lite Implementation Requirements ..........................10
      4.5. Encoding the SDP ..........................................11
   5. Candidate Collection Techniques ................................12
      5.1. Host Candidates ...........................................12
      5.2. Server Reflexive Candidates ...............................13
      5.3. NAT-Assisted Candidates ...................................13
      5.4. UDP-Tunneled Candidates ...................................14
      5.5. Relayed Candidates ........................................15
   6. Receiving the Initial Offer and Answer .........................15
      6.1. Considerations with Two Lite Agents .......................16
      6.2. Forming the Check Lists ...................................16
   7. Connectivity Checks ............................................17
      7.1. STUN Client Procedures ....................................17
      7.2. STUN Server Procedures ....................................18
   8. Concluding ICE Processing ......................................18
   9. Subsequent Offer/Answer Exchanges ..............................18
      9.1. Updated Offer .............................................18
      9.2. ICE Restarts ..............................................19
   10. Media Handling ................................................19
      10.1. Sending Media ............................................19
      10.2. Receiving Media ..........................................20
   11. Connection Management .........................................20
      11.1. Connections Formed during Connectivity Checks ............20
      11.2. Connections Formed for Gathering Candidates ..............21
   12. Security Considerations .......................................22
   13. IANA Considerations ...........................................23
   14. Acknowledgements ..............................................23
   15. References ....................................................23
      15.1. Normative References .....................................23
      15.2. Informative References ...................................24
   Appendix A.  Limitations of ICE TCP ...............................26
   Appendix B.  Implementation Considerations for BSD Sockets ........27
   Appendix C.  SDP Examples .........................................28

1. Introduction ✨

ICE [RFC5245] is a way to do NAT traversal for multimedia communication protocols based on the offer/answer model [RFC3264] of session negotiation. It hands out a set of candidate transport addresses for each media stream, then validates them with peer-to-peer connectivity checks based on Session Traversal Utilities for NAT (STUN) [RFC5389]. The catch? ICE only defines procedures for UDP-based transport protocols πŸ₯²

Okay, so why does ICE support for TCP matter? πŸ€” First, there are media protocols that only run over TCP. They're used for things like screen sharing and instant messaging [RFC4975]. For those protocols to work behind NAT, ICE support for TCP is needed, unless the protocols come with their own NAT traversal mechanisms. Also, RTP can run over TCP [RFC4571]. Normally RTP over UDP is the move, not TCP. BUT in some network environments, super restrictive NAT and firewall devices block UDP completely while still letting general TCP through. In those spots, sending RTP over TCP (and actually getting the media session up) is way better than having the whole thing fail πŸ’€

Here's the plan with this spec: agents gather both UDP and TCP candidates for a media stream, rank the UDP ones higher, and only use the TCP ones if the UDP ones fail. That's a built-in fallback, which makes multimedia communication super reliable πŸ’…

The RTP-over-TCP setup gets even better with Traversal Using Relays around NAT (TURN) [RFC5766]. Picture this: one agent connects to its TURN server over TCP and gets a TCP-based relayed candidate. It offers that to its peer as a candidate. The other agent starts a TCP connection toward the TURN server. Once that connection is up, media flows through the TURN server 🌈 The best part is that it only needs agents to make outbound TCP connections to a server on the public network. That works pretty much everywhere through NAT and firewall devices. ICE and this extension are all about highly reliable communications that "just work" in as many network setups as possible, so this use case is a big deal 🫢

This specification extends ICE by defining how it's used with TCP candidates. It also defines how ICE works with RTP and Secure RTP (SRTP) to give you both TCP and UDP candidates. It follows the outline of ICE itself and calls out the additions and changes needed for TCP candidates. The base behavior of ICE [RFC5245] stays the same, except for the extensions in this doc that define ICE usage with TCP candidates ⭐

Quick heads-up: TCP NAT traversal is more complicated than UDP, so ICE TCP is generally not as efficient as UDP-based ICE. There's more on this in Appendix A πŸ™ˆ

2. Terminology πŸ’…

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [RFC2119].

This document uses the same terminology as ICE (see Section 3 of [RFC5245]). πŸ¦‹

3. Overview of Operation ✨

okay so ICE over TCP is honestly pretty straightforward πŸ’… this spec is mostly about how and when connections get opened, and how those connections line up with candidate pairs πŸ¦‹

when agents allocate addresses to gather TCP-based candidates, there are three flavours: active, passive, and simultaneous-open (S-O) candidates 🌸 an active candidate is one where the agent tries to open an outbound connection but won't get incoming connection requests. A passive candidate is the opposite: the agent gets incoming connection attempts but never tries to connect itself 🧚 and an S-O candidate is one where the agent tries to open a connection at the same time as its peer 🫢

when you gather candidates from a host interface, you usually end up with active, passive, and S-O candidates all together ✨ you can also use different techniques to get server reflexive, NAT-assisted, tunneled, or relayed candidates of those same three types (see Section 5) πŸ’œ and connections to the servers used for relayed and server reflexive candidates stay open during ICE processing, no cap πŸ”₯

when these candidates get encoded into offers and answers, the candidate type gets signaled πŸŽ€ for active candidates, both the IP address and the port are there, but the port is meaningless πŸ™ˆ it's only there so active candidates encode the same way as the other types, and the peer just ignores it. The big payoff is that active candidates don't have to be physically allocated when you gather addresses πŸ¦‹ the real allocations happen as a result of a connection attempt, which means they happen during the connectivity checks ⭐

when candidates get paired, active candidates are always paired with passive ones, and S-O candidates get paired with each other πŸ’– when a connectivity check is going to run on a candidate pair, each agent works out whether it's the one that should make a connection attempt for that pair πŸ‘‘

the actual process of generating connectivity checks, managing the check list state, and updating the Valid list works exactly the same for TCP as it does for UDP πŸ’…βœ¨

ICE needs an agent to demultiplex STUN and application-layer traffic, since they show up on the same port 🌈 this is described in [RFC5245] and it's done with the magic cookie and other fields of the message πŸ“ stream-oriented transports add a wrinkle though, because they need a way to frame the connection so the application and STUN packets can be pulled out in order and told apart 😭 so TCP media streams using ICE use the basic framing from RFC 4571 [RFC4571], even when the application protocol isn't RTP 🫢

when Transport Layer Security (TLS) or Datagram Transport Layer Security (DTLS) is in use, they also run over the RFC 4571 framing shim, while STUN runs outside the (D)TLS connection πŸ”’ the resulting ICE TCP protocol stack is shown in Figure 1, with (D)TLS on the left and without it on the right πŸŽ€

                       +----------+
                       |          |
                       |    App   |
            +----------+----------+     +----------+----------+
            |          |          |     |          |          |
            |   STUN   |  (D)TLS  |     |   STUN   |    App   |
            +----------+----------+     +----------+----------+
            |                     |     |                     |
            |      RFC 4571       |     |      RFC 4571       |
            +---------------------+     +---------------------+
            |                     |     |                     |
            |         TCP         |     |         TCP         |
            +---------------------+     +---------------------+
            |                     |     |                     |
            |         IP          |     |         IP          |
            +---------------------+     +---------------------+

              Figure 1: ICE TCP Stack with and without (D)TLS

so for any media stream protected by (D)TLS, the agent runs ICE procedures first and exchanges STUN messages 🧚 then, once ICE completes, the (D)TLS procedures kick off πŸ’œ ICE and (D)TLS are basically "peers" in the protocol stack, bestie πŸ‘―β€β™€οΈ and the STUN messages are never sent over the (D)TLS connection, not even the ones used as keepalives in the middle of the media session πŸ™…β€β™€οΈβœ¨

4. Sending the Initial Offer

okay now for offerers who are using ICE for TCP streams, these are the procedures to follow πŸŽ€ the main differences compared to UDP candidates are the new ways of gathering candidates, how TCP candidates get prioritized, and how they're encoded in the Session Description Protocol (SDP) offer and answer 🌸

4.1. Gathering Candidates

okay so providers of real-time communications services might decide they'd rather have no media at all than media over TCP πŸ’€ to allow for that choice, it is highly RECOMMENDED that agents can be configured to either obtain or not obtain TCP candidates for real-time media 🌈

having it be configurable, and then configuring it to be off, is WAY better than not having the capability at all 😭 a big goal of this spec is to give you one mechanism that works across all types of endpoints πŸ¦‹ so it's better to handle provider and network variation through configuration instead of hard-coding limitations into an implementation πŸ’… and honestly, network characteristics and connectivity assumptions change over time πŸ™ˆ just because an agent is talking to a server on the public network today doesn't mean it won't need to talk to one behind a NAT tomorrow πŸ’€ just because an agent is behind a NAT with endpoint-independent mapping today doesn't mean tomorrow it won't get picked up and carried to a public access point with a NAT that has address- and port-dependent mapping, or one that only allows outbound TCP 🧳 the way to handle all of this and build a reliable system is for agents to implement a diverse set of address allocation techniques, so at least one of them is almost certainly going to work in any situation πŸ‘‘ implementors should consider very carefully any assumptions they make about deployments before choosing not to implement one of the address allocation mechanisms πŸ” especially whether the elements in the system might be mobile and connect through different networks with different connectivity πŸš— and whether endpoints that are under their control, in terms of location and network connectivity, would always stay under their control πŸ€” in environments where mobility and user control are possible, having a multiplicity of techniques is essential for reliability 🫢✨

first, agents SHOULD obtain host candidates as described in Section 5.1 🌸 then each agent SHOULD "obtain" (allocate a placeholder for) an active host candidate for each component of each TCP-capable media stream on each interface the host has 🧚 the agent doesn't have to actually allocate a port for these candidates yet, but they get used to create the check lists πŸ’œ

the agent SHOULD then obtain server reflexive, NAT-assisted, and/or UDP-tunneled candidates (see Section 5.2, Section 5.3, and Section 5.4) πŸ¦‹ how you establish these and how many candidates you collect varies from technique to technique, and those considerations are discussed in the relevant sections πŸ“š

next, agents SHOULD obtain passive (and possibly S-O) relayed candidates for each component as described in Section 5.5 🌈 each agent SHOULD also allocate a placeholder for an active relayed candidate for each component of each TCP-capable media stream πŸŽ€

it is highly RECOMMENDED that a host obtains at least one set of host candidates and one set of relayed candidates πŸ’– getting extra candidates increases the chance of successfully creating a direct connection ⭐

once the candidates have been obtained, the agent MUST keep the TCP connections open until ICE processing has completed πŸ”₯ see Appendix B for important implementation guidelines πŸ“–

if a media stream is UDP-based (like RTP), an agent MAY use an additional host TCP candidate to request a UDP-based candidate from a TURN server (or some other relay with similar functionality) 🧚 the usage of those UDP candidates follows the procedures defined in ICE for UDP candidates πŸ¦‹

like their UDP counterparts, TCP-based STUN transactions are paced out at one every Ta milliseconds (see Section 16 of [RFC5245]) ⏰ this pacing refers strictly to STUN transactions (both Binding and Allocate requests) πŸ’… and if a transaction needs a TCP connection to be established first, that connection gets opened when the transaction is performed 🫢

4.2. Prioritization πŸ’…

Okay so the transport protocol itself is a criteria for choosing one candidate over another πŸ¦‹ If a media stream can run over UDP or TCP, the UDP candidates might be preferred over the TCP candidates. This lets ICE use the lower latency UDP connectivity if it exists, but fall back to TCP if UDP doesn't work. It's giving smart planning ✨

In Section 4.1.2.1 of [RFC5245], a recommended formula for UDP ICE candidate prioritization is defined. For TCP candidates, the same formula and candidate type preferences SHOULD be used, and the RECOMMENDED type preferences for the new candidate types defined in this document (see Section 5) are 105 for NAT-assisted candidates and 75 for UDP-tunneled candidates. πŸŽ€

When both UDP and TCP candidates are offered for the same media stream, and one transport protocol should be preferred over the other, the type preferences for the preferred transport protocol candidates SHOULD be increased and/or the type preferences for the other transport protocol candidates SHOULD be decreased. How much the values should be increased or decreased depends on whether it is more important to choose a certain transport protocol or a certain candidate type. 🌸

If the candidate type is more important (e.g., even if UDP is preferred, TCP host candidates are preferred over UDP server reflexive candidates), changing type preference values by one for the other transport protocol candidates is enough. On the other hand, if the transport protocol is more important (e.g., any UDP candidate is preferred over any TCP candidate), all the preferred transport protocol candidates SHOULD have type preference higher than the other transport protocol candidates. πŸ‘‘

However, it is RECOMMENDED that the relayed candidates are still preferred lower than the other candidate types. For RTP-based media streams, it is RECOMMENDED that UDP candidates are preferred over TCP candidates. 🧚

With TCP candidates, the local preference part of the recommended priority formula is updated to also include the directionality (active, passive, or simultaneous-open) of the TCP connection. The RECOMMENDED local preference is then defined as:

   local preference = (2^13) * direction-pref + other-pref

The direction-pref MUST be between 0 and 7 (both inclusive), with 7 being the most preferred. The other-pref MUST be between 0 and 8191 (both inclusive), with 8191 being the most preferred. πŸ’œ

It is RECOMMENDED that the host, UDP-tunneled, and relayed TCP candidates have the direction-pref assigned as follows: 6 for active, 4 for passive, and 2 for S-O. For the NAT-assisted and server reflexive candidates, the RECOMMENDED values are: 6 for S-O, 4 for active, and 2 for passive. ⭐

The preference priorities listed here are simply recommendations that try to strike a balance between success probability and the resulting path's efficiency. Depending on the scenario where ICE TCP is used, different values may be appropriate. For example, if the overhead of a UDP tunnel is not an issue, those candidates should be prioritized higher since they are likely to have a high success probability. πŸ“

Also, simultaneous-open is prioritized higher than active and passive candidates for NAT-assisted and server reflexive candidates, since if TCP S-O is supported by the operating systems of both endpoints, it should work at least as well as the active-passive approach. If an implementation is uncertain whether S-O candidates are supported, it may be reasonable to prioritize them lower. For host, UDP-tunneled, and relayed candidates, the S-O candidates are prioritized lower than active and passive, since active-passive candidates should work with them at least as well as the S-O candidates. πŸ™ˆ

If any two candidates have the same type-preference and direction-pref, they MUST have a unique other-pref. With this specification, this usually only happens with multi-homed hosts, in which case other-pref is the preference for the particular IP address from which the candidate was obtained. When there is only a single IP address, this value SHOULD be set to the maximum allowed value (8191). πŸ’–


4.3. Choosing Default Candidates 🌈

The default candidate is chosen primarily based on the likelihood of it working with a non-ICE peer. When media streams supporting mixed modes (both TCP and UDP) are used with ICE, it is RECOMMENDED that, for real-time streams (such as RTP), the default candidates be UDP-based. However, the default SHOULD NOT be a simultaneous-open candidate. πŸ¦‹

If a media stream is inherently TCP-based, it is RECOMMENDED for an offering full agent to select an active candidate as the default candidate and use [RFC4145] "setup" attribute value "active". This increases the chances for a successful NAT traversal, even without ICE support, if the agent is behind a NAT and the peer is not. For the same reason, for a lite agent, it is RECOMMENDED to use a passive candidate and "setup" attribute value "passive" in the offer. 🫢


4.4. Lite Implementation Requirements πŸŽ€

If an offerer meets the criteria for the lite mode as described in Appendix A of [RFC5245] (i.e., it will always have a public, globally unique IP address), it MAY use the lite mode of ICE for TCP candidates. In the lite mode, for TCP candidates, only passive host candidates are gathered, unless active candidates are needed as the default candidates. Otherwise, the procedures described for lite mode in [RFC5245] also apply to TCP candidates. If UDP and TCP candidates are mixed in a media stream, the mode (lite or full) applies to both UDP and TCP candidates. πŸ’…


4.5. Encoding the SDP πŸ”₯

TCP-based candidates are encoded into a=candidate lines like the UDP candidates described in [RFC5245]. However, the transport protocol (i.e., value of the transport-extension token defined in [RFC5245], Section 15.1) is set to "TCP", and the connection type (active, passive, or S-O) is encoded using a new extension attribute. With TCP candidates, the candidate-attribute syntax with Augmented BNF [RFC5234] is then:

   candidate-attribute   = "candidate" ":" foundation SP component-id SP
                           "TCP" SP
                           priority SP
                           connection-address SP
                           port SP
                           cand-type
                           [SP rel-addr]
                           [SP rel-port]
                           SP tcp-type-ext
                           *(SP extension-att-name SP
                                extension-att-value)

   tcp-type-ext          = "tcptype" SP tcp-type
   tcp-type              = "active" / "passive" / "so"

The connection-address encoded into the candidate-attribute for active candidates MUST be set to the IP address that will be used for the attempt, but the port(s) MUST be set to 9 (i.e., Discard). For active relayed candidates, the value for connection-address MUST be identical to the IP address of a passive or simultaneous-open candidate from the same relay server. 🧚

If the default candidate is TCP-based, the agent MUST include the a=setup and a=connection attributes from RFC 4145 [RFC4145], following the procedures defined there as if ICE were not in use. In particular, if an agent is the answerer, the a=setup attribute MUST meet the constraints in RFC 4145 based on the value in the offer. ✨

If an agent is utilizing SRTP [RFC3711], it MAY include a mix of UDP and TCP candidates. If ICE selects a TCP candidate pair, it is RECOMMENDED that the agent still utilizes SRTP but runs it over the connection established by ICE. The alternative, RTP over TLS, breaks RTP header compression and on-path RTP analysis tools and hence SHOULD be avoided. πŸ’€ In the case of DTLS-SRTP [RFC5764], the directionality attributes (a=setup) are utilized strictly to determine the direction of the DTLS handshake. Directionality of the TCP connection establishment is determined by the ICE attributes and procedures defined here. 🌸

If an agent is securing non-RTP media over TCP/TLS, the SDP MUST be constructed as described in RFC 4572 [RFC4572]. The directionality attributes (a=setup) are utilized strictly to determine the direction of the TLS handshake. Directionality of the TCP connection establishment is determined by the ICE attributes and procedures defined here. πŸ¦‹

Examples of SDP offers and answers with ICE TCP extensions are shown in Appendix C. πŸŽ€


5. Candidate Collection Techniques πŸ—ΊοΈ

The following sections discuss a number of techniques that can be used to obtain candidates for use with ICE TCP. It is important to note that this list is not intended to be exhaustive, nor is implementation of any specific technique beyond host candidates (Section 5.1) considered mandatory. πŸ’–

Implementors are encouraged to implement as many of the following techniques from the following list as is practical, as well as to explore additional NAT-traversal techniques beyond those discussed in this document. However, to get a reasonable success ratio, one SHOULD implement at least one relayed technique (e.g., TURN) and one technique for discovering the address given for the host by a NAT (e.g., STUN). πŸ‘‘

To increase the success probability with the techniques described below, and to aid with transition to IPv6, implementors SHOULD take particular care to include both IPv4 and IPv6 candidates as part of the process of gathering candidates. If the local network or host does not support IPv6 addressing, then clients SHOULD make use of other techniques, e.g., TURN-IPv6 [RFC6156], Teredo [RFC4380], or SOCKS IPv4-IPv6 gatewaying [RFC3089], for obtaining IPv6 candidates. 🌈

While implementations SHOULD support as many techniques as feasible, they SHOULD also consider which of them to use if multiple options are available. Since different candidates are paired with each other, offering a large number of candidates results in a large check list and potentially long-lasting connectivity checks. For example, using multiple NAT-assisted techniques with the same NAT usually results only in redundant candidates. Similarly, using just one of the multiple UDP tunneling or relaying techniques is often enough. πŸ“

5.1. Host Candidates 🏠

Host candidates are the most simple candidates, since they only require opening TCP sockets on the host's interfaces and sending/receiving connectivity checks from them. However, if the hosts are behind different NATs, host candidates usually fail to work. On the other hand, if there are no NATs between the hosts, host candidates are the most efficient method, since they require no additional NAT traversal protocols or techniques. πŸ§ƒ

For each TCP-capable media stream the agent wishes to use (including ones like RTP that can be either UDP or TCP), the agent SHOULD obtain two host candidates (each on a different port) for each component of the media stream on each interface that the host has -- one for the simultaneous-open and one for the passive candidate. If an agent is not capable of acting in one of these modes, it would omit those candidates. 🫢

5.2. Server Reflexive Candidates ✨

okay so server reflexive techniques are all about finding out what address a NAT gave the host πŸ‘€. You ask a server on the other side of the NAT, then create proper bindings on the NATs (unless they already exist) with connectivity checks sent between the hosts πŸ¦‹. Whether this slays depends on the NATs' mapping and filtering behavior [RFC5382], and also on whether the NATs and hosts support the TCP simultaneous-open technique πŸ’….

Getting server reflexive passive candidates may mean starting connections from the host's passive candidates, so check out Appendix B for the implementation details 🧚. Server reflexive active candidates can be derived from passive or S-O candidates by using the same IP addresses and interfaces as those candidates. It's honestly useful to get both the server reflexive passive and S-O candidates, because which one works better depends on the hosts and NATs πŸ€·β€β™€οΈ. Also, some techniques (e.g., TURN relaying) need to know the IP address of the peer's active candidates beforehand, so active server reflexive candidates are needed for those techniques to work properly πŸŽ€.

A widely used protocol for getting server reflexive candidates is STUN 🌸. Its TCP-specific behavior is described in [RFC5389], Section 7.2.2.

5.3. NAT-Assisted Candidates

NAT-assisted techniques talk to the NATs directly, so they can discover the address the NAT gave the host πŸ“‘. They also create proper bindings on the NATs ✨. The benefit over server reflexive techniques is that the NATs can adjust their mapping and filtering behavior so connections can actually get created πŸ™Œ. The downside is that they commonly allow communicating only with a NAT in the same subnet as the host, so they often fail when there are multiple layers of NATs 😭. These techniques also rely on NATs supporting the specific protocols and on users being allowed to modify their behavior πŸ™ƒ.

These candidates are encoded in the ICE offer and answer just like the server reflexive candidates. They (commonly) use a higher priority (as described in Section 4.2), so they get tested before the server reflexive candidates πŸ”₯.

Right now, the Universal Plug and Play (UPnP) forum's Internet Gateway Device (IGD) protocol [UPnP-IGD] and the NAT Port Mapping Protocol (PMP) [NAT-PMP] are the widely supported NAT-assisted techniques πŸ‘‘. Other known protocols include Port Control Protocol (PCP) [PCP-BASE], SOCKS [RFC1928], Realm Specific IP (RSIP) [RFC3103], and Simple Middlebox Configuration (SIMCO) [RFC4540]. Also, the Middlebox Communications (MIDCOM) MIB [RFC5190] defines a mechanism based on the Simple Network Management Protocol (SNMP) for controlling NATs 🧁.

5.4. UDP-Tunneled Candidates

UDP-tunneled NAT traversal techniques lean on the fact that UDP NAT traversal is simpler and more efficient than TCP NAT traversal πŸ¦‹. With these techniques, TCP packets (or possibly complete IP packets) get wrapped up inside UDP packets. Because of the encapsulation, these techniques add overhead to the connection and may need support from both endpoints 😬. BUT on the flip side, UDP tunneling commonly results in reliable and fairly simple TCP NAT traversal πŸ’–.

UDP-tunneled candidates can be encoded in the ICE offer and answer either as relayed or server reflexive candidates, depending on whether the tunneling protocol uses a relay between the hosts πŸŽ€. These candidates may show up to applications as host candidates from a local pseudo-interface. Treating them as host candidates results in incorrect prioritization and possibly non-optimal candidate selection, which is a whole vibe-kill πŸ’€. Implementations may try to detect pseudo-interfaces, e.g., from the address prefix of the interface, but the detection details vary from technique to technique πŸ•΅οΈβ€β™€οΈ.

For example, the Teredo protocol [RFC4380] [RFC6081] provides automatic UDP tunneling and IPv6 interworking 🌈. The Teredo UDP tunnel is visible to the host application as an IPv6 address, so Teredo candidates are encoded as IPv6 addresses ✨.

5.5. Relayed Candidates

Relaying packets through a relay server is often the NAT traversal technique with the highest success probability πŸ‘‘. Communicating via a relay in the public Internet looks like normal client-server communication to the NATs, and it's supported in practice by all existing NATs, regardless of their filtering and mapping behavior 🫢. However, relaying has some drawbacks. It usually results in a sub-optimal path for the packets, the relay needs to exist and needs to be discovered, the relay is a possible single point of failure, and relaying can eat a lot of the relay server's resources 😩. So relaying is often used as the last resort when no direct path can be created with other NAT traversal techniques 🫠.

With relayed candidates, the host commonly needs to obtain only a passive candidate, since any of the peer's server reflexive (and NAT-assisted, if the peer can communicate with the outermost NAT) active candidates should work with the passive relayed candidate 🧚. However, if the relay is behind a NAT or a firewall, using active and S-O candidates too will increase the success probability πŸ“ˆ.

Relaying protocols that can relay TCP connections include TURN TCP [RFC6062] and SOCKS [RFC1928] (which can also be used for IPv4-IPv6 gatewaying [RFC3089]). It's also possible to use a Secure SHell (SSH) [RFC4251] tunnel as a relayed candidate, if a suitable server is available and the server permits this πŸ“.

6. Receiving the Initial Offer and Answer πŸ’Œ

Handling an ICE offer with TCP candidates works in a similar way to UDP candidates πŸ¦‹. First, ICE support is verified (including the check for ice-mismatch described in Section 5.1 of [RFC5245]) and agent roles are determined πŸ”. Candidates are gathered using the techniques described in Section 5 and prioritized as described in Section 4.2. Default candidates are selected taking into account the considerations in Section 4.3. The SDP answer is encoded as in Section 4.3 of [RFC5245], except for TCP candidates, whose encoding is described in Section 4.5 πŸ“.

When the offerer receives the initial answer, it also verifies ICE support and determines its role πŸŽ€. If both agents use lite implementations, the offerer takes the controlling role and uses the procedures defined in [RFC4145] to select the most preferred candidate pair with a new offer πŸ‘‘.

6.1. Considerations with Two Lite Agents

If both agents are using lite mode, and the offerer uses the a=setup:active attribute [RFC4145] in the new offer, the offerer MAY initiate the TCP connection on the selected pair in parallel with the new offer, to speed up connection establishment πŸƒβ€β™€οΈπŸ’¨. Because of this, the answerer MUST still accept incoming TCP connections to any of the passive candidates it listed in the answer, from any of the IP addresses the offerer listed in the initial offer 🫢.

If the answerer receives the new offer matching the candidate pair where a connection was already created in parallel with the new offer, it MUST accept the offer and respond to it while keeping the already-created connection πŸ₯°. If the connection created in parallel with the new offer does not match the candidate pair in the new offer, the connection MUST be closed, and ICE restart SHOULD be performed πŸ™ˆ.

Since the connection endpoints are not authenticated using the connectivity checks when both agents use lite mode, unless media-level security (e.g., TLS) is used, it is RECOMMENDED to use full mode instead πŸ”. For more lite versus full implementation considerations, see Appendix A of [RFC5245] πŸ“š.

6.2. Forming the Check Lists

As with UDP, check lists are formed only by full ICE implementations 🧩. When forming candidate pairs, the following types of TCP candidates can be paired with each other:

Local           Remote
Candidate       Candidate
---------------------------
tcp-so          tcp-so
tcp-active      tcp-passive
tcp-passive     tcp-active

When the agent prunes the check list, it MUST also remove any pair for which the local candidate is a passive TCP candidate 🧹. With pruning, NAT-assisted candidates are treated like server reflexive candidates if the base is also used as a host candidate πŸ’….

The rest of the check list processing works the same way as the UDP case 🌸.

7. Connectivity Checks ⭐

The TCP connectivity checks, like with UDP, are generated only by full implementations πŸ™Œ. The TCP candidate pairs sit in the same check list as the UDP candidate pairs, and they're scheduled for connectivity checks, as described in Section 5.8 of [RFC5245], based on priority order πŸ“‹.

7.1. STUN Client Procedures

When an agent wants to send a TCP-based connectivity check, it first opens a TCP connection (if none exists yet) for the 5-tuple defined by the candidate pair the check is going to be sent on πŸ›€οΈ. This connection is opened from the local candidate of the pair to the remote candidate of the pair. If the local candidate is tcp-active, the agent MUST open a connection from the interface associated with that local candidate. This connection SHOULD be opened from an unallocated port πŸ”Œ. For host candidates, this is easy: just connect from the local candidate's interface πŸ₯³. For relayed, NAT-assisted, and UDP-tunneled candidates, the agent may need extra procedures specific to the protocol 🧠.

Once the connection is established, the agent MUST utilize the shim defined in RFC 4571 [RFC4571] for as long as the connection stays open πŸ’«. The STUN Binding requests and responses are sent on top of this shim, so the length field defined in RFC 4571 precedes each STUN message πŸ“¦. If TLS or DTLS-SRTP is going to be used for the media session, the TLS or DTLS-SRTP handshakes take place on top of this shim as well πŸ”. However, they only start once ICE processing has completed. Basically, the TLS or DTLS-SRTP handshakes are considered part of the media protocol 🎧. STUN is never run within the TLS or DTLS-SRTP session as part of the ICE procedures πŸ™…β€β™€οΈ.

If the TCP connection cannot be established, the check is considered to have failed, and a full-mode agent MUST update the pair state to Failed in the check list πŸ’”. See Section 7.2.2 of [RFC5389] for more details on STUN over TCP πŸ“–.

Once the connection is established, client procedures are identical to those for UDP candidates πŸ’–. However, retransmissions of the STUN connectivity check messages are not needed, since TCP takes care of reliable delivery of the messages 🫢. Note also that STUN responses received on an active TCP candidate will typically produce a peer reflexive candidate πŸͺž. If the response to the first connectivity check on the established TCP connection is something other than a STUN message, the remote candidate address apparently was not one of the peer's addresses, so the agent SHOULD close the connection and consider all pairs with that remote candidate as failed 🚫.

7.2. STUN Server Procedures ✨

okay so ICE TCP agents (full or lite, doesn't matter) MUST be ready to catch incoming TCP connection requests on the base of any TCP candidate that is simultaneous-open or passive 🧚. When a request comes in, the agent MUST accept it, no drama. πŸ’… It also MUST use the framing from [RFC4571] for the whole life of that connection, bestie. Because of that framing, the data shows up in discrete frames πŸ“¦. Each frame could be media (like RTP or SRTP), TLS, DTLS, or STUN packets. The STUN packets get pulled out the way Section 10.2 describes πŸ”.

Once the connection is up, STUN server procedures are literally identical to the ones for UDP candidates 🫢. Also, STUN requests that land on a passive TCP candidate will typically spit out a remote peer reflexive candidate πŸ¦‹. Iconic.

8. Concluding ICE Processing πŸŽ€

If a media stream has TCP candidates, a controlling agent MUST use the regular selection algorithm πŸ‘‘.

When ICE processing for a media stream wraps up, each agent SHOULD close all TCP connections (the ones that got opened because of this ICE session) except the ones between the candidate pairs ICE selected 🧹.

These two rules are connected, so pay attention πŸ‘€. Closing the connections when ICE finishes means you HAVE to use the regular selection algorithm. The reason is that aggressive selection could pick transient pairs. Once one of those gets selected, the agents would close the other connections, and one of those might have been about to become the better choice 😭. That race condition could accidentally close the TCP connections for the pair that ICE actually picked. Not it. πŸ™ˆ

9. Subsequent Offer/Answer Exchanges πŸ¦‹

9.1. Updated Offer

When the controlling endpoint makes an updated offer after the connectivity checks succeed, the SDP extensions for connection-oriented media [RFC4145] are used to say "hey, reuse the existing connection instead of opening a new one" 🀝.

9.2. ICE Restarts

If an ICE restart happens on a media stream that has TCP candidate pairs ICE already selected, the agents MUST NOT close those connections after the restart 🚫. In the offer or answer that kicks off the restart, an agent MAY include a simultaneous-open candidate whose transport address matches the previously selected candidate. If both agents do this, you end up with a simultaneous-open candidate pair that matches an existing TCP connection, and that's giving full circle πŸ”„. In that case, the agents MUST NOT attempt to open a new connection (or start new TLS or DTLS-SRTP procedures). Instead, they reuse the existing connection and run STUN checks πŸ’–.

Once the restart is done, if the selected pair is different from the previously selected pair, the agent SHOULD close the TCP connection for the previously selected pair 🧼.

10. Media Handling 🌸

10.1. Sending Media

When you're sending media and the selected candidate pair matches an existing TCP connection, that connection MUST be used for sending media πŸ“€.

The framing from [RFC4571] MUST be used when sending media πŸŽ€. For media streams that aren't RTP-based and don't normally use [RFC4571], the agent treats the stream as a byte stream and assumes it has its own framing of some sort, if it needs any. Then it grabs an arbitrary number of bytes from that byte stream and puts them in as the payload of an [RFC4571] frame, length included πŸ“. Next, the sender checks whether the resulting bytes would look like a STUN packet, based on the rules in Sections 6 and 8 of [RFC5389]. That means checking the most significant two bits, the magic cookie, the length, and the fingerprint πŸ”Ž. If the bytes would be read as a STUN message under those rules, the sender MUST use a different number of bytes so the length checks fail πŸ™…β€β™€οΈ. Honestly, it's super unlikely that random bytes from a byte stream would pass all of those checks. But it can happen if the application stream happens to contain a STUN message, like a file transfer of logs from a client that includes STUN messages 🀯.

If TLS or DTLS-SRTP procedures are protecting the media stream, those procedures start at the point where media is allowed to flow, as defined in the ICE specification [RFC5245] πŸ”. The TLS or DTLS-SRTP handshakes happen on top of the [RFC4571] shim, and for this spec they count as part of the media stream πŸ’œ.

10.2. Receiving Media

The framing from [RFC4571] MUST be used when receiving media πŸ“₯. For media streams that aren't RTP-based and don't normally use [RFC4571], the agent pulls the payload out of each [RFC4571] frame and figures out whether it's STUN or application-layer data, based on the procedures in ICE [RFC5245] 🧐. If the media is protected with DTLS-SRTP, the DTLS, RTP, and STUN packets get demultiplexed the way Section 5.1.2 of [RFC5764] describes πŸͺ„.

For non-STUN data, the agent tacks this onto the ongoing byte stream collected from the frames. Then it parses that byte stream as if it had come straight over the TCP connection 🧡. This is what lets ICE TCP work no matter what framing the application-layer protocol uses. Slay πŸ’….

11. Connection Management 🧚

11.1. Connections Formed during Connectivity Checks

Once ICE opens a TCP or TCP/TLS connection for connectivity checks, its life cycle depends on how it gets used πŸ”. If ICE selects that candidate pair for media, an agent SHOULD keep the connection open until:

  • the session terminates,
  • the media stream is removed, or
  • an ICE restart takes place, resulting in the selection of a different candidate pair.

In any of those cases, the agent SHOULD close the connection when that event happens πŸšͺ. This goes for both agents in the session, so usually one of them ends up closing it first 🫠.

If ICE already selected a connection, an agent MAY close it anyway πŸ€·β€β™€οΈ. As the next paragraph explains, this makes it reopen almost immediately, and until then, media can't be sent 😬. So these closures have a negative effect and are NOT RECOMMENDED 🚫. That said, there might be times when an agent really needs to close a connection for some reason.

If an agent needs to send media on the selected candidate pair and its TCP connection has closed, then:

  • If the agent's local candidate is tcp-active or tcp-so, it MUST reopen a connection to the remote candidate of the selected pair πŸ”„.
  • If the agent's local candidate is tcp-passive, the agent MUST await an incoming connection request. Until that connection is opened, it won't be able to send media πŸ₯Ί.

If the TCP connection gets established, the framing of [RFC4571] is used πŸŽ€. If the agent opened the connection and it's a full agent, it MUST send a STUN connectivity check πŸ“¨. An agent MUST be ready to receive a connectivity check over a connection it opened or accepted. (This is true in general: ICE requires that an agent be ready to receive a connectivity check at any time, even after ICE processing is finished πŸ‘€.) If a full agent gets a connectivity check after the connection is re-established, it MUST generate a triggered check over that connection in response, as long as it hasn't already sent a check πŸ’«. Once an agent has sent a check and gotten a successful response, the connection counts as Valid, and media can be sent. That includes a TLS or DTLS-SRTP session resumption or restart πŸŽ‰.

If the TCP connection can't be established, the controlling agent SHOULD restart ICE for this media stream πŸ”. This happens when one of the agents is behind a NAT with connection-dependent mapping properties [RFC5382] 🏠.

11.2. Connections Formed for Gathering Candidates

If the agent opened a connection to a STUN server, or another similar server, to gather a server reflexive candidate, the client SHOULD close that connection once ICE processing is complete 🧽. This happens whether or not ICE selected the candidate learned from that server.

If the agent opened a connection to a TURN server to gather a relayed candidate, the client MUST keep that connection open for the duration of the media session if ICE selected a relayed candidate from the TURN server πŸ“‘. Otherwise, the connection to the TURN server SHOULD be closed once ICE processing completes 🧹.

If, despite the client's efforts, a TCP connection to a TURN server fails during the media session while using a transport address that server allocated, the client SHOULD reconnect to the TURN server, get a new allocation, and restart ICE for that media stream πŸ”„. Similar measures SHOULD also apply to other types of relaying servers 🫢.

12. Security Considerations πŸ’€

⚠️ This part is seriously important, bestie. Please read it carefully. 🫣

The main threat in ICE is hijacking connections to point media streams at denial-of-service (DoS) targets or at malicious users 😱. When full implementations are used, ICE TCP prevents this by only using TCP connections that have been validated. Validation requires a STUN transaction to take place over the connection. That transaction can't complete unless both participants know a shared secret exchanged in the rendezvous protocol used with ICE, such as SIP [RFC3261] πŸ”‘. That shared secret is protected by that protocol exchange. For SIP, using SIP Secure (SIPS) [RFC3261] is RECOMMENDED πŸ”’. When you do this, an attacker can't open a connection and send media to the agent, even if they know or can guess the port the agent is listening on for incoming TCP connections πŸ›‘οΈ.

If the rendezvous protocol exchange gets compromised, an attacker can learn the shared secret. Then they might fake the connectivity check validation and open a TCP connection to the target 🚨. So using additional security mechanisms (e.g., application-layer security) that reduce these risks is RECOMMENDED πŸ’ͺ.

A STUN amplification attack is described in Section 18.5.2 of [RFC5245]. The same considerations apply to TCP, but the amplification effect is larger with TCP because a TCP connection has to be established before any checks are performed πŸ“ˆ. So an ICE agent SHOULD NOT have more than 5 outstanding TCP connection attempts with the same peer to the same IP address 🚧.

If both agents use lite mode, no connectivity checks get sent, so additional procedures (e.g., media-level security) are needed to validate the connection 😰. The lack of connectivity checks is especially problematic if one host is behind a NAT and has an address from private address space. The peer might accidentally connect to a host in a different subnet that uses the same private address space πŸŒ€. That's one of the reasons lite mode isn't appropriate for an ICE agent located behind a NAT πŸ™…β€β™€οΈ.

A more detailed analysis of the different attacks, and the various ways ICE prevents them, is in [RFC5245]. Those considerations apply to this specification too πŸ“š.

13. IANA Considerations ✨

okay so IANA made a brand new sub-registry called "ICE Transport Protocols" inside the "Interactive Connectivity Establishment (ICE)" registry πŸŽ€ It's for ICE candidate-attribute transport extensions, and it's giving organized. Future assignments go through IETF Review or IESG Approval [RFC5226]. Each entry is an extension token (defined in Section 15.1 of [RFC5245]) plus a pointer to the document that defines the extension πŸ’…

Token   Reference
-----   ---------
UDP     RFC 5245, Section 15.1
TCP     RFC 6544, Section 4.5

14. Acknowledgements

the authors gave a huge shoutout 🫢 to Tim Moore, Saikat Guha, Francois Audet, Roni Even, Simon Perreault, Alfred Heggestad, Hadriel Kaplan, Jonathan Lennox, Flemming Andreasen, Dan Wing, and Vijay Gurbani for reviewing and giving input on this document 🌸 Special thanks to Marc Petit-Huguenin for the SDP examples, which are literally iconic πŸ‘‘

15. References

15.1. Normative References

[RFC2119]   Bradner, S., "Key words for use in RFCs to Indicate
            Requirement Levels", BCP 14, RFC 2119, March 1997.

[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.

[RFC3711]   Baugher, M., McGrew, D., Naslund, M., Carrara, E., and K.
            Norrman, "The Secure Real-time Transport Protocol
            (SRTP)", RFC 3711, March 2004.

[RFC4145]   Yon, D. and G. Camarillo, "TCP-Based Media Transport in
            the Session Description Protocol (SDP)", RFC 4145,
            September 2005.

[RFC4571]   Lazzaro, J., "Framing Real-time Transport Protocol (RTP)
            and RTP Control Protocol (RTCP) Packets over Connection-
            Oriented Transport", RFC 4571, July 2006.

[RFC4572]   Lennox, J., "Connection-Oriented Media Transport over the
            Transport Layer Security (TLS) Protocol in the Session
            Description Protocol (SDP)", RFC 4572, July 2006.

[RFC5226]   Narten, T. and H. Alvestrand, "Guidelines for Writing an
            IANA Considerations Section in RFCs", BCP 26, RFC 5226,
            May 2008.

[RFC5234]   Crocker, D. and P. Overell, "Augmented BNF for Syntax
            Specifications: ABNF", STD 68, RFC 5234, January 2008.

[RFC5245]   Rosenberg, J., "Interactive Connectivity Establishment
            (ICE): A Protocol for Network Address Translator (NAT)
            Traversal for Offer/Answer Protocols", RFC 5245,
            April 2010.

[RFC5389]   Rosenberg, J., Mahy, R., Matthews, P., and D. Wing,
            "Session Traversal Utilities for NAT (STUN)", RFC 5389,
            October 2008.

[RFC5764]   McGrew, D. and E. Rescorla, "Datagram Transport Layer
            Security (DTLS) Extension to Establish Keys for the
            Secure Real-time Transport Protocol (SRTP)", RFC 5764,
            May 2010.

[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.

15.2. Informative References

[IMC05]     Guha, S. and P. Francis, "Characterization and
            Measurement of TCP Traversal through NATs and Firewalls",
            Proceedings of the 5th ACM SIGCOMM Conference on Internet
            Measurement, 2005.

[NAT-PMP]   Cheshire, S., Krochmal, M., and K. Sekar, "NAT Port
            Mapping Protocol (NAT-PMP)", Work in Progress,
            April 2008.

[PCP-BASE]  Wing, D., Cheshire, S., Boucadair, M., Penno, R., and P.
            Selkirk, "Port Control Protocol (PCP)", Work in Progress,
            March 2012.

[RFC1928]   Leech, M., Ganis, M., Lee, Y., Kuris, R., Koblas, D., and
            L. Jones, "SOCKS Protocol Version 5", RFC 1928,
            March 1996.

[RFC3089]   Kitamura, H., "A SOCKS-based IPv6/IPv4 Gateway
            Mechanism", RFC 3089, April 2001.

[RFC3103]   Borella, M., Grabelsky, D., Lo, J., and K. Taniguchi,
            "Realm Specific IP: Protocol Specification", RFC 3103,
            October 2001.

[RFC4251]   Ylonen, T. and C. Lonvick, "The Secure Shell (SSH)
            Protocol Architecture", RFC 4251, January 2006.

[RFC4380]   Huitema, C., "Teredo: Tunneling IPv6 over UDP through
            Network Address Translations (NATs)", RFC 4380,
            February 2006.

[RFC4540]   Stiemerling, M., Quittek, J., and C. Cadar, "NEC's Simple
            Middlebox Configuration (SIMCO) Protocol Version 3.0",
            RFC 4540, May 2006.

[RFC4975]   Campbell, B., Mahy, R., and C. Jennings, "The Message
            Session Relay Protocol (MSRP)", RFC 4975, September 2007.

[RFC5190]   Quittek, J., Stiemerling, M., and P. Srisuresh,
            "Definitions of Managed Objects for Middlebox
            Communication", RFC 5190, March 2008.

[RFC5382]   Guha, S., Biswas, K., Ford, B., Sivakumar, S., and P.
            Srisuresh, "NAT Behavioral Requirements for TCP",
            BCP 142, RFC 5382, October 2008.

[RFC6062]   Perreault, S. and J. Rosenberg, "Traversal Using Relays
            around NAT (TURN) Extensions for TCP Allocations",
            RFC 6062, November 2010.

[RFC6081]   Thaler, D., "Teredo Extensions", RFC 6081, January 2011.

[RFC6156]   Camarillo, G., Novo, O., and S. Perreault, "Traversal
            Using Relays around NAT (TURN) Extension for IPv6",
            RFC 6156, April 2011.

[UPnP-IGD]  Warrier, U., Iyer, P., Pennerath, F., Marynissen, G.,
            Schmitz, M., Siddiqi, W., and M. Blaszczak, "Internet
            Gateway Device (IGD) Standardized Device Control Protocol
            V 1.0", November 2001.

Appendix A. Limitations of ICE TCP

okay real talk, ICE over TCP is a little less reliable than ICE over UDP 😭 If both hosts are sitting behind a NAT, TCP has a lower chance of getting a connection through without a relay. The reason is that a lot of NATs out there today use endpoint-dependent mapping, or they can't handle the TCP handshake flow you see with a TCP simultaneous-open. Some NATs block incoming SYN packets from an address you recently sent a SYN to, or they don't process the follow-up SYN-ACK correctly. Major mood πŸ’€

[IMC05] reported that with the NATs that were around when they measured in 2005, one of the NAT traversal techniques covered here, TCP simultaneous-open, worked in roughly 45% of cases πŸ¦‹ Also, not every operating system does TCP simultaneous-open properly, so some of them can't use those candidates at all. The good news is that once more NATs follow the requirements in [RFC5382] and OS TCP stacks get fixed, simultaneous-open's success rate will probably go up ✨ It's also important to implement extra techniques with higher success ratios, like Teredo, whose success in different scenarios is shown in Figure 1 of [RFC6081] 🌈

Last thing, bestie: implementing the various techniques listed in Section 5 should boost the success probability, but a lot of them need support from the endpoints and/or from some network elements (like the NATs). Without solid experimental data on how well each technique is supported, it's honestly hard to measure how much the success probability actually goes up πŸ™ˆ

Appendix B. Implementation Considerations for BSD Sockets

this spec asks for some unusual TCP connection handling, and that's not easy to pull off with traditional BSD socket APIs 🧚

Here's the deal: ICE needs an agent to get a local TCP candidate that's bound to a local IP and port. Then it has to start a TCP connection from that same local port. That could be to the STUN server (to get server reflexive candidates), to the TURN server (to get a relayed candidate), or to the peer as part of a connectivity check. It also has to be ready to receive incoming TCP connections for passive and simultaneous-open candidates πŸ’– The catch is that a "typical" BSD socket is used for either initiating or receiving connections, not both. Allowing incoming and outgoing connections on the same local IP and port is non-obvious, so here's the pseudocode, contributed by Saikat Guha, which has been found to work on many platforms πŸ’…

for i in 0 to MAX
   sock_i = socket()
   set(sock_i, SO_REUSEADDR)
   bind(sock_i, local)

listen(sock_0)
connect(sock_1, stun)
connect(sock_2, remote_a)
connect(sock_3, remote_b)

The key thing here: before you call listen(), you have to allocate and bind the full set of sockets you'll need for outgoing connections to the local IP address and port. MAX is the maximum number of TCP connections to different destinations that might need to be set up from the same local candidate, and it can get pretty big for simultaneous-open candidates 😭 If a request forks, ICE procedures may happen with multiple peers. For each peer, you'd need connections to every passive or simultaneous-open candidate for the same component. Worst case, say 5 forked branches with five simultaneous-open candidates per peer, and that gives you MAX=25 ✨

Appendix C. SDP Examples ✨

Okay bestie, this last bit is all about examples πŸ’– Both of them use the simplified topology from Figure 8 in [RFC5245], with the same IP addresses 🌸 The examples are strictly informative, so think of them as a friendly demo and not the boss rulebook πŸŽ€

In the first example, the offer has only TCP candidates πŸ¦‹ (the lines are folded here just so they fit the page, fr fr):

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 TCP/RTP/AVP 0
b=RS:0
b=RR:0
a=rtpmap:0 PCMU/8000
a=setup:active
a=connection:new
a=candidate:1 1 TCP 2128609279 10.0.1.1 9 typ host tcptype active
a=candidate:2 1 TCP 2124414975 10.0.1.1 8998 typ host tcptype passive
a=candidate:3 1 TCP 2120220671 10.0.1.1 8999 typ host tcptype so
a=candidate:4 1 TCP 1688207359 192.0.2.3 9 typ srflx raddr 10.0.1.1
  rport 9 tcptype active
a=candidate:5 1 TCP 1684013055 192.0.2.3 45664 typ srflx raddr
  10.0.1.1 rport 8998 tcptype passive
a=candidate:6 1 TCP 1692401663 192.0.2.3 45687 typ srflx raddr
  10.0.1.1 rport 8999 tcptype so

And here's how the answer to that offer could look 🧚

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 TCP/RTP/AVP 0
b=RS:0
b=RR:0
a=setup:passive
a=connection:new
a=rtpmap:0 PCMU/8000
a=candidate:1 1 TCP 2128609279 192.0.2.1 9 typ host tcptype active
a=candidate:2 1 TCP 2124414975 192.0.2.1 3478 typ host tcptype passive
a=candidate:3 1 TCP 2120220671 192.0.2.1 3482 typ host tcptype so

Now the second example is where it gets spicy 🌢️ It mixes UDP and TCP media streams 🌈 The S-O candidates are left out because the hosts don't support TCP simultaneous-open πŸ™ˆ UDP candidates are preferred, but the preference order for candidate types stays the same. To make that happen, the TCP candidate type preferences go down by one, so the host candidates use type preference 125 and the server reflexive candidates use 99 🫢

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 TCP 2111832063 10.0.1.1 9 typ host tcptype active
a=candidate:2 1 TCP 2107637759 10.0.1.1 9012 typ host tcptype passive
a=candidate:3 1 TCP 1671430143 192.0.2.3 9 typ srflx raddr 10.0.1.1
  rport 9 tcptype active
a=candidate:4 1 TCP 1667235839 192.0.2.3 44642 typ srflx raddr
  10.0.1.1 rport 9012 tcptype passive
a=candidate:5 1 UDP 2130706431 10.0.1.1 8998 typ host
a=candidate:6 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr
  10.0.1.1 rport 8998

The matching answer could look like this πŸ’œ

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 TCP 2111832063 192.0.2.1 9 typ host tcptype active
a=candidate:2 1 TCP 2107637759 192.0.2.1 3478 typ host tcptype passive
a=candidate:3 1 UDP 2130706431 192.0.2.1 3478 typ host

Authors' Addresses πŸ’Œ

Here are the people who wrote this thing, and where you can reach them 🌷 Big thanks to all of them 🫢

Jonathan Rosenberg
jdrosen.net

EMail: jdrosen@jdrosen.net
URI:   http://www.jdrosen.net
Ari Keranen
Ericsson
Hirsalantie 11
02420 Jorvas
Finland

EMail: ari.keranen@ericsson.com
Bruce B. Lowekamp
Skype

EMail: bbl@lowekamp.net
Adam Roach
Tekelec
17210 Campbell Rd., Suite 250
Dallas, TX 75252
US

EMail: adam@nostrum.com
πŸ“¦ where this page comes from