ROHC is a kind of algorithm to compress the header of various IP packets. In case of IPv4, the size of uncompressed IP/UDP/RTP header is 40 bytes and in case of IPv6, the size of uncompressed IP/UDP/RTP header is 60 bytes.
Only 40 bytes and 60 bytes ? It sounds pretty small. Why do these matters ?
If it is ordinary packet application like file transfer or browsing, it would not be a big issue since the size of data being transferred would be very huge comparing to the size of header. So the overhead created by IP header would not be a big issue. But in some applications (e.g, VoIP, short message, gaming etc) the size of data being transferred tend to be small and they generate very frequent transactions, in this case overhead created by IP header gets very large. In this case, it would be huge benefit if we can come up with any method to reduce the size of IP header and ROHC is one of these method defined by RFC 3095. Ideal compression rate of ROHC is to reduce the size of header (40 or 60 bytes in original size) to only 1 or 2 bytes.
Let's put numbers on the 40 and 60 bytes. An IPv4 header is 20 bytes, a UDP header is 8 bytes and an RTP header is 12 bytes, so a voice packet over IPv4 carries 40 bytes of headers. With IPv6 the IP header grows to 40 bytes, and the total becomes 60 bytes. In LTE, ROHC runs in PDCP, so the header is compressed before the packet goes down to RLC and MAC.
- Overall Logic of Header Compression
- Component State of ROHC Statemachine - Compressor
- Component State of ROHC Statemachine - Decompressor
- Component State of ROHC Statemachine - Mode of Operation
- Component State of ROHC Statemachine - Combined
- ROHC Profiles
- ROHC Modes
- ROHC in LTE
- Possible application : VoLTE
- UE Capability Information for ROHC
- RRC Connection Reconfiguration for ROHC
- An Example : ROHC Compression - VoLTE
- An Example : ROHC UnCompression - VoLTE
- Reference
Overall Logic of Header Compression
Header compression works only because most header fields repeat from one packet to the next in the same flow. So the compressor and the decompressor first agree on those fields. After that, the compressor sends only what has changed.
Basic idea of this compression method is pretty simple which can be described as follows.
i) At the start of a session (the initiation state), the transmitter and reciever sends the full size header without any compression.
ii) From step i), both transmitter and reciever extract all the information from the header and store them.
iii) After the initial transaction, the transmitter sends only those information that is different from the header information exchanged at the initial transaction. (Since a lot of information in the header would not change during the whole session, the size of changing part would become very small. So transmitting only the changing part would create the effect like data compression).
iv) Now perform real compression for the data still remaining after step iii).
For example, let's take a look at IP/UDP header. It would look as follows.
Out of all these information included in the header, which part do you think would not change during the session ? My guess is as follows.
i) Source IP Address
ii) Destination IP Address
iii) Version
iv) IHL
v) Type of Service
vi) Source Port
vii) Destination Port
Simply removing these information would decrease the size of header in very large degree. Those engineers who never get satisfied wanted to go even further and they came up with a lot of additional ideas which can reduce the size of the changing part (e.g, Checksum etc) and the collection of all of these ideas were packaged into a single specification called ROHC.
Let's also look at the fields that do change. In the IPv4 header drawn above, Total Length, Identification and Header Checksum can differ from packet to packet, and so can Length and Checksum in the UDP header. But most of them can be worked out at the receiver. The two length fields follow from the size of the packet that the lower layer delivers. The IPv4 Header Checksum can be recomputed from the rebuilt header. For a voice flow, an RTP header also sits after the UDP header. Its sequence number and timestamp change in every packet, but they usually grow by a fixed step, so the compressor sends only a few bits of the sequence number.
Static fields are sent once : the addresses, the ports and the version fields are sent in full at the start, and then they are left out of later packets.Some changing fields are inferred : the length fields and the IPv4 header checksum are rebuilt by the decompressor rather than sent.A non-zero UDP checksum is still sent : ROHC carries it unchanged. The VoLTE examples at the end of this page show it inside the ROHC header.The saving is largest for small packets : a 40 byte header on a voice frame of about 30 bytes is more than half of the packet.
Component State of ROHC Statemachine - Compressor
The compressor does not jump straight to its smallest header. It moves up through three states, and it moves down again when it has reason to doubt that the decompressor still holds the right context.
ROHC has three main states as illustrated below. Try to correlate these states with the overall algorithm description that I mentioned above.
IR, Initialization and Refresh : the compressor has just been created or reset, and it sends full packet headers.FO, First Order : the static fields, such as the IP addresses and the port numbers, are stored on both sides. The compressor sends the differences in the dynamic fields.SO, Second Order : the compressor suppresses the dynamic fields, such as the RTP sequence number. It sends only a logical sequence number and a partial checksum, and the decompressor predicts the next header and checks it with that checksum.The arrows : the compressor moves one step at a time between IR and FO, and between FO and SO, in both directions. The two outer lines on the left also connect IR and SO directly, in both directions.
Now match these states to the four steps in the section above. Step i) is the IR state. Steps ii) and iii) are the FO state, where only the differences are sent. Step iv) is the SO state, where the remaining changes are squeezed into a few bits.
The compressor moves up only when it is confident : it needs to believe that the decompressor has the context before it sends a smaller header.The compressor moves down to repair : a return to FO or IR resends the dynamic or the static fields, so the decompressor can recover.
Component State of ROHC Statemachine - Decompressor
The decompressor has its own state machine. It can rebuild a header only when it holds the right context, so its states describe how much of that context it has. The drawing below has three states, No Context, Static Context and Full Context, and seven numbered transitions.

