>
Home5G NRPDCP โ€” Packet Data ConvergenceROHC
๐Ÿ›ก๏ธ PDCP โ€” Packet Data ConvergenceIntermediate

ROHC Header Compression in 5G NR

Robust Header Compression profiles and why they matter for VoNR and small packets.

📚 3GPP-basedTS 38.323

A voice-over-NR packet can carry as little as ~30 bytes of actual speech underneath a stack of IP/UDP/RTP headers that is routinely 40–60 bytes — more overhead than payload. Robust Header Compression (ROHC) collapses those headers to a handful of bytes so the air interface stops spending precious radio resources on fields that barely change from one packet to the next. It is a PDCP transmit-side function, specified in TS 38.323 on top of the IETF ROHC framework (RFC 5795, formerly RFC 3095), and it is switched on per radio bearer.

Introduction

ROHC (RObust Header Compression) is a PDCP user-plane function: it lives inside the PDCP entity of a DRB and runs on the transmit side (the compressor) and receive side (the decompressor). NR does not define the compression algorithm itself — that belongs to the IETF (RFC 5795 and the per-stack profile RFCs). TS 38.323 defines only how PDCP invokes ROHC, which profiles it may use, and how the compressed output is carried as a PDCP SDU.

It is configured when the network sets up or reconfigures a DRB, via the headerCompression choice inside pdcp-Config, and it runs continuously for the life of that bearer. Because ROHC context is state held per PDCP entity, it is also touched at every handover and PDCP re-establishment — normally reset, optionally preserved.

It matters most for small, frequent, real-time flows — above all VoNR — where the header genuinely dwarfs the payload. On such flows ROHC roughly doubles the useful payload ratio, which translates almost directly into how many simultaneous voice users a cell can carry, and it saves UE battery by putting fewer bits on the air.

Why Header Compression Exists

💡

In plain words: imagine mailing a postcard every 20 milliseconds to the same friend. Writing out their full home address, your return address and the date on every single card is a waste — after the first card they already know all of it. ROHC is the agreement "I'll write the full address once; after that I'll just scribble the card number and you fill in the rest from memory." The address is the static IP/UDP/RTP header; the card number is the little that actually changes.

The problem ROHC solves is structural, not incidental. A real-time media flow such as VoNR emits a small speech frame every 20 ms, but each frame is wrapped in three protocol headers before it hits the radio stack: RTP (12 bytes) inside UDP (8 bytes) inside IP (20 bytes for IPv4, 40 bytes for IPv6). That is 40 bytes over IPv4 and 60 bytes over IPv6 of pure header on top of a payload that may itself be only 20–40 bytes. On the air interface, where every resource element is scarce and shared, sending 60 bytes of header to protect 30 bytes of speech is a direct waste of capacity and battery.

What

A header-compression scheme that runs in PDCP on the transmit side (the compressor) and the receive side (the decompressor). It replaces the full IP/UDP/RTP header with a small compressed header while the two ends share a synchronised context.

Why

For small, frequent packets — VoNR speech, gaming, some IoT flows — the header dwarfs the payload. Squeezing it multiplies effective spectral efficiency and lets far more voice users share one cell.

How

The compressor learns which header fields are constant (addresses, ports) and which change predictably (RTP sequence number, timestamp), then transmits only the unpredictable remainder. The decompressor rebuilds the full header from its stored context plus that remainder.

The reason this works is redundancy. Across the thousands of packets in a single call, the source and destination IP addresses never change, the UDP ports never change, and the IP version, protocol byte and RTP SSRC are all fixed. After the first packet those fields are pure repetition — the receiver already holds them. The fields that do change usually change by the same amount every packet: a speech frame every 20 ms means the RTP timestamp jumps by a constant sample count and the sequence number increments by one. If both ends agree on the starting value and the step, the compressor need not send these fields at all; the decompressor extrapolates them. Only when something breaks the pattern — a lost packet, a silence-suppression gap, a marker bit — does the compressor transmit a correction.

๐ŸŽฏ

