Comprehensive Guide to NAT Traversal Solutions: Understanding STUN, ICE, and TURN Protocols

STUN supports two types of transactions: REQUEST/RESPONSE transactions and INDICATION transactions. STUN itself is not a traversal solution but a protocol used in NAT traversal solutions like ICE and TURN.

The STUN protocol helps terminals behind a NAT determine the public IP address and port allocated to them by the NAT.

In ICE, the STUN protocol is used for connectivity checks (BINDING_REQUEST/RESPONSE) and ICE keep-alive (BINDING_INDICATION). In the TURN protocol, the STUN protocol is used for the establishment of Allocations and can act as a carrier for relay data (such as sendindication and dataindication). ICE and TURN are two different uses of STUN.

Here, we will temporarily discuss the protocol itself:

 NAT traversal solutions

STUN Message Format

The STUN protocol structure consists of a 20-byte header and several attributes. The number of attributes can be zero, and they are uniformly big-endian binary sequences. The message is divided into two parts:

  • 20-byte STUN Header
  • Body carrying zero or more attributes

STUN Header

The message header consists of 20 bytes, with the following breakdown:

// STUN packet transaction id = 96 bits
#define STUN_TRANSACTION_ID_LEN (UINT16) 12

// STUN Header
typedef struct {
    UINT16 stunMessageType; // 14 bit
    UINT16 messageLength; // 16 bit
    UINT32 magicCookie; // 32 bit
    BYTE transactionId[STUN_TRANSACTION_ID_LEN]; // 96 bit
} StunHeader, *PStunHeader;
 NAT traversal solutions

Message Type

The Message Type is in host byte order and is quite important:

1. BINDING/Address Binding

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0001  |   BINDING REQUEST                                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0011  |   BINDING INDICATION                                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0101  |   BINDING RESPONSE SUCCESS                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0111  |   BINDING RESPONSE ERROR                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

2. SHARED SECRET

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0002  |   SHARED SECRET REQUEST                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0102  |   SHARED SECRET RESPONSE                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0112  |   SHARED SECRET ERROR RESPONSE                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3. ALLOCATE

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0003  |   ALLOCATE                                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0103  |   ALLOCATE SUCCESS RESPONSE                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0113  |   ALLOCATE ERROR RESPONSE                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

4. REFRESH

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0004  |   REFRESH                                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0104  |   REFRESH SUCCESS RESPONSE                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0114  |   REFRESH ERROR RESPONSE                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

5. SEND

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0006  |   SEND                                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0106  |   SEND INDICATION                                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

6. DATA

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0007  |   DATA                                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0107  |   DATA INDICATION                                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7. CREATE PERMISSION

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0008  |   CREATE PERMISSION                                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0108  |   CREATE PERMISSION SUCCESS RESPONSE                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0118  |   CREATE PERMISSION ERROR RESPONSE                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

8. CHANNEL BIND

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0009  |   CHANNEL BIND REQUEST                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0109  |   CHANNEL BIND SUCCESS RESPONSE                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0119  |   CHANNEL BIND ERROR RESPONSE                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Message Length

The length here refers to the total length of all STUN Attributes:

The following is the STUN Attributes:

Magic Cookie & Transaction ID

During transmission, the Cookie is generally considered part of the Transaction ID, so the Transaction ID is considered to be 16 bytes long. However, in [RFC 3489] or [RFC 5389], the Cookie is a fixed value 0x2112A442.

The Transaction ID is generated by the requester and must be the same in replies as in requests. The same request uses the same Transaction ID, but clients need to choose a new Transaction ID for new transactions.

STUN Attributes

The attributes are structured (STUN Attributes) following the Header, arranged one after another:

STUN Attributes consist of an Attribute Header and an Attribute Body. The Attribute Header includes the Type and Length, with a fixed length of 4 bytes. The Attribute Body length is variable, determined by the Length.

Below, we explain some important attributes:

/**
 * STUN attribute types
 */