(1) Context not established : in No Context, the decompressor stays where it is until a packet sets up the context.(2) Success : a packet that carries the whole context, such as an IR packet, takes the decompressor from No Context straight to Full Context.(3) Successful Decompression : in Full Context, every packet that decompresses correctly keeps the decompressor there.(4) Repeated Failure to decompress : after repeated failures in Full Context, the decompressor moves down to Static Context. It still trusts the static part of the context, but not the dynamic part.(5) Repeated Failure to decompress : repeated failures in Static Context take the decompressor down to No Context.(6) No Dynamic context : in Static Context, the decompressor waits for a packet that updates the dynamic part.(7) Success : once such a packet decompresses correctly, the decompressor returns to Full Context.
Compare this drawing with the compressor above. The decompressor falls back one step at a time, from Full Context to Static Context and only then to No Context. So a short burst of errors costs only the dynamic part of the context, and the compressor can repair it from FO rather than from IR.
Component State of ROHC Statemachine - Mode of Operation
The states above say how small the header is. The mode says how the two ends use the feedback channel, and that decides which events move the compressor between its states. ROHC compression always starts in U mode.
The drawing below shows the three modes, U, O and R, and four numbered transitions between them.

(1) : U mode to O mode.(4) : U mode to R mode.(2) and (3) : O mode to R mode, and back from R mode to O mode.No arrow returns to U mode : in this drawing, U mode is only the starting point.
Why would the decompressor ask for a change? In U mode the compressor has no feedback, so it must go back to IR or FO from time to time just in case the context was damaged. Those refresh packets cost bandwidth even when nothing went wrong. Once a feedback channel exists, O mode removes most of that cost, because the compressor repairs the context only when the decompressor asks for it. R mode goes one step further and trades more feedback for stronger protection of the context. A transition between O mode and R mode is a handshake. The decompressor asks for the new mode, the compressor answers with packets that carry the new mode, and the decompressor acknowledges them.
A move to O mode or R mode needs a feedback channel, because the decompressor asks for the new mode in its feedback. On an LTE DRB this channel always exists, since PDCP carries ROHC feedback in the opposite direction of the same bearer.
The decompressor chooses the mode : it asks for O mode or R mode in its feedback, and the compressor follows.U mode pays for periodic refreshes : O mode and R mode replace them with repairs that the feedback requests.
Component State of ROHC Statemachine - Combined
Now let's put the two pictures together. Each mode runs the same three compressor states, IR, FO and SO. But the event that moves the compressor between those states depends on the mode.
In the drawing below, each blue box is a mode and each group of green circles is the compressor state machine inside that mode. The green arrows take the compressor up to a smaller header, and the pink arrows take it back down.