The headline gain: once the context is established, a 40–60 byte header collapses to roughly 1–3 bytes. For VoNR that roughly doubles the useful payload ratio, which translates almost directly into how many simultaneous voice users a cell can carry. ROHC runs before ciphering in the PDCP transmit chain, and decompression runs after deciphering on receive.

Profiles — One Compressor per Protocol Stack

ROHC does not squeeze bytes blindly; it understands specific protocol stacks. Each stack has a profile, identified by a 16-bit profile number, and both ends must agree on which profiles are permitted for a bearer. A profile encodes the field-classification rules and the compressed-header formats for exactly that combination of protocols. The set of profiles PDCP is allowed to use is signalled to the UE inside pdcp-Config at bearer setup.

ProfileCompressesReferenceTypical use
0x0000Uncompressed (no compression)RFC 5795Fallback / passthrough for traffic ROHC cannot help.
0x0001RTP/UDP/IPRFC 3095, RFC 4815The big VoNR win — voice and other RTP media.
0x0002UDP/IPRFC 3095, RFC 4815Non-RTP UDP flows (signalling, DNS-like traffic).
0x0003ESP/IPRFC 3095, RFC 4815IPsec ESP-tunnelled traffic.
0x0004IP (IP-only)RFC 3843, RFC 4815Any IP flow with no useful transport-layer regularity.
0x0006TCP/IPRFC 6846TCP flows (ROHC-TCP), including window and option handling.
0x01010x0104RTP / UDP / ESP / IP (ROHCv2)RFC 5225Enhanced "v2" profiles with cleaner robustness rules.

The pairing is deliberate: 0x0001 is where the value lives for operators, because it targets RTP media, and RTP is where the constant-stride timestamp and sequence number make near-total compression possible. The ESP profile (0x0003) handles the case where the payload is an IPsec-encrypted tunnel, and the TCP profile (0x0006) adds machinery for TCP's sliding window and options that the simpler UDP-family profiles do not need. The 0x01010x0104 range is the ROHCv2 revision of the same four stacks, redesigned so that recovery from context damage is simpler and more uniform. Every profile always coexists with 0x0000, which is the guaranteed escape hatch when a flow simply is not compressible.

๐Ÿ“—

Spec note: the profiles themselves are defined by the IETF; TS 38.323 defines only how PDCP invokes ROHC, enumerates the supported profiles, and carries the compressed packets as PDCP SDUs. PDCP does not redefine the compression algorithm.

How Fields Are Classified

Before it can compress anything, ROHC sorts every header field into a class, because the class decides whether the field is sent once, sent as a delta, or never sent at all. The classification is what turns "60 bytes of header" into "a few bits of genuinely new information."

ClassBehaviourExamples
StaticFixed for the life of the flow. Sent once in the IR packet, then never again.IP version, source/destination address, UDP ports, RTP SSRC, protocol number.
Changing (dynamic)Change during the flow, but usually predictably. Carried by efficient encodings rather than full values.RTP sequence number, RTP timestamp, IP identification, TTL.
InferredNever sent — the decompressor derives them from other fields or lower layers.IP total length, UDP length, header checksums.

The changing class is where the cleverness sits. ROHC further splits it by behaviour — some fields are effectively static, some change rarely, some alternate, and a few are genuinely irregular and must be sent verbatim each time (an occasional RTP marker bit, for example). The workhorse encoding is window-based least-significant-bits (W-LSB): rather than send a whole sequence number, the compressor sends only the low-order bits needed to disambiguate it within a window of recently acknowledged values. The RTP timestamp gets an additional trick, scaled timestamp encoding, which divides out the constant per-packet stride so only the multiplier — often unchanged — needs representing. Between static suppression, inference, and W-LSB deltas, a steady-state packet ends up carrying little more than a truncated sequence number.

Compressor States — IR, FO, SO

The compressor cannot begin sending tiny headers immediately, because the decompressor starts with no context. So it works through three states, sending progressively less as its confidence grows that the far end is synchronised.

