skip to content

On a WebRTC media port shared by STUN, DTLS and SRTP, how does the receiver classify each packet, and what breaks when classification fails?

level: seniorimportance: should knowfreq 14%

answer

  1. no DTLS framing around SRTP
  2. look before you decrypt
  3. one byte decides the handler
  4. version bits, content types, zero bits
  5. second byte splits RTP from RTCP

basics

~20 s

The receiver reads the first byte: 0-3 is STUN, 20-63 DTLS, 128-191 RTP or RTCP (RFC 7983, updated by RFC 9443). With rtcp-mux the second byte then separates RTCP from RTP; misrouted or unmatched packets are dropped, so media goes silent.

solid answer

~50 s

ICE's STUN checks, the DTLS handshake and SRTP/SRTCP share one 5-tuple, and because DTLS-SRTP sends SRTP without DTLS framing, the receiver sorts them by their first byte. The ranges are disjoint by design: STUN's top two bits are zero, DTLS content types sit in 20-63 (DTLS 1.3's unified header starts `001`), and RTP version 2 sets the top bits to `10`, giving 128-191. RFC 7983 adds ZRTP at 16-19 and TURN channels at 64-79; RFC 9443 gives 80-127 and 192-255 to QUIC and treats 64-79 as TURN only from the TURN server. Within 128-191, `rtcp-mux` (RFC 5761) reads the second byte, which is why RTP payload types 64-95 MUST NOT be used. Then the SSRC, and with BUNDLE the MID, picks the stream. Errors are silent: a packet in an unknown range is dropped, and SRTP in the wrong context fails authentication and is discarded.

code

pseudocode · 14 lines
pseudocode
function classify(packet, source):
    b = packet[0]
    if b in 0..3:       return STUN
    if b in 16..19:     return ZRTP
    if b in 20..63:     return DTLS
    if b in 64..79:
        if source == responding_turn_server: return TURN_CHANNEL
        return QUIC
    if b in 128..191:
        second = packet[1]
        if rtcp_mux and second in 192..223: return SRTCP
        return SRTP
    if b in 80..127 or b in 192..255: return QUIC
    return DROP

go deeper

for a junior

Recall that a WebRTC call sends connectivity checks, the DTLS handshake and the encrypted media over the same port, and that the receiver must sort them before decrypting.

for a middle

Explain the first-byte ranges for STUN, DTLS and RTP, why those protocols' headers make the ranges disjoint, and how rtcp-mux separates RTCP by the second byte.

for a senior

Diagnose a silent or one-way call by classifying the shared port's packets: an rtcp-mux mismatch, a payload type in 64-95 or SRTP hitting the wrong context each fail without an error.

for a principal

Judge the cost of collapsing everything onto one port: fewer NAT bindings and one DTLS association, against a classifier every middlebox and endpoint must implement identically.

## Why one port carries several protocols A WebRTC call, or a SIP call through a border controller that speaks WebRTC, collapses media onto as few ports as possible so that NAT bindings and connectivity checks are needed for one flow instead of many: - **ICE** connectivity checks are **STUN** Binding requests sent on the media 5-tuple itself. - **DTLS-SRTP** (RFC 5764) runs its **DTLS** handshake on that same 5-tuple, then derives SRTP keys from it. - The resulting **SRTP** packets are sent "directly on the wire as a single datagram with no DTLS framing", so they do not look like DTLS records. - **rtcp-mux** (RFC 5761) puts RTCP on the RTP port, and **BUNDLE** (RFC 9143) puts audio, video and data on a single transport. The receiver therefore has to decide, before any decryption, which handler gets each datagram. ## The first-byte table RFC 5764 originally looked only for STUN, DTLS and RTP. RFC 7983 widened the table and RFC 9443 revised it again for QUIC. Both updates are Standards Track. | First byte | RFC 7983 | RFC 9443 (current) | |---|---|---| | 0-3 | STUN | STUN | | 4-15 | not assigned, drop | drop | | 16-19 | ZRTP | ZRTP | | 20-63 | DTLS | DTLS | | 64-79 | TURN channel | TURN channel if from the responding TURN server, else QUIC | | 80-127 | not assigned, drop | QUIC | | 128-191 | RTP or RTCP | RTP or RTCP | | 192-255 | not assigned, drop | QUIC | A value that matches no known range MUST be dropped, and an alert MAY be logged. ## Why the ranges never overlap - **STUN**: RFC 8489 requires the most significant two bits of every STUN message to be zero, so its first byte is below 64, and RFC 7983 reserves 0-3 of that space for it. - **DTLS**: a DTLS 1.2 record begins with its content type (`change_cipher_spec` 20, `alert` 21, `handshake` 22, `application_data` 23); DTLS 1.3's unified header begins with the bits `001`, which is 32-63. All fall in 20-63. - **RTP and RTCP**: both start with a 2-bit version field set to 2, binary `10`, so their first byte is 128-191 whatever the other bits are. - **ZRTP** (RFC 6189, Informational) and **TURN ChannelData** were given ranges that avoid all three. ## Second stage: RTP versus RTCP Inside 128-191 the receiver still has to tell SRTP from SRTCP. In RTP the second byte is the marker bit plus the 7-bit payload type; in RTCP it is the packet type (sender report 200, receiver report 201, `BYE` 203). RFC 5761 keeps them apart by forbidding RTP payload types 64-95 when multiplexing: an RTP packet with payload type 72 and the marker bit set would carry 200 in its second byte and be read as a sender report. Dynamic payload types SHOULD be chosen from 96-127. RFC 5761 also requires the multiplexing to happen **below** the SRTP layer: SRTP and SRTCP are produced from their own contexts and only then share the port. Without rtcp-mux, DTLS-SRTP needs two DTLS sessions, one on the RTP port pair and one on the RTCP pair (RFC 5764 section 4.2). ## Third stage: which stream With BUNDLE, every RTP stream shares the transport, so the address and port cannot identify the media section. RFC 9143 requires the MID identification tag, carried in an RTP header extension or an RTCP SDES item, alongside the SSRC; legacy peers fall back to unique payload types. A BUNDLE group has exactly one DTLS association and MUST use RTP/RTCP multiplexing. ## How it fails on a real call 1. **One side multiplexes, the other does not.** If an offerer gets no `a=rtcp-mux` in the answer it MUST keep RTCP on a separate port. A border controller that forwards the attribute without honouring it leaves RTCP on the wrong port: call statistics vanish and the sender gets no feedback. 2. **A payload type in 64-95.** Some RTP packets are classified as RTCP, handed to the SRTCP context, fail authentication and are discarded, producing choppy or one-way audio. 3. **Wrong context.** SRTP that reaches a context with the wrong key fails its tag check and is silently discarded; the call shows "connected" with no sound. 4. **Unmatched first byte.** A peer or middlebox emitting a protocol the receiver did not expect has its packets dropped. None of these raises an error to the user, which is why one-way or silent calls are debugged by capturing the shared port and classifying packets by first byte.

  • Why can a receiver read SRTP's first two bytes before it has decrypted anything?
    SRTP encrypts only the RTP payload; the RTP header, including the version bits, marker bit, payload type and SSRC, travels in clear and is only authenticated. SRTCP likewise leaves its first eight octets unencrypted. So the version bits for the first-byte check, the type byte for the RTP/RTCP split and the SSRC for picking a context are all readable before any key is used.
  • What does RFC 9443 change for a first byte of 64-79, and why?
    RFC 7983 gave 64-79 to TURN ChannelData. RFC 9443 also assigns parts of the space to QUIC, whose packets can begin with values in that range, so it treats 64-79 as a TURN channel only when the packet's source address and port belong to a responding TURN server, and as QUIC otherwise.

A mailroom where each sender's reference codes come from a different number range: the clerk routes every envelope by its first digit without opening it, and an envelope whose code fits no range goes in the bin.

saying these in an interview costs you the question

  • SRTP packets are carried inside DTLS records, so the receiver decrypts DTLS first.
  • STUN, DTLS and SRTP each need their own UDP port on a WebRTC call.
  • Any first byte from 128 to 191 can only be an RTP media packet.
  • Dynamic RTP payload types 64 to 95 are fine under rtcp-mux.
  • A misclassified packet makes the stack raise an error the user will see.