U mode : the compressor moves up by the optimistic approach, after it has sent enough packets to be confident. It moves down to IR on a Timeout, and from SO to FO on a Timeout or an Update.O mode : the compressor moves up by the optimistic approach or on an Ack. A Static Nack sends it back to IR, and a Nack or an Update sends it from SO back to FO.R mode : the compressor moves up only on an Ack. Static Nack and Nack/Update take it down in the same way as in O mode.The red loop at SO : in O mode and R mode, an Ack keeps the compressor in SO.Feedback decides the direction : an Ack moves the compressor up, and a Nack or a Static Nack moves it down.U mode relies on timeouts : without feedback, the compressor refreshes the context by going back to IR or FO from time to time.R mode waits for an Ack before every move up : it sends larger headers for longer, but it gives the context the most protection.
ROHC Profiles
A profile tells ROHC which protocol stack it is compressing. The compressor and the decompressor must use the same profile for a flow. So in LTE, the network tells the UE which profiles are allowed on each radio bearer.
There are four different ROHC Profiles defined in RFC 3095 as follows.
Profile 0 (ROHC Uncompressed) : Carries packets, which cannot be compressed by any of the following profiles, without compression
Profile 1 (ROHC RTP) : Compresses packets with IP/UDP/RTP protocol headers
Profile 2 (ROHC UDP) : Compresses packets with IP/UDP protocol headers
Profile 3 (ROHC ESP) : Compresses packets with IP/ESP protocol headers
In 3GPP 36.323 (Table 5.5.1.1: Supported header compression protocols and profiles) , Following profiles are defined.
|
Profile Identifier |
Usage |
Reference |
|
0x0000 |
No compression |
RFC 5795 |
|
0x0001 |
RTP/UDP/IP |
RFC 3095, RFC 4815 |
|
0x0002 |
UDP/IP |
RFC 3095, RFC 4815 |
|
0x0003 |
ESP/IP |
RFC 3095, RFC 4815 |
|
0x0004 |
IP |
RFC 3843, RFC 4815 |
|
0x0006 |
TCP/IP |
RFC 6846 |
|
0x0101 |
RTP/UDP/IP |
RFC 5225 |
|
0x0102 |
UDP/IP |
RFC 5225 |
|
0x0103 |
ESP/IP |
RFC 5225 |
|
0x0104 |
IP |
RFC 5225 |
Let's read the table in two groups. Profiles 0x0001 to 0x0004 and 0x0006 are ROHC version 1 profiles. Profiles 0x0101 to 0x0104 are the ROHCv2 profiles of RFC 5225 for the same stacks, so each pair shares the same low 8 bits. For example, 0x0001 and 0x0101 both compress RTP/UDP/IP. 36.331 says that if both profiles of such a pair are enabled, only the higher value applies, which is the ROHCv2 profile. It also says that profile 0x0000 is always supported once ROHC is configured, so 0x0000 has no flag in the RRC message.
The table follows 36.323 v19.0.0, where it is now titled Supported ROHC protocols and profiles. Two references have changed since this page was first written. Profile 0x0000 now points to RFC 5795, which replaced RFC 4995. Profile 0x0006 now points to RFC 6846, which replaced RFC 4996.
Profile 0x0000 is always available : it carries the packets that no configured profile can compress.0x01xx profiles are ROHCv2 : when both versions of a pair are enabled, the ROHCv2 profile is used.VoLTE voice uses 0x0001 : RTP/UDP/IP is the profile that compresses the voice packets. The RRC capture later on this page enables 0x0001 and 0x0002.
ROHC Modes
The mode decides how much the compressor relies on feedback from the decompressor. That choice matters on a radio link, because feedback costs uplink resources, while a lost context costs voice packets.
The ROHC scheme has three modes of operation, called Unidirectional, Bidirectional Optimistic, and Bidirectional Reliable mode.
Which mode would be the best one in a certain situation depends on the characteristics of the environment of the compression protocol, such as feedback abilities,error probabilities and distributions, effects of header size variation, etc. All ROHC implementations MUST implement and support all three modes of operation.
For the details, I recommed the RFC 3095 or a good white paper at http://www.effnet.com/sites/effnet/pdf/uk/Whitepaper_Robust_Header_Compression.pdf
Here is a short comparison. In U mode, packets flow only from the compressor to the decompressor, and the compressor refreshes the context from time to time. In O mode, the decompressor sends feedback mainly to request a repair, so the feedback traffic stays low. In R mode, the compressor moves to a smaller header only after an Ack. The feedback traffic is higher, but the context is best protected against loss.
In LTE, ROHC feedback travels in PDCP as an interspersed ROHC feedback packet. PDCP sends it in a PDCP Control PDU, without a PDCP SN and without ciphering. 36.323 also says that feedback received on one ROHC channel always refers to the ROHC channel in the opposite direction of the same PDCP entity.
RRC does not configure the mode : PDCP-Config carries no mode field, so the two ROHC ends choose the mode at run time.O mode and R mode need feedback : in LTE that feedback is a PDCP Control PDU on the same bearer, in the opposite direction.
ROHC in LTE
So far the page has described ROHC as the RFCs define it. In LTE, ROHC runs in the PDCP layer of a DRB, and RRC decides whether it is used. The UE first reports which profiles it supports, and the network then enables some of them for each DRB. The subsections below start with the reason VoLTE needs ROHC, follow that signalling order, and end with one compressed and one decompressed VoLTE packet.
Possible application : VoLTE
One of the applications for which people are most actively talking about ROHC would be VoLTE since there can be a lot of small data chunck carried by huge IP packets. For example, there can be a cases where only around 30 bytes of voice data (coded data) carried with around 60 bytes of header. In this case, only header parts takes more resources than the real data itself. So this kind of packet can be a good candiate for ROHC.
As you know, in VoLTE, you would see two kinds of packets. One of them is SIP Signaling packet and the other one would be voice traffic packet. As I mentioned above, the voice traffic part tend to be a very small data size but cause very frequent transmission. So ROHC can be a very efficient solution to save network resources. But for SIP signaling part, the header compression may not be so efficient because the size of SIP signaling packet tend to be relatively huge comparing to header size. Of course, even in this case you can save a little bit of resource, but the processing overhead caused by header compression processing can be even bigger. So normally, ROHC does not apply to SIP signaling message packet.
UE Capability Information for ROHC
The UE reports its ROHC support in pdcp-Parameters of UE-EUTRA-Capability. The network is expected to enable only profiles from this list, so check this list first when ROHC does not start on a bearer. The red lines in the capture below are the profile flags.
Decoded RRC message,
c1: ueCapabilityInformation-r8 (0)
ueCapabilityInformation-r8
ue-CapabilityRAT-ContainerList: 2 items
Item 0
UE-CapabilityRAT-Container
rat-Type: eutra (0)
ueCapabilityRAT-Container: c51800304184200e1f8dfe1f8dfe1f8dfe1f8dfdfc37f2ea...
UE-EUTRA-Capability
accessStratumRelease: rel9 (1)
ue-Category: 3
pdcp-Parameters
supportedROHC-Profiles
...1 .... profile0x0001: True
.... 1... profile0x0002: True
.... .0.. profile0x0003: False
.... ..0. profile0x0004: False
.... ...0 profile0x0006: False
0... .... profile0x0101: False
.0.. .... profile0x0102: False
..0. .... profile0x0103: False
...0 .... profile0x0104: False
In this capture the UE is a Release 9, category 3 UE. It supports profile 0x0001 for RTP/UDP/IP and profile 0x0002 for UDP/IP, and it supports none of the ROHCv2 profiles. The capture stops after supportedROHC-Profiles. In 36.331, maxNumberROHC-ContextSessions follows it with DEFAULT cs16, so a UE that leaves it out supports 16 context sessions.
36.331 v19.3.0 now defines these flags in ROHC-ProfileSupportList-r15. So a decoder built on a recent release shows the same flags as profile0x0001-r15, profile0x0002-r15 and so on.
RRC Connection Reconfiguration for ROHC
The network switches ROHC on for each DRB, in pdcp-Config of DRB-ToAddMod. The ASN.1 below shows where headerCompression sits, and the capture after it shows one real configuration.
Following is based on
PDCP-Config ::= SEQUENCE {
discardTimer ENUMERATED {
ms50, ms100, ms150, ms300, ms500,
ms750, ms1500, infinity
} OPTIONAL, -- Cond Setup
rlc-AM SEQUENCE {
statusReportRequired BOOLEAN
} OPTIONAL, -- Cond Rlc-AM-UM
rlc-UM SEQUENCE {
pdcp-SN-Size ENUMERATED {len7bits, len12bits}
} OPTIONAL, -- Cond Rlc-UM
headerCompression CHOICE {
notUsed NULL,
rohc SEQUENCE {
maxCID INTEGER (1..16383) DEFAULT 15,
profiles SEQUENCE {
profile0x0001 BOOLEAN,
profile0x0002 BOOLEAN,
profile0x0003 BOOLEAN,
profile0x0004 BOOLEAN,
profile0x0006 BOOLEAN,
profile0x0101 BOOLEAN,
profile0x0102 BOOLEAN,
profile0x0103 BOOLEAN,
profile0x0104 BOOLEAN
},
...
}
},
...,
[[ rn-IntegrityProtection-r10 ENUMERATED {enabled} OPTIONAL -- Cond RN
]],
[[ pdcp-SN-Size-v1130 ENUMERATED {len15bits} OPTIONAL -- Cond Rlc-AM2
]],
[[ ul-DataSplitDRB-ViaSCG-r12 BOOLEAN OPTIONAL, -- Need ON
t-Reordering-r12 ENUMERATED {
ms0, ms20, ms40, ms60, ms80, ms100, ms120, ms140,
ms160, ms180, ms200, ms220, ms240, ms260, ms280, ms300,
ms500, ms750, spare14, spare13, spare12, spare11, spare10,
spare9, spare8, spare7, spare6, spare5, spare4, spare3,
spare2, spare1} OPTIONAL -- Cond SetupS
]],
[[ ul-DataSplitThreshold-r13 CHOICE {
release NULL,
setup ENUMERATED {
b0, b100, b200, b400, b800, b1600, b3200, b6400, b12800,
b25600, b51200, b102400, b204800, b409600, b819200,
spare1}
} OPTIONAL, -- Need ON
pdcp-SN-Size-v1310 ENUMERATED {len18bits} OPTIONAL, -- Cond Rlc-AM3
statusFeedback-r13 CHOICE {
release NULL,
setup SEQUENCE {
statusPDU-TypeForPolling-r13 ENUMERATED {type1, type2} OPTIONAL, -- Need ON
statusPDU-Periodicity-Type1-r13 ENUMERATED {
ms5, ms10, ms20, ms30, ms40, ms50, ms60, ms70, ms80, ms90,
ms100, ms150, ms200, ms300, ms500, ms1000, ms2000, ms5000,
ms10000, ms20000, ms50000} OPTIONAL, -- Need ON
statusPDU-Periodicity-Type2-r13 ENUMERATED {
ms5, ms10, ms20, ms30, ms40, ms50, ms60, ms70, ms80, ms90,
ms100, ms150, ms200, ms300, ms500, ms1000, ms2000, ms5000,
ms10000, ms20000, ms50000} OPTIONAL, -- Need ON
statusPDU-Periodicity-Offset-r13 ENUMERATED {
ms1, ms2, ms5, ms10, ms25, ms50, ms100, ms250, ms500,
ms2500, ms5000, ms25000} OPTIONAL -- Need ON
}
} OPTIONAL -- Need ON
]],
[[ ul-LWA-Config-r14 CHOICE {
release NULL,
setup SEQUENCE {
ul-LWA-DRB-ViaWLAN-r14 BOOLEAN,
ul-LWA-DataSplitThreshold-r14 ENUMERATED {
b0, b100, b200, b400, b800, b1600, b3200, b6400,
b12800, b25600, b51200, b102400, b204800, b409600,
b819200 } OPTIONAL -- Need OR
}
} OPTIONAL, -- Need ON
uplinkOnlyHeaderCompression-r14 CHOICE {
notUsed-r14 NULL,
rohc-r14 SEQUENCE {
maxCID-r14 INTEGER (1..16383) DEFAULT 15,
profiles-r14 SEQUENCE {
profile0x0006-r14 BOOLEAN
},
...
}
} OPTIONAL -- Need ON
]],
[[ uplinkDataCompression-r15 SEQUENCE {
bufferSize-r15 ENUMERATED {kbyte2, kbyte4, kbyte8, spare1},
dictionary-r15 ENUMERATED {sip-SDP, operator} OPTIONAL, -- Need OR
...
} OPTIONAL,-- Cond Rlc-AM4
pdcp-DuplicationConfig-r15 CHOICE {
release NULL,
setup SEQUENCE {
pdcp-Duplication-r15 ENUMERATED {configured, activated}
}
} OPTIONAL -- Need ON
]],
[[
ethernetHeaderCompression-r16 SetupRelease {EthernetHeaderCompression-r16} OPTIONAL -- Need ON
]],
[[ discardTimerExt-r17 SetupRelease {DiscardTimerExt-r17} OPTIONAL -- Need ON
]]
}
Three fields in the listing matter for ROHC. The headerCompression field selects notUsed or rohc. The maxCID field sets the MAX_CID parameter of 36.323, and it has DEFAULT 15. The profiles field holds one BOOLEAN per profile, and value true enables that profile in both uplink and downlink.
Two later extensions are also worth knowing. The uplinkOnlyHeaderCompression-r14 field applies ROHC to the uplink only, and it offers only profile 0x0006, TCP/IP. The ethernetHeaderCompression-r16 field configures Ethernet header compression, which is a separate protocol from ROHC. The network configures headerCompression only when neither uplinkOnlyHeaderCompression nor uplinkDataCompression is configured. It also does not change headerCompression on an existing MCG DRB at any time. 36.331 limits that to handover and to the first reconfiguration after RRC connection re-establishment, when drb-ContinueROHC is not used.
Decoded RRC message,
c1: rrcConnectionReconfiguration-r8 (0)
rrcConnectionReconfiguration-r8
radioResourceConfigDedicated
drb-ToAddModList: 1 item
Item 0
DRB-ToAddMod
drb-Identity: 1
pdcp-Config
rlc-AM
...0 .... statusReportRequired: False
headerCompression: rohc (1)
rohc
profiles
.... ...1 profile0x0001: True
1... .... profile0x0002: True
.0.. .... profile0x0003: False
..0. .... profile0x0004: False
...0 .... profile0x0006: False
.... 0... profile0x0101: False
.... .0.. profile0x0102: False
.... ..0. profile0x0103: False
.... ...0 profile0x0104: False
In the capture, headerCompression is rohc, and profiles 0x0001 and 0x0002 are true. These are exactly the two profiles that the UE reported in the capture above. The maxCID field is absent, so its DEFAULT of 15 applies. With a MAX_CID of 15, 36.323 sets LARGE_CIDS to FALSE, so the ROHC packets on this bearer use small CIDs.
An Example : ROHC Compression - VoLTE
Now let's follow one VoLTE packet through PDCP on the compressing side. The drawing below shows the same packet before and after ROHC compression, byte by byte.