typedef enum {
    STUN_ATTRIBUTE_TYPE_MAPPED_ADDRESS = (UINT16)0x0001,
    STUN_ATTRIBUTE_TYPE_RESPONSE_ADDRESS = (UINT16)0x0002,
    STUN_ATTRIBUTE_TYPE_CHANGE_REQUEST = (UINT16)0x0003,
    STUN_ATTRIBUTE_TYPE_SOURCE_ADDRESS = (UINT16)0x0004,
    STUN_ATTRIBUTE_TYPE_CHANGED_ADDRESS = (UINT16)0x0005,
    STUN_ATTRIBUTE_TYPE_USERNAME = (UINT16)0x0006,
    STUN_ATTRIBUTE_TYPE_PASSWORD = (UINT16)0x0007,
    STUN_ATTRIBUTE_TYPE_MESSAGE_INTEGRITY = (UINT16)0x0008,
    STUN_ATTRIBUTE_TYPE_ERROR_CODE = (UINT16)0x0009,
    STUN_ATTRIBUTE_TYPE_UNKNOWN_ATTRIBUTES = (UINT16)0x000A,
    STUN_ATTRIBUTE_TYPE_REFLECTED_FROM = (UINT16)0x000B,
    STUN_ATTRIBUTE_TYPE_XOR_MAPPED_ADDRESS = (UINT16)0x0020,
    STUN_ATTRIBUTE_TYPE_PRIORITY = (UINT16)0x0024,
    STUN_ATTRIBUTE_TYPE_USE_CANDIDATE = (UINT16)0x0025,
    STUN_ATTRIBUTE_TYPE_FINGERPRINT = (UINT16)0x8028,
    STUN_ATTRIBUTE_TYPE_ICE_CONTROLLED = (UINT16)0x8029,
    STUN_ATTRIBUTE_TYPE_ICE_CONTROLLING = (UINT16)0x802A,
    STUN_ATTRIBUTE_TYPE_CHANNEL_NUMBER = (UINT16)0x000C,
    STUN_ATTRIBUTE_TYPE_LIFETIME = (UINT16)0x000D,
    STUN_ATTRIBUTE_TYPE_XOR_PEER_ADDRESS = (UINT16)0x0012,
    STUN_ATTRIBUTE_TYPE_DATA = (UINT16)0x0013,
    STUN_ATTRIBUTE_TYPE_REALM = (UINT16)0x0014,
    STUN_ATTRIBUTE_TYPE_NONCE = (UINT16)0x0015,
    STUN_ATTRIBUTE_TYPE_XOR_RELAYED_ADDRESS = (UINT16)0x0016,
    STUN_ATTRIBUTE_TYPE_EVEN_PORT = (UINT16)0x0018,
    STUN_ATTRIBUTE_TYPE_REQUESTED_TRANSPORT = (UINT16)0x0019,
    STUN_ATTRIBUTE_TYPE_DONT_FRAGMENT = (UINT16)0x001A,
    STUN_ATTRIBUTE_TYPE_RESERVATION_TOKEN = (UINT16)0x0022,

} STUN_ATTRIBUTE_TYPE;

MAPPED-ADDRESS

Represents the “reflected address” of a NAT client. The Family indicates the protocol family, including IPv4 and IPv6. IPv4 is 0x01, and IPv6 is 0x02. Note that Port and Address are in network order and need to be converted to host order when viewed locally.

XOR-MAPPED-ADDRESS

Similar to the MAPPED-ADDRESS attribute, the difference is that the reflected address undergoes an XOR operation. The XOR operation is its own inverse operation, allowing the client to obtain the real reflected address through another XOR operation. This is done to address the issue of ALG altering addresses and ports.

USERNAME

The username used for message integrity checks identifies the username and password combination used in the integrity check.

MESSAGE-INTEGRITY

The HMAC-SHA1 value of a STUN message, with a length of 20 bytes, used for message integrity verification.

FINGERPRINT

The FINGERPRINT attribute may appear in all STUN messages. Its value is computed as the CRC-32 of the STUN message, XORed with the 32-bit value 0x5354554e (this XOR helps to handle scenarios where application packets also use CRC-32).

The 32-bit CRC defined by ITU V.42 [ITU.V42.2002] has a generator polynomial of x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1. When present, the FINGERPRINT attribute must be the last attribute in the message, and therefore appears after MESSAGE-INTEGRITY.

The FINGERPRINT attribute helps distinguish STUN packets from other protocol packets. Like MESSAGE-INTEGRITY, the CRC used in the FINGERPRINT attribute also covers the length field of the STUN message header. Therefore, this value must be correct before the CRC is computed, and the CRC attribute must be included as part of the message length.

When using the FINGERPRINT attribute in a message, the attribute is first placed in the message with a placeholder value, then the CRC is computed, and the attribute’s value is updated. If the MESSAGE-INTEGRITY attribute is also present, it must occur prior to computing the CRC with the correct message-integrity value, as the CRC also operates over the value of the MESSAGE-INTEGRITY attribute.

PRIORITY and USE-CANDIDATE

The terminal must include the PRIORITY attribute in its request to indicate its priority, calculated using a formula. If necessary, it can specify a particular candidate (i.e., the USE-CANDIDATE attribute).

UNKNOWN-ATTRIBUTES

This attribute appears only in error responses with an error code of 420.

ICE-CONTROLLING and ICE-CONTROLLED

The ICE process defines two roles: Controlling and Controlled.

Different roles impact the calculation of Candidate Pair priorities and Pair Nomination decisions. Generally, the role selection during a session is related to the session negotiation process, with the Offer party as Controlling and the Answer party as Controlled.

ICE-CONTROLLED or ICE-CONTROLLING attributes carry a “Tie breaker” field, which includes a randomly generated value by the local machine. The party receiving this Binding Request checks these two fields and, if they conflict with its current Role, compares the local Tie breaker value with the one in the message to determine the appropriate Role.

The method of determination is that the side with the larger Tie breaker value becomes the Controlling role. If it’s determined that the local side should change roles, it does so directly; if it’s determined that the remote side should change roles, a 487 error response is sent to this Binding Request, and the recipient of this error response changes roles accordingly.

ERROR-CODE

This attribute is used in “error response” messages, containing the error code represented by values between 300 and 699, along with a UTF-8 formatted reason phrase.

SOFTWARE

This attribute is used when a proxy sends a message containing a description of the version, applicable to both clients and servers. Its value includes the manufacturer and version number. This attribute has no impact on the operation of the protocol and is used solely for diagnostics and debugging purposes. The SOFTWARE attribute is variable length, using a sequence of less than 128 UTF-8 encoded characters.

ALTERNATE-SERVER

This attribute indicates a different STUN server address that the STUN client can attempt. The format of the attribute is the same as that of the MAPPED-ADDRESS.