StateNameWhat is sentMoves up whenDrops down when
IRInitialization & RefreshFull static context plus dynamic fields, so the decompressor can build a context from scratch. Largest headers.Static context has been established (confirmed or optimistically repeated).— (lowest state)
FOFirst OrderChanges to the dynamic fields once the static part is known — new strides, irregularities, pattern updates.Dynamic pattern is stable and confirmed → SO.A static field changes → back to IR.
SOSecond OrderSteady state: near-minimal headers, essentially just the W-LSB sequence number from which everything else is extrapolated.— (highest state)A dynamic field breaks the pattern → FO; a static field changes → IR.

The compressor climbs upward — IR to FO to SO — as it becomes confident the decompressor's context matches its own. It gains that confidence in one of two ways: through explicit feedback in the bidirectional modes, or optimistically in the unidirectional mode, by repeating the same information several times on the assumption that at least one copy arrives. When something changes that the decompressor may have missed, the compressor drops back down — to FO if only a dynamic field jumped, or all the way to IR if a static field changed. Independently of all this, it periodically emits an IR packet as a refresh, so a decompressor that quietly lost sync can recover without any signalling.

Compressor state machine flow start IR full static + dynamic FO dynamic field updates SO sequence number only confidence up confidence up static change dynamic change static field change or periodic IR refresh → back to IR Upward = context confirmed (feedback or optimistic repetition). Downward = context may be stale.
Figure 1. The ROHC compressor climbs IR→FO→SO as it gains confidence the decompressor is in sync, and drops back to FO or IR when fields change or a refresh is due.

Decompressor States — NC, SC, FC

The decompressor runs its own, mirror-image state machine with three states, and the two machines are not the same: the compressor's states describe what it sends, while the decompressor's describe what it can currently rebuild.

StateNameCan decompressMeaning
NCNo ContextOnly IR packetsStart of a flow, or after total loss of context. Nothing can be reconstructed until a full IR arrives and passes its CRC.
SCStatic ContextIR and FO packetsThe static part is known but the dynamic state is uncertain, so minimal (SO) packets cannot yet be trusted.
FCFull ContextAll packets, including SOFull, validated context. The normal steady-state condition where the tiniest headers are accepted.

A fresh decompressor starts in NC and jumps straight to FC the first time it successfully decompresses an IR packet whose CRC verifies. It stays in FC as long as headers keep passing their CRC checks. If they start failing — because the context has drifted — it does not collapse all the way at once: it degrades from FC to SC, attempting context repair using the static part it still trusts, and only falls to NC if repair also fails. Every compressed header carries a small CRC computed over the original uncompressed header precisely so the decompressor can tell whether its reconstruction was correct before passing the packet up the stack.

โš ๏ธ

Common confusion: "robust" in ROHC does not mean it never loses sync. It means it recovers gracefully and bounds how far one lost packet can propagate damage — through CRC-verified reconstruction, W-LSB window encoding, staged FCSCNC degradation, and periodic IR refresh.

Operating Modes — U, O and R

The same state machines behave differently depending on whether a feedback channel is available and how aggressively it is used. ROHC defines three operating modes, and a session can transition between them as conditions change. Compression always begins in U-mode and may be moved to O-mode or R-mode by feedback.

ModeFeedbackBehaviourTrade-off
U-modeNoneUnidirectional. State changes are driven by timeouts and the optimistic approach — repeat updates N times — plus periodic IR refreshes.Works with no return path; least efficient and slowest to recover.
O-modeOccasionalBidirectional Optimistic. Feedback is used sparingly — NACKs for recovery and ACKs for significant context changes.The common working point: strong compression with cheap error recovery.
R-modeIntensiveBidirectional Reliable. Every context-changing update is acknowledged before the compressor advances, using a secure-reference principle.Maximum robustness against loss; highest feedback overhead.