The PDCP packet at the top : a 20 byte IPv4 header, an 8 byte UDP header and a 12 byte RTP header, 40 bytes in total, followed by the voice payload that starts with FD FD.The IPv4 header : 45 means IPv4 with a 20 byte header, and 11 is the protocol number of UDP. The addresses C0 A8 01 02 and C0 A8 01 01 are 192.168.1.2 and 192.168.1.1.The UDP header : the ports are EA 60 and A6 68, the length 00 B4 is 180 bytes, and the checksum is E3 DB.The RTP header : 80 00 05 FD gives RTP version 2 and sequence number 0x05FD. The timestamp is 24 E8 A9 55 and the SSRC is 40 09 04 DE.After compression : the 40 bytes of headers become the 3 byte ROHC header 6F E3 DB, and the payload follows unchanged. The drawing labels the two bytes 80 10 in front of it as the RLC header.
Let's decode the 3 bytes. E3 DB is the UDP checksum, copied unchanged, because profile 0x0001 sends a non-zero UDP checksum in every packet. 6F is 0110 1111 in binary. The leading 0 marks a UO-0 packet, the smallest ROHC header, which carries a 4 bit sequence number and a 3 bit CRC. The sequence number bits are 1101, which is 0xD. This matches the low 4 bits of the RTP sequence number 0x05FD. The decompressor rebuilds every other header byte from its context.
An Example : ROHC UnCompression - VoLTE
The receiving side runs the same steps in reverse. The drawing below starts from an RLC packet that carries a ROHC header, and it ends with the full IP packet that PDCP rebuilds from it.