The distinction is really about how the compressor earns confidence to climb its state machine. In U-mode it can only guess — it sends an update several times and assumes at least one got through — which is why it leans on periodic refresh to self-heal. In O-mode the decompressor sends a short feedback element (piggybacked on reverse-direction packets or sent standalone) carrying an ACK when context is good or a NACK/STATIC-NACK when it needs the compressor to refresh dynamic or static state. In R-mode that feedback becomes mandatory for advancement, so the two ends are tightly locked and a single loss can never silently desynchronise them — at the cost of the extra reverse traffic. Feedback also carries the mode-transition requests themselves, so the network can migrate a flow from U to O to R without any RRC involvement.

CID, maxCID and Configuration in PDCP

A single PDCP entity can compress several flows at once — imagine voice plus a signalling flow on the same bearer. ROHC keeps them apart with a Context Identifier, the CID, prefixed to each compressed packet so the decompressor knows which context to apply. The highest usable value is maxCID, and it is configured, because it directly sizes how many parallel contexts each end must maintain (contexts run 0 through maxCID).

Small vs large CID

Small-CID space covers 0–15 and is carried in a compact one-octet form (often a zero-cost "add-CID" nibble). Large-CID space covers 0–16383 using a self-describing variable-length encoding, for when many flows share one entity.

Why it is configured

maxCID bounds memory and complexity at both ends. Small-CID with a low maxCID is the norm for a handful of flows; large-CID is reserved for high-multiplexing cases.

How it is signalled

maxCID is one of the ROHC parameters inside headerCompression in pdcp-Config; whether the small- or large-CID format applies follows from the configured value.

ROHC is a per-bearer decision. When the network sets up or reconfigures a DRB, the RRC pdcp-Config for that bearer carries the header-compression choice: headerCompression is either notUsed, or rohc with a list of permitted profiles, the maxCID, and the drb-ContinueROHC flag. A DRB carrying VoNR would enable profile 0x0001; a DRB carrying bulk TCP data would typically leave header compression off, because large payloads gain little and the context overhead is not worth it. QoS flows land on a DRB through the SDAP mapping, so the voice 5QI flow inherits ROHC because its DRB was configured for it.

๐ŸŽฏ

Continuity on mobility: ROHC context is tied to the PDCP entity, so a handover or PDCP re-establishment normally resets it — the first packets after mobility carry full IR headers again while the target's decompressor rebuilds context from scratch. The drb-ContinueROHC flag is the exception: when set, PDCP keeps the existing ROHC context across re-establishment (used for lossless intra-node cases) instead of reinitialising it.

NR adds a second, simpler compression scheme alongside ROHC: Ethernet Header Compression (EHC), introduced for the Ethernet PDU session type used in TSN and industrial deployments. Where ROHC targets IP/UDP/RTP, EHC compresses the Ethernet MAC header — destination and source MAC addresses, EtherType and any VLAN tags — which are static for the life of a flow. The full header is sent once to establish a context identified by an EHC context ID, and every packet thereafter carries only that short ID. It is configured independently of ROHC by ehc-DL and ehc-UL inside pdcp-Config, and the two can be enabled together on the same bearer.

⚠ Common pitfalls / gotchas

  • ROHC does not help bulk data. Enabling profile 0x0006 (TCP) on a high-throughput file-transfer DRB adds context overhead and CPU for little gain, because the header is a tiny fraction of a large payload. Compression pays off on small, frequent packets, not big ones.
  • Context loss at handover. Unless drb-ContinueROHC is set, mobility resets the context, so the first post-handover packets fall back to full IR headers — a brief throughput/overhead spike that is normal, not a fault.
  • Undersized maxCID. If more simultaneous flows land on the bearer than maxCID+1 contexts allow, some flows cannot be compressed and pass through as profile 0x0000, quietly losing the gain.
  • U-mode on a lossy link. With no feedback path, recovery relies solely on periodic IR refresh, so a burst of loss can leave the decompressor mis-reconstructing until the next refresh. O-/R-mode recover far faster where a return channel exists.
🔀