At the bottom : the RLC header 80 10, which the drawing says is removed before the packet leaves RLC, then the ROHC header 6B E3 DC, then the payload FD FD FE FC and so on.The ROHC header : 6B is 0110 1011 in binary. This is again a UO-0 packet, with sequence number bits 1101 and CRC bits 011. E3 DC is the UDP checksum, carried as it is.At the top : the rebuilt 20 byte IPv4 header, 8 byte UDP header and 12 byte RTP header. The addresses and the ports are swapped compared with the compression example, because this packet travels in the other direction.The checks line up : the rebuilt RTP sequence number is 0x05FD, and its low 4 bits 0xD match the bits in 6B. The rebuilt UDP checksum E3 DC matches the 2 bytes that followed 6B.
The two examples also connect back to the captures above. Both packets are compressed with profile 0x0001, which the UE reported and the network enabled. Neither packet starts with an Add-CID octet, so both belong to CID 0. That fits the small CID space that a MAX_CID of 15 gives.
RRC enables ROHC for each DRB : headerCompression in pdcp-Config selects the profiles and maxCID for that bearer.The profiles follow the UE capability : in the captures above, the network enabled exactly the two profiles that the UE reported.A VoLTE header shrinks from 40 bytes to 3 bytes here : 1 byte of UO-0 header plus the 2 byte UDP checksum.A UO-0 packet carries only 4 bits of sequence number : the decompressor relies on its context for everything else, which is why the state machines above matter.
Reference
- 3GPP TS 36.323 v19.0.0 - clause 5.5 Robust Header Compression and Decompression, Table 5.5.1.1
- 3GPP TS 36.331 v19.3.0 - PDCP-Config, PDCP-Parameters and ROHC-ProfileSupportList-r15 with their field descriptions