LTE ↔ NR: ROHC is essentially the same PDCP function in both RATs — LTE PDCP (TS 36.323) already carried the identical IETF profiles and the IR/FO/SO and NC/SC/FC state machines, so the algorithm is unchanged. What NR adds is EHC (Ethernet Header Compression) for the Ethernet PDU session type, which has no LTE equivalent, and the drb-ContinueROHC continuity flag is carried in NR's pdcp-Config. In practice ROHC knowledge transfers directly from LTE to NR; only the surrounding configuration IEs and the EHC companion scheme are new.

Summary

ROHC is a PDCP transmit-side, per-DRB function that collapses the redundant IP/UDP/RTP header of small, frequent flows — above all VoNR — from 40–60 bytes to roughly 1–3 once a shared context is built. It works by classifying every header field as static (sent once), changing (sent as W-LSB or scaled-timestamp deltas) or inferred (never sent), so a steady-state packet carries little more than a truncated sequence number. It always runs before ciphering.

Each protocol stack has a numbered profile (0x0001 RTP/UDP/IP is the VoNR workhorse; 0x0000 is the always-present passthrough). The compressor climbs IRFOSO as confidence grows and drops back when fields change or a refresh is due; the decompressor mirrors this with NCSCFC, using per-header CRCs and staged degradation to stay "robust." The three operating modes (U/O/R) trade feedback overhead for recovery speed. Everything is configured in headerCompression inside pdcp-Config — permitted profiles, maxCID, and drb-ContinueROHC — with NR's EHC available alongside for Ethernet flows.

Quick Q&A

Q&A Quick Q&A

Q. Why is ROHC such a big deal for VoNR specifically?

A. A VoNR speech frame is tiny (tens of bytes) but the IP/UDP/RTP header is 40–60 bytes, so the header can exceed the payload. Compressing it to 1–3 bytes roughly doubles the useful payload ratio, which directly increases how many voice users a cell can carry.

Q&A Quick Q&A

Q. Walk me through the compressor states.

A. IR sends the full static context plus dynamic fields so the decompressor can build context. FO sends updates to the changing fields once the static part is known. SO is steady state with near-minimal headers, essentially just a W-LSB sequence number. The compressor climbs IR→FO→SO as confidence grows and drops back to FO (dynamic change) or IR (static change / periodic refresh).

Q&A Quick Q&A

Q. How do the decompressor states relate to the compressor states?

A. They are separate machines. The decompressor has NC (no context, only IR decodable), SC (static known, FO/IR decodable), and FC (full context, everything including SO). It jumps NC→FC on a CRC-valid IR, and degrades FC→SC→NC as reconstructions start failing.

Q&A Quick Q&A

Q. What are U-mode, O-mode and R-mode?

A. Operating modes differing in feedback use. U-mode has none and relies on optimistic repetition and periodic refresh. O-mode uses feedback occasionally (ACK for major updates, NACK for recovery). R-mode acknowledges every context change for maximum robustness. Sessions start in U-mode and can transition upward via feedback.

Q&A Quick Q&A

Q. Where is ROHC configured, and what is maxCID?

A. Per DRB, via headerCompression in pdcp-Config (RRC), listing the permitted profiles (e.g. 0x0001 for RTP/UDP/IP), the maxCID, and drb-ContinueROHC. CID identifies which flow a compressed packet belongs to; maxCID is the highest context number, sizing how many parallel contexts each end maintains — small-CID covers 0–15, large-CID up to 16383.

Q&A Quick Q&A

Q. What does NR's EHC add on top of ROHC?

A. Ethernet Header Compression compresses the static Ethernet MAC header (MAC addresses, EtherType, VLAN tags) for Ethernet PDU sessions. The full header is sent once to build a context, then only a short context ID is carried. It is configured separately via ehc-DL/ehc-UL and can run alongside ROHC.

Where this sits in PDCP

ROHC is one of the transmit-side functions of the PDCP entity, applied before ciphering and configured alongside the rest of the bearer's PDCP behaviour. To see the full processing chain and how voice flows reach a ROHC-enabled DRB in the first place, follow these:

PDCP OverviewQoS Flow & DRB MappingSDAP & 5QI