RFC 3095
RObust Header Compression (ROHC): Framework and four profiles: RTP, UDP, ESP, and uncompressed
RFC 3095: RObust Header Compression (ROHC) ๐
okay bestie, this is RFC 3095, a Standards Track doc from July 2001 in the Network Working Group ๐ It's the framework for ROHC (RObust Header Compression) plus four profiles: RTP, UDP, ESP, and uncompressed โจ
C. Bormann is the editor from TZI/Uni Bremen ๐ and the co-authors are a whole international squad: C. Burmeister, M. Degermark (Univ. of Arizona), H. Fukushima, R. Hakenberg, and A. Miyazaki, T. Wiebke and T. Yoshimura from Matsushita and NTT DoCoMo, H. Hannu, L-E. Jonsson, A. Martensson and K. Svanbro from Ericsson, T. Koren from Cisco, and K. Le, Z. Liu and H. Zheng from Nokia ๐ iconic cast, no notes ๐ญ The title block doesn't list anything it obsoletes or updates, so no drama there ๐
Status of this Memo ๐ฅ
ngl this part is kinda formal, but the gist is: this document specifies an Internet standards track protocol for the Internet community, and it wants discussion and suggestions for improvements ๐ For the standardization state and status, check the current edition of the "Internet Official Protocol Standards" (STD 1). And distribution of this memo is unlimited ๐ฆ
Copyright Notice ๐ธ
The Internet Society owns the copyright (2001) and all rights are reserved ๐
Abstract โจ
okay so the big idea: this doc specifies a highly robust and efficient header compression scheme for RTP/UDP/IP (Real-Time Transport Protocol, User Datagram Protocol, Internet Protocol), UDP/IP, and ESP/IP (Encapsulating Security Payload) headers ๐ง
Existing header compression schemes don't work well over links with significant error rates and long round-trip times ๐ and honestly, lots of bandwidth-limited links where header compression is essential have exactly those characteristics. so this is the glow-up they need ๐ฅ
It's built as a framework that's designed to be extensible ๐ For example, a scheme for compressing TCP/IP headers will be simple to add, and it's in development. Mobile IPv4 headers don't get special treatment, but they're expected to be compressed well enough by the provided methods for sequences of extension headers and tunneling headers ๐ซถ For the most part the same goes for work in progress on Mobile IPv6, though future work might be needed for some extension headers once a standards track Mobile IPv6 is finished ๐
Table of Contents ๐
the list of sections picks up in the next part, bestie ๐
1. Introduction โจ
Heads up, bestie: only the table of contents came through in what you sent, not the body text. So I've laid out the section skeleton below, with every number and name exactly as the original has them. Paste the body text for each part and I'll fill in the rewrite. ๐ซถ
2. Terminology ๐
2.1. Acronyms
3. Background ๐ธ
3.1. Header compression fundamentals
3.2. Existing header compression schemes
3.3. Requirements on a new header compression scheme
3.4. Classification of header fields
4. Header compression framework ๐ฆ
4.1. Operating assumptions
4.2. Dynamicity
4.3. Compression and decompression states
4.3.1. Compressor states
4.3.1.1. Initialization and Refresh (IR) State
4.3.1.2. First Order (FO) State
4.3.1.3. Second Order (SO) State
4.3.2. Decompressor states
4.4. Modes of operation
4.4.1. Unidirectional mode -- U-mode
4.4.2. Bidirectional Optimistic mode -- O-mode
4.4.3. Bidirectional Reliable mode -- R-mode
4.5. Encoding methods
4.5.1. Least Significant Bits (LSB) encoding
4.5.2. Window-based LSB encoding (W-LSB encoding)
4.5.3. Scaled RTP Timestamp encoding
4.5.4. Timer-based compression of RTP Timestamp
4.5.5. Offset IP-ID encoding
4.5.6. Self-describing variable-length values
4.5.7. Encoded values across several fields in compressed headers
4.6. Errors caused by residual errors
4.7. Impairment considerations
5. The protocol ๐
5.1. Data structures
5.1.1. Per-channel parameters
5.1.2. Per-context parameters, profiles
5.1.3. Contexts and context identifiers
5.2. ROHC packets and packet types
5.2.1. ROHC feedback
5.2.2. ROHC feedback format
5.2.3. ROHC IR packet type
5.2.4. ROHC IR-DYN packet type
5.2.5. ROHC segmentation
5.2.5.1. Segmentation usage considerations
5.2.5.2. Segmentation protocol
5.2.6. ROHC initial decompressor processing
5.2.7. ROHC RTP packet formats from compressor to decompressor
5.2.8. Parameters needed for mode transition in ROHC RTP
5.3. Operation in Unidirectional mode
5.3.1. Compressor states and logic (U-mode)
5.3.1.1. State transition logic (U-mode)
5.3.1.1.1. Optimistic approach, upwards transition
5.3.1.1.2. Timeouts, downward transition
5.3.1.1.3. Need for updates, downward transition
5.3.1.2. Compression logic and packets used (U-mode)
5.3.1.3. Feedback in Unidirectional mode
5.3.2. Decompressor states and logic (U-mode)
5.3.2.1. State transition logic (U-mode)
5.3.2.2. Decompression logic (U-mode)
5.3.2.2.1. Decide whether decompression is allowed
5.3.2.2.2. Reconstruct and verify the header
5.3.2.2.3. Actions upon CRC failure
5.3.2.2.4. Correction of SN LSB wraparound
5.3.2.2.5. Repair of incorrect SN updates
5.3.2.3. Feedback in Unidirectional mode
5.4. Operation in Bidirectional Optimistic mode
5.4.1. Compressor states and logic (O-mode)
5.4.1.1. State transition logic
5.4.1.1.1. Negative acknowledgments (NACKs), downward transition
5.4.1.1.2. Optional acknowledgments, upwards transition
5.4.1.2. Compression logic and packets used
5.4.2. Decompressor states and logic (O-mode)
5.4.2.1. Decompression logic, timer-based timestamp decompression
5.4.2.2. Feedback logic (O-mode)
5.5. Operation in Bidirectional Reliable mode
5.5.1. Compressor states and logic (R-mode)
5.5.1.1. State transition logic (R-mode)
5.5.1.1.1. Upwards transition
5.5.1.1.2. Downward transition
5.5.1.2. Compression logic and packets used (R-mode)
5.5.2. Decompressor states and logic (R-mode)
5.5.2.1. Decompression logic (R-mode)
5.5.2.2. Feedback logic (R-mode)
5.6. Mode transitions
5.6.1. Compression and decompression during mode transitions
5.6.2. Transition from Unidirectional to Optimistic mode
5.6.3. From Optimistic to Reliable mode
5.6.4. From Unidirectional to Reliable mode
5.6.5. From Reliable to Optimistic mode
5.6.6. Transition to Unidirectional mode
5.7. Packet formats
5.7.1. Packet type 0: UO-0, R-0, R-0-CRC
5.7.2. Packet type 1 (R-mode): R-1, R-1-TS, R-1-ID
5.7.3. Packet type 1 (U/O-mode): UO-1, UO-1-ID, UO-1-TS
5.7.4. Packet type 2: UOR-2
5.7.5. Extension formats
5.7.5.1. RND flags and packet types
5.7.5.2. Flags/Fields in context
5.7.6. Feedback packets and formats
5.7.6.1. Feedback formats for ROHC RTP
5.7.6.2. ROHC RTP Feedback options
5.7.6.3. The CRC option
5.7.6.4. The REJECT option
5.7.6.5. The SN-NOT-VALID option
5.7.6.6. The SN option
5.7.6.7. The CLOCK option
5.7.6.8. The JITTER option
5.7.6.9. The LOSS option
5.7.6.10. Unknown option types
5.7.6.11. RTP feedback example
5.7.7. RTP IR and IR-DYN packets
5.7.7.1. Basic structure of the IR packet
5.7.7.2. Basic structure of the IR-DYN packet
5.7.7.3. Initialization of IPv6 Header [IPv6]
5.7.7.4. Initialization of IPv4 Header [IPv4, section 3.1]
5.7.7.5. Initialization of UDP Header [RFC-768]
5.7.7.6. Initialization of RTP Header [RTP]
5.7.7.7. Initialization of ESP Header [ESP, section 2]
5.7.7.8. Initialization of Other Headers
5.8. List compression
5.8.1. Table-based item compression
5.8.1.1. Translation table in R-mode
5.8.1.2. Translation table in U/O-modes
5.8.2. Reference list determination
5.8.2.1. Reference list in R-mode and U/O-mode
5.8.3. Encoding schemes for the compressed list
5.8.4. Special handling of IP extension headers
5.8.4.1. Next Header field
5.8.4.2. Authentication Header (AH)
5.8.4.3. Encapsulating Security Payload Header (ESP)
5.8.4.4. GRE Header [RFC 2784, RFC 2890]
5.8.5. Format of compressed lists in Extension 3
5.8.5.1. Format of IP Extension Header(s) field
5.8.5.2. Format of Compressed CSRC List
5.8.6. Compressed list formats
5.8.6.1. Encoding Type 0 (generic scheme)
5.8.6.2. Encoding Type 1 (insertion only scheme)
5.8.6.3. Encoding Type 2 (removal only scheme)
5.8.6.4. Encoding Type 3 (remove then insert scheme)
5.8.7. CRC coverage for extension headers
5.9. Header compression CRCs, coverage and polynomials
5.9.1. IR and IR-DYN packet CRCs
5.9.2. CRCs in compressed headers
5.10. ROHC UNCOMPRESSED -- no compression (Profile 0x0000)
5.10.1. IR packet
5.10.2. Normal packet
5.10.3. States and modes
5.10.4. Feedback
5.11. ROHC UDP -- non-RTP UDP/IP compression (Profile 0x0002)
5.11.1. Initialization
5.11.2. States and modes
5.11.3. Packet types
5.11.4. Extensions
5.11.5. IP-ID
5.11.6. Feedback
5.12. ROHC ESP -- ESP/IP compression (Profile 0x0003)
5.12.1. Initialization
5.12.2. Packet types
6. Implementation issues ๐ง
6.1. Reverse decompression
6.2. RTCP
6.3. Implementation parameters and signals
6.3.1. ROHC implementation parameters at compressor
6.3.2. ROHC implementation parameters at decompressor
6.4. Handling of resource limitations at the decompressor
6.5. Implementation structures...................................139
6.5.1. Compressor context........................................139
6.5.2. Decompressor context......................................141
6.5.3. List compression: Sliding windows in R-mode and U/O-mode..142
7. Security Considerations.......................................143
8. IANA Considerations...........................................144
9. Acknowledgments...............................................145
10. Intellectual Property Right Claim Considerations.............145
11. References...................................................146
11.1. Normative References.......................................146
11.2. Informative References.....................................147
12. Authors' Addresses...........................................148
Appendix A. Detailed classification of header fields.............152
A.1. General classification......................................153
A.1.1. IPv6 header fields........................................153
A.1.2. IPv4 header fields........................................155
A.1.3. UDP header fields.........................................157
A.1.4. RTP header fields.........................................157
A.1.5. Summary for IP/UDP/RTP....................................159
A.2. Analysis of change patterns of header fields................159
A.2.1. IPv4 Identification.......................................162
A.2.2. IP Traffic-Class / Type-Of-Service........................163
A.2.3. IP Hop-Limit / Time-To-Live...............................163
A.2.4. UDP Checksum..............................................163
A.2.5. RTP CSRC Counter..........................................164
A.2.6. RTP Marker................................................164
A.2.7. RTP Payload Type..........................................164
A.2.8. RTP Sequence Number.......................................164
A.2.9. RTP Timestamp.............................................164
A.2.10. RTP Contributing Sources (CSRC)..........................165
A.3. Header compression strategies...............................165
A.3.1. Do not send at all........................................165
A.3.2. Transmit only initially...................................165
A.3.3. Transmit initially, but be prepared to update.............166
A.3.4. Be prepared to update or send as-is frequently............166
A.3.5. Guarantee continuous robustness...........................166
A.3.6. Transmit as-is in all packets.............................167
A.3.7. Establish and be prepared to update delta.................167
Full Copyright Statement..........................................168
1. Introduction โจ
okay so bestie, the last five years were literally a whole era ๐ two things blew up with the general public: cellular phones and the Internet. Cellular phones gave everyone the iconic power of always being reachable, no matter where they are, at a decent quality level ๐ฑ. The main thing those dedicated terminals did was speech. The Internet, meanwhile, was built for many services from day one, and that flexibility for all kinds of usage is literally its whole main character trait ๐ . Internet terminals have mostly been general-purpose and plugged into fixed connections. And ngl, the experienced quality of some services (like Internet telephony) has sometimes been kind of low ๐ญ.
Now IP telephony is gaining momentum because the technical solutions have gotten so much better ๐ฆ. It seems reasonable to think that in the years ahead, IP will become a commonly used way to carry phone calls. Some future cellular links might also be built on IP and IP telephony. Cellular phones may have become more general-purpose too, with IP stacks that support not only audio and video but also web browsing, email, gaming, and so on ๐.
One scenario we're picturing is the one in Figure 1.1, where two mobile terminals are talking to each other ๐ซถ. Both are connected to base stations over cellular links, and the base stations are connected to each other through a wired (or possibly wireless) network. Sure, you could have one mobile and one wired terminal instead, but the case with two cellular links is technically way more demanding ๐ฅ.
Mobile Base Base Mobile
Terminal Station Station Terminal
| ~ ~ ~ \ / \ / ~ ~ ~ ~ |
| | | |
+--+ | | +--+
| | | | | |
| | | | | |
+--+ | | +--+
| |
|=========================|
Cellular Wired Cellular
Link Network Link
Figure 1.1 : Scenario for IP telephony over cellular links
It's pretty obvious that the wired network can be IP-based ๐ง. With the cellular links, it's less clear. IP could get terminated in the fixed network, with special solutions built for each supported service over the cellular link. But that would limit how flexible the services can be ๐. If it's technically and economically feasible, pure IP all the way from terminal to terminal would have certain advantages ๐. To make that a viable option though, a bunch of problems have to be sorted out, especially the ones about bandwidth efficiency.
For cellular phone systems, using the scarce radio resources efficiently is of vital importance ๐. You need a healthy number of users per cell, or deployment costs will be totally prohibitive ๐. The voice quality should also be as good as today's cellular systems. Even with support for new services, people will probably only accept lower voice quality if the costs drop by a lot ๐.
Now, a big problem with IP over cellular links, especially for interactive voice calls, is the huge header overhead ๐ญ. Speech data for IP telephony will most likely be carried by RTP [RTP]. So a packet gets link layer framing, plus an IP [IPv4] header (20 octets), a UDP [UDP] header (8 octets), and an RTP header (12 octets), for a total of 40 octets. With IPv6 [IPv6], the IP header is 40 octets, which brings the total to 60 octets. The payload size depends on the speech coding and frame sizes being used, and it can be as low as 15-20 octets ๐ธ. So yeah, the need to shrink those headers for efficiency is obvious.
The thing is, cellular links have characteristics that make header compression as defined in [IPHC,CRTP] perform less than well ๐ฅฒ. The biggest one is that the links are lossy: a bit error rate (BER) as high as 1e-3 has to be accepted so the radio resources stay efficiently utilized. In rough operating situations, the BER can climb as high as 1e-2 โก. The other problem is the long round-trip time (RTT) of the cellular link, which can be as high as 100-200 milliseconds. There's also the fact that the residual BER is nontrivial, meaning lower layers can sometimes hand over frames that contain undetected errors ๐ซ . A viable header compression scheme for cellular links must be able to handle loss on the link between the compression and decompression point, as well as loss before the compression point ๐ก๏ธ.
Bandwidth is the most expensive resource on cellular links, while processing power is super cheap in comparison ๐ค. So implementation or computational simplicity of a header compression scheme matters less than its compression ratio and its robustness. Robustness and compression ratio are the stars of the show here โจ๐
2. Terminology โจ
okay bestie, the BCP 14 key words get a special mention first ๐ซถ The words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. So those capitals are doing real work, periodt ๐
Now the glossary, which is honestly my favourite part because it's where all the vocab lives ๐ฆ
BER
Bit Error Rate. Cellular radio links can have a fairly high BER ๐ก In this document BER is usually given as a probability, but you also need to think about the error distribution, because bit errors are not independent ๐
Cellular links
Wireless links between mobile terminals and base stations ๐ฑ
Compression efficiency
The performance of a header compression scheme can be described with three parameters: compression efficiency, robustness and compression transparency ๐ Compression efficiency is determined by how much the header sizes are reduced by the compression scheme โจ
Compression transparency
Same three parameters: compression efficiency, robustness, and compression transparency ๐ธ Compression transparency is a measure of the extent to which the scheme ensures that the decompressed headers are semantically identical to the original headers ๐ซง If all decompressed headers are semantically identical to the corresponding original headers, the transparency is 100 percent ๐ฏ Compression transparency is high when damage propagation is low ๐
Context
The context of the compressor is the state it uses to compress a header. The context of the decompressor is the state it uses to decompress a header. Either of these, or the two in combination, are usually referred to as "context", when it is clear which is intended ๐ง The context holds relevant information from previous headers in the packet stream, such as static fields and possible reference values for compression and decompression. On top of that, additional information describing the packet stream is also part of the context, for example information about how the IP Identifier field changes and the typical inter-packet increase in sequence numbers or timestamps ๐
Context damage
When the context of the decompressor is not consistent with the context of the compressor, decompression may fail to reproduce the original header ๐ญ This can happen when the context of the decompressor has not been initialized properly, or when packets have been lost or damaged between compressor and decompressor ๐
Packets which cannot be decompressed due to inconsistent contexts are said to be lost due to context damage. Packets that are decompressed but contain errors due to inconsistent contexts are said to be damaged due to context damage.
Context repair mechanism
Context repair mechanisms bring the contexts back in sync when they were not ๐ง This is needed to avoid excessive loss due to context damage. Examples are the context request mechanism of CRTP, the NACK mechanisms of O- and R-mode, and the periodic refreshes of U-mode ๐
Also note that there are mechanisms that prevent (some) context inconsistencies from happening in the first place. Examples are the ACK-based updates of the context in R-mode, the repetitions after change in U- and O-mode, and the CRCs which protect context updating information ๐ก๏ธ
CRC-DYNAMIC
Opposite of CRC-STATIC ๐
CRC-STATIC
A CRC over the original header is the primary mechanism ROHC uses to detect incorrect decompression ๐ To decrease computational complexity, the fields of the header are conceptually rearranged when the CRC is computed. It is first computed over octets which are static (called CRC-STATIC in this document), and then over octets whose values are expected to change between packets (CRC-DYNAMIC) ๐ง This way, the intermediate result of the CRC computation, after it has covered the CRC-STATIC fields, can be reused for several packets โป๏ธ The restarted CRC computation only covers the CRC-DYNAMIC octets. See section 5.9.
Damage propagation
Delivery of incorrect decompressed headers, due to errors in (i.e., loss of or damage to) previous header(s) or feedback ๐ฅ
Loss propagation
Loss of headers, due to errors in (i.e., loss of or damage to) previous header(s) or feedback ๐ซ
Error detection
Detection of errors. If error detection is not perfect, there will be residual errors ๐ซฃ
Error propagation
Damage propagation or loss propagation. Basically the drama that spreads from one header to the next ๐ญ
Header compression profile
A header compression profile is a specification of how to compress the headers of a certain kind of packet stream over a certain kind of link ๐ Profiles provide the details of the header compression framework introduced in this document. The profile concept makes use of profile identifiers to separate the different profiles used when setting up the compression scheme. All variations and parameters of the header compression scheme that are not part of the context state are handled by different profile identifiers ๐ท๏ธ
Packet
Generally, a unit of transmission and reception (protocol data unit). Specifically, when contrasted with "frame", it is the packet that is compressed and then decompressed by ROHC ๐ฆ Also called "uncompressed packet".
Packet Stream
A sequence of packets where the field values, and the change patterns of field values, are such that the headers can be compressed using the same context ๐
Pre-HC links
The Pre-HC links are all the links a packet has traversed before the header compression point ๐ค๏ธ If we consider a path with cellular links as first and last hops, the Pre-HC links for the compressor at the last link are the first cellular link plus the wired links in between ๐งต
Residual error
An error introduced during transmission and not detected by lower-layer error detection schemes ๐ซฅ
Robustness
Same three parameters again: compression efficiency, robustness, and compression transparency ๐ช A robust scheme tolerates loss and residual errors on the link over which header compression takes place, without losing additional packets or introducing additional errors in decompressed headers. Main character energy ๐
RTT
The RTT (round-trip time) is the time elapsing from the moment the compressor sends a packet until it receives feedback related to that packet (when such feedback is sent) โฑ๏ธ
Spectrum efficiency
Radio resources are limited and expensive, so they must be used efficiently to make the system economically feasible ๐ถ In cellular systems this is achieved by maximizing the number of users served within each cell, while the quality of the provided services is kept at an acceptable level. A consequence of efficient spectrum use is a high rate of errors (frame loss and residual bit errors), even after channel coding with error correction ๐ฉ
String
A sequence of headers in which the values of all fields being compressed change according to a pattern which is fixed with respect to a sequence number ๐งถ Each header in a string can be compressed by representing it with a ROHC header which essentially only carries an encoded sequence number. Fields not being compressed (e.g., random IP-ID, UDP Checksum) are irrelevant to this definition ๐
Timestamp stride
The timestamp stride (TS_STRIDE) is the expected increase in the timestamp value between two RTP packets with consecutive sequence numbers โฉ
2.1. Acronyms
This section lists most of the acronyms used for reference, so bookmark it ๐
AH Authentication Header.
CID Context Identifier.
CRC Cyclic Redundancy Check. Error detection mechanism.
CRTP Compressed RTP. RFC 2508.
CTCP Compressed TCP. Also called VJ header compression. RFC 1144.
ESP Encapsulating Security Payload.
FC Full Context state (decompressor).
FO First Order state (compressor).
GRE Generic Routing Encapsulation. RFC 2784, RFC 2890.
HC Header Compression.
IPHC IP Header Compression. RFC 2507.
IPX Flag in Extension 2.
IR Initiation and Refresh state (compressor). Also IR packet.
IR-DYN IR-DYN packet.
LSB Least Significant Bits.
MRRU Maximum Reconstructed Reception Unit.
MTU Maximum Transmission Unit.
MSB Most Significant Bits.
NBO Flag indicating whether the IP-ID is in Network Byte Order.
NC No Context state (decompressor).
O-mode Bidirectional Optimistic mode.
PPP Point-to-Point Protocol.
R-mode Bidirectional Reliable mode.
RND Flag indicating whether the IP-ID behaves randomly.
ROHC RObust Header Compression.
RTCP Real-Time Control Protocol. See RTP.
RTP Real-Time Protocol. RFC 1889.
RTT Round Trip Time (see section 2).
SC Static Context state (decompressor).
SN (compressed) Sequence Number. Usually RTP Sequence Number.
SO Second Order state (compressor).
SPI Security Parameters Index.
SSRC Sending source. Field in RTP header.
CSRC Contributing source. Optional list of CSRCs in RTP header.
TC Traffic Class. Octet in IPv6 header. See also TOS.
TOS Type Of Service. Octet in IPv4 header. See also TC.
TS (compressed) RTP Timestamp.
U-mode Unidirectional mode.
W-LSB Window based LSB encoding. See section 4.5.2.
3. Background ๐ฌ
This chapter gives you the background on header compression ๐ฆ The fundamental ideas are described together with existing header compression schemes. Then their drawbacks and requirements are discussed, which is the motivation for new header compression solutions ๐ก
3.1. Header compression fundamentals
okay so the main reason header compression can be done at all is that there is significant redundancy between header fields ๐ That redundancy shows up both within the same packet header and, in particular, between consecutive packets belonging to the same packet stream. By sending static field information only initially, and then utilizing dependencies and predictability for other fields, the header size can be significantly reduced for most packets โจ It's giving efficiency, honestly ๐
Relevant information from past packets is maintained in a context ๐ง The context information is used to compress (decompress) subsequent packets. The compressor and decompressor update their contexts upon certain events. Impairment events may lead to inconsistencies between the contexts of the compressor and decompressor, which in turn may cause incorrect decompression ๐ฌ So a robust header compression scheme needs mechanisms for avoiding context inconsistencies, and it also needs mechanisms for making the contexts consistent when they were not ๐ซถ
3.2. Existing header compression schemes โจ
okay so the OG header compression scheme is CTCP [VJHC], invented by Van Jacobson ๐ฆ. it squishes the 40 octet IP+TCP header down to 4 octets ๐คฏ. when the CTCP compressor spots a transport-level retransmission, it sends a header that completely updates the context ๐. and the iconic part is that this repair trick doesn't need any explicit signaling between compressor and decompressor ๐ .
IP header compression [IPHC] is the general-purpose one ๐ธ. it's a little better than CTCP and can squish arbitrary IP, TCP, and UDP headers. for non-TCP headers, IPHC doesn't use delta encoding, but it is robust ๐ก๏ธ. for TCP, the CTCP repair mechanism gets a boost from a link-level nacking scheme that speeds up the repair. and IPHC doesn't compress RTP headers ๐ โโ๏ธ.
CRTP [CRTP, IPHC] by Casner and Jacobson is giving compression realness ๐. it takes 40 octets of IPv4/UDP/RTP headers down to a minimum of 2 octets when the UDP Checksum is not enabled. if the UDP Checksum is enabled, the minimum CRTP header is 4 octets. CRTP can't use CTCP's repair mechanism, though, since UDP/RTP doesn't retransmit ๐ญ. so instead, CRTP uses explicit signaling messages from decompressor to compressor, called CONTEXT_STATE messages, to say the context is out of sync. that means the link round-trip time limits how fast this context repair can happen โณ.
on lossy links with long round-trip times, like most cellular links, CRTP does not perform well ๐. each lost packet causes several subsequent packets to be lost too, since the context is out of sync for at least one link round-trip time. this behavior is documented in [CRTPC]. for voice calls, those long loss events will wreck the voice quality ๐. bandwidth also gets wasted by the big headers CRTP sends when it updates the context. [CRTPC] found that CRTP did not perform well enough over a lossy cellular link. it is clear that CRTP alone is not a viable header compression scheme for IP telephony over cellular links ๐ซ.
to avoid losing packets because the context is out of sync, CRTP decompressors can try to repair the context locally using a mechanism called TWICE ๐ฎ. each CRTP packet contains a counter that goes up by one for each packet the CRTP compressor sends. if the counter jumps by more than one, at least one packet was lost over the link. the decompressor then guesses how the lost packet(s) would have updated the context. the guess gets verified by decompressing the packet and checking the UDP Checksum -- if that succeeds, the repair counts as successful and the packet can be forwarded or delivered โ . the name TWICE comes from the observation that when the compressed packet stream is regular, the correct guess is to apply the update in the current packet twice ๐ฏ. [CRTPC] found that even with TWICE, CRTP doubled the number of lost packets. TWICE does improve CRTP performance a lot, but there are several problems with using it ๐ฌ:
- It becomes mandatory to use the UDP Checksum:
- the minimal compressed header size increases by 100% to 4 octets.
- most speech codecs developed for cellular links tolerate errors in the encoded data. such codecs won't want to enable the UDP Checksum, since they do want damaged packets to be delivered.
- errors in the payload will make the UDP Checksum fail when the guess is correct (and might make it succeed when the guess is wrong).
- loss in an RTP stream that happens before the compression point makes updates in CRTP headers less regular. simple-minded versions of TWICE will then perform badly. more sophisticated versions would need more repair attempts to succeed ๐งฉ.
3.3. Requirements on a new header compression scheme ๐
the big problem with CRTP is that it isn't robust enough against packets getting damaged between compressor and decompressor ๐ต. a viable header compression scheme must be less fragile. and that extra robustness has to come without making the compressed header bigger, because a larger header would make IP telephony over cellular links economically unattractive ๐ธ.
a major reason CRTP performs badly over cellular links is the long link round-trip time, during which many packets get lost while the context is out of sync โฑ๏ธ. you could attack this directly by finding ways to reduce the link round-trip time. future cellular technologies may indeed achieve lower round-trip times. however, these will probably always be fairly high. the benefits of lower loss and smaller bandwidth demands, if the context can be repaired locally, will still be there even if the link round-trip time goes down. so a reliable way to detect a successful context repair is needed ๐ฏ.
one might argue the better fix is to improve the cellular link so packet loss is less likely to happen. those modifications don't seem to come for free, though. if links were made (almost) error free, the system might not be able to support a large enough number of users per cell, and it might be economically infeasible ๐.
one might also argue that speech codecs should be able to deal with the kind of packet loss CRTP causes, especially since they probably have to handle packet loss anyway if the RTP stream crosses the Internet. that's true, but the kind of loss CRTP induces is hard to deal with ๐ค. it's usually not possible to completely hide a loss event where well over 100 ms worth of sound is totally lost. if loss like that happens often at both ends of the end-to-end path, the speech quality will suffer ๐.
a detailed description of the requirements specified for ROHC may be found in [REQ] ๐.
3.4. Classification of header fields ๐๏ธ
as mentioned earlier, header compression is possible because there's a ton of redundancy between header field values within packets, but especially between consecutive packets ๐. to use these properties, it's important to understand the change patterns of the various header fields.
all header fields are classified in detail in appendix A ๐ง. the fields are first classified at a high level, and then some of them get studied more closely. the appendix ends with recommendations on how the various fields should be handled by header compression algorithms. the main takeaway is that most header fields can easily be compressed away, since they never or seldom change. only 5 fields, with a combined size of about 10 octets, need more sophisticated mechanisms ๐ช. these fields are:
- IPv4 Identification (16 bits) - IP-ID
- UDP Checksum (16 bits)
- RTP Marker (1 bit) - M-bit
- RTP Sequence Number (16 bits) - SN
- RTP Timestamp (32 bits) - TS
the analysis in appendix A reveals that the values of the TS and IP-ID fields can usually be predicted from the RTP Sequence Number, which goes up by one for each packet emitted by an RTP source ๐ข. the M-bit is also usually the same, but it needs to be communicated explicitly every now and then. the UDP Checksum should not be predicted, and it is sent as-is when enabled ๐ โโ๏ธ.
so the way ROHC RTP compression operates is to first establish functions from SN to the other fields, and then reliably communicate the SN ๐ง . whenever a function from SN to another field changes, i.e., the existing function gives a result that's different from the field in the header to be compressed, additional information gets sent to update the parameters of that function โจ.
headers specific to Mobile IP (for IPv4 or IPv6) don't get any special treatment in this document ๐ . they are compressible, though, and it is expected that the compression efficiency for Mobile IP headers will be good enough thanks to the handling of extension header lists and tunneling headers. it would be relatively painless to introduce a new ROHC profile with special treatment for Mobile IPv6 specific headers, should the completed work on the Mobile IPv6 protocols (work in progress in the IETF) make that necessary ๐ง.
4. Header compression framework ๐
4.1. Operating assumptions ๐
okay so cellular links are the VIP guests here, since they're a primary target for ROHC ๐. They have a bunch of characteristics worth knowing about, and ROHC also needs some stuff from the lower layers. All of that is outlined here and explained in way more depth in the lower layer guidelines document [LLG] ๐.
Channels ๐ฆ
ROHC header-compressed packets flow on channels. Unlike many fixed links, some cellular radio links can have several channels connecting the same pair of nodes. Each channel can have different characteristics in terms of error rate, bandwidth, etc. ๐ถ
Context identifiers ๐ง
Sometimes a channel has to carry multiple packet streams, and sometimes it makes sense to give a channel to one packet stream. So ROHC uses a distinct context identifier space per channel, and it can get rid of context identifiers completely for one of the streams when only a few streams share a channel. It's giving efficiency ๐
Packet type indication ๐
Packet type indication happens inside the header compression scheme itself. Unless the link already has a way of indicating packet types that can be used (like PPP), this gives smaller compressed headers overall. It may also be less difficult to allocate a single packet type, rather than many, in order to run ROHC over links like PPP ๐ซถ
Reordering ๐
The channel between compressor and decompressor is required to maintain packet ordering, i.e., the decompressor must receive packets in the same order as the compressor sent them. (Reordering before the compression point, however, is dealt with, i.e., there is no assumption that the compressor will only receive packets in sequence.) ๐
Duplication ๐ฏ
The channel between compressor and decompressor is required to not duplicate packets. (Duplication before the compression point, however, is dealt with, i.e., there is no assumption that the compressor will receive only one copy of each packet.) no cap, that's a lot of responsibility for the channel ๐ญ
Packet length ๐
ROHC is designed under the assumption that lower layers indicate the length of a compressed packet. ROHC packets do not contain length information for the payload. So the lower layers handle the length, and ROHC just vibes ๐ธ
Framing ๐ผ๏ธ
The link layer must provide framing that makes it possible to distinguish frame boundaries and individual frames. Main character energy for the link layer, honestly โจ
Error detection/protection ๐ก๏ธ
The ROHC scheme has been designed to cope with residual errors in the headers delivered to the decompressor. CRCs and sanity checks are used to prevent or reduce damage propagation. However, it is RECOMMENDED that lower layers deploy error detection for ROHC headers and do not deliver ROHC headers with high residual error rates. Bestie, please let the lower layers do their job ๐ฅน
Without giving a hard limit on the residual error rate acceptable to ROHC, it is noted that for a residual bit error rate of at most 1E-5, the ROHC scheme has been designed not to increase the number of damaged headers. In other words, the number of damaged headers due to damage propagation is designed to be less than the number of damaged headers caught by the ROHC error detection scheme. That's the goal, and it's lowkey impressive ๐ฅ
Negotiation ๐ค
In addition to the packet handling mechanisms above, the link layer MUST provide a way to negotiate header compression parameters, see also section 5.1.1. (For unidirectional links, this negotiation may be performed out-of-band or even a priori.) The link layer MUST handle this, no exceptions ๐
4.2. Dynamicity โจ
ROHC gets its compression gain by setting up state information at both ends of the link, meaning at the compressor and at the decompressor. Different parts of the state get established at different times and with different frequency, so some of the state information is more dynamic than the rest. It's giving layers of drama ๐ญ
Some state information gets established when a channel is established. ROHC assumes either an out-of-band negotiation protocol (such as PPP) or predefined channel state (most useful for unidirectional links). In both cases, we call it "negotiated channel state". ROHC does not assume that this state can change dynamically during the channel lifetime, and it does not explicitly support such changes, although some changes may be innocuous from a protocol point of view. An example of negotiated channel state is the highest context ID number to be used by the compressor (MAX_CID) ๐งพ
Other state information is associated with the individual packet streams in the channel. This state is called the context ๐. Using context identifiers (CIDs), multiple packet streams with different contexts can share a channel. The negotiated channel state indicates the highest context identifier to be used, as well as the selection of one of two ways to indicate the CID in the compressed header.
It's up to the compressor to decide which packets to associate with a context (or, equivalently, which packets make up a single stream). However, ROHC is efficient only when all packets of a stream share certain properties, such as having the same values for fields that are described as "static" in this document (for example the IP addresses, port numbers, and RTP parameters such as the payload type). The efficiency of ROHC RTP also depends on the compressor seeing most RTP Sequence Numbers. Streams don't need to share every characteristic important for compression, though ๐
ROHC has a notion of compression profiles. A compression profile denotes a predefined set of such characteristics. To provide extensibility, the negotiated channel state includes the set of profiles acceptable to the decompressor. The context state includes the profile currently in use for the context ๐ง
Other elements of the context state may include the current values of all header fields. From these, one can deduce whether an IPv4 header is present in the header chain, and whether UDP Checksums are enabled. The context state may also include additional compression context that isn't part of an uncompressed header, for example TS_STRIDE, IP-ID characteristics (incrementing as a 16-bit value in network byte order? random?), a number of old reference headers, and the compressor/decompressor state machines (see next section). So much state, bestie ๐
This document actually defines four ROHC profiles: one uncompressed profile, the main ROHC RTP compression profile, and two variants of this profile for compression of packets with header chains that end in UDP and ESP, respectively, but where RTP compression is not applicable. The descriptive text in the rest of this section refers to the main ROHC RTP compression profile ๐
4.3. Compression and decompression states ๐ฆ
Header compression with ROHC can be described as an interaction between two state machines, one compressor machine and one decompressor machine. Each one is instantiated once per context. The compressor and the decompressor each have three states. These are related to each other in many ways, even though the meaning of the states is slightly different for the two parties. Both machines start in the lowest compression state and gradually transit to higher states ๐ช
Transitions don't need to be synchronized between the two machines. In normal operation, only the compressor temporarily transits back to lower states. The decompressor transits back only when context damage is detected ๐
The following subsections give an overview of the state machines and their corresponding states, starting with the compressor ๐ธ
4.3.1. Compressor states ๐
For ROHC compression, the three compressor states are the Initialization and Refresh (IR), First Order (FO), and Second Order (SO) states. The compressor starts in the lowest compression state (IR) and gradually transits to higher compression states. The compressor always operates in the highest possible compression state, under the constraint that the compressor is sufficiently confident that the decompressor has the information necessary to decompress a header compressed according to that state. Honestly, it's a whole glow-up arc ๐
+----------+ +----------+ +----------+
| IR State | <--------> | FO State | <--------> | SO State |
+----------+ +----------+ +----------+
Decisions about transitions between the various compression states are taken by the compressor on the basis of:
- variations in packet headers
- positive feedback from decompressor (Acknowledgments -- ACKs)
- negative feedback from decompressor (Negative ACKs -- NACKs)
- periodic timeouts (when operating in unidirectional mode, i.e., over simplex channels or when feedback is not enabled)
How transitions are performed is explained in detail in chapter 5 for each mode of operation ๐
4.3.1.1. Initialization and Refresh (IR) State ๐ฑ
The purpose of the IR state is to initialize the static parts of the context at the decompressor or to recover after failure. In this state, the compressor sends complete header information. This includes all static and nonstatic fields in uncompressed form, plus some additional information. It's the full reset, bestie ๐
The compressor stays in the IR state until it is fairly confident that the decompressor has received the static information correctly ๐ซถ
4.3.1.2. First Order (FO) State ๐ฆ
The purpose of the FO state is to efficiently communicate irregularities in the packet stream. When operating in this state, the compressor rarely sends information about all dynamic fields, and the information it does send is usually compressed at least partially. Only a few static fields can be updated. The difference between IR and FO should therefore be clear โจ
The compressor enters this state from the IR state, and from the SO state whenever the headers of the packet stream do not conform to their previous pattern. It stays in the FO state until it is confident that the decompressor has acquired all the parameters of the new pattern. Changes in fields that are always irregular are communicated in all packets and are therefore part of what is a uniform pattern ๐
Some or all packets sent in the FO state carry context updating information. It is very important to detect corruption of such packets to avoid erroneous updates and context inconsistencies. This one is serious, so no bad vibes allowed ๐ญ
4.3.1.3. Second Order (SO) State ๐
This is the state where compression is optimal ๐. The compressor enters the SO state when the header to be compressed is completely predictable given the SN (RTP Sequence Number), and the compressor is sufficiently confident that the decompressor has acquired all parameters of the functions from SN to other fields. Correct decompression of packets sent in the SO state only hinges on correct decompression of the SN. However, successful decompression also requires that the information sent in the preceding FO state packets has been successfully received by the decompressor. Teamwork makes the dream work ๐ซถ
The compressor leaves this state and goes back to the FO state when the header no longer conforms to the uniform pattern and cannot be independently compressed on the basis of previous context information ๐
4.3.2. Decompressor states โจ
okay so the decompressor starts out in its lowest state, "No Context" ๐ฑ, and then levels up bit by bit ๐. once it reaches "Full Context" ๐, it basically never leaves (it's giving commitment ๐ ).
+--------------+ +----------------+ +--------------+
| No Context | <---> | Static Context | <---> | Full Context |
+--------------+ +----------------+ +--------------+
at the very start, while it's in "No Context", the decompressor hasn't managed to decompress a single packet yet ๐. once one comes through correctly (like an initialization packet carrying static and dynamic info), it can jump all the way up to "Full Context" ๐. it only drops back down after repeated failures, and even then it goes to "Static Context" first ๐ซถ. if packets sent in the FO state keep decompressing fine there, that's usually enough to get it back to "Full Context". it only falls all the way to "No Context" if decompression of several FO-state packets fails while it's in "Static Context" ๐ญ.
when these state transitions happen is explained in detail in chapter 5 ๐. iykyk ๐
4.4. Modes of operation ๐ฆ
ROHC has three modes of operation: Unidirectional, Bidirectional Optimistic, and Bidirectional Reliable mode ๐.
it's important to get the difference between states (from the last chapter) and modes ๐ง . they're totally separate things โจ. the state stuff is the same no matter which mode you're in, but the mode is what decides the logic for state transitions and what to do in each state ๐.
+----------------------+
| Unidirectional Mode |
| +--+ +--+ +--+ |
| |IR| |FO| |SO| |
| +--+ +--+ +--+ |
+----------------------+
^ ^
/ \
/ \
v v
+----------------------+ +----------------------+
| Optimistic Mode | | Reliable Mode |
| +--+ +--+ +--+ | | +--+ +--+ +--+ |
| |IR| |FO| |SO| | <--------------> | |IR| |FO| |SO| |
| +--+ +--+ +--+ | | +--+ +--+ +--+ |
+----------------------+ +----------------------+
the best mode depends on the environment the compression protocol is running in ๐, like how good the feedback channel is, how often errors happen and how they're spread out, and how much header size varies ๐. All ROHC implementations MUST implement and support all three modes of operation. ๐ each mode gets a quick intro in the subsections below. the detailed compression and decompression logic for all three is in chapter 5, and so are the mode transitions ๐ซถ.
4.4.1. Unidirectional mode -- U-mode ๐ค
in Unidirectional mode, packets only go one way: from compressor to decompressor ๐. no return path needed! which makes ROHC work over links where a way back is missing or just not wanted ๐.
in U-mode, the compressor only changes states because of periodic timeouts and weird changes in the header field pattern of the compressed stream โฐ. since there are periodic refreshes and no feedback to kick off error recovery, U-mode is less efficient and has a slightly higher chance of loss propagation than the Bidirectional modes ๐ฌ.
Compression with ROHC MUST start in the Unidirectional mode. ๐ after that, it can switch to any Bidirectional mode as soon as a packet has reached the decompressor and the decompressor has sent back a feedback packet saying it wants that switch (see chapter 5) โจ.
4.4.2. Bidirectional Optimistic mode -- O-mode ๐ธ
O-mode is basically a cousin of Unidirectional mode ๐ฏ. the big difference is that there's a feedback channel from decompressor to compressor, used for error recovery requests and (optionally) acknowledgments of significant context updates. pure sequence number updates don't get acks though ๐. and there are no periodic refreshes in O-mode ๐ซ.
O-mode is all about max compression efficiency and using the feedback channel sparingly ๐ท. it also cuts down on damaged headers reaching the upper layers because of residual errors or context invalidation ๐ก๏ธ. the context gets invalidated more often than in R-mode, especially when there are long loss or error bursts ๐ต. check section 4.7 for more details ๐.
4.4.3. Bidirectional Reliable mode -- R-mode ๐ฅ
R-mode is pretty different from the other two ๐ . the biggest differences: it uses the feedback channel a lot more, and it has stricter logic on both sides that stops compressor and decompressor from losing context sync, except when the residual bit error rate is really high ๐คฏ. feedback goes out to acknowledge every context update, including updates to the sequence number field ๐. but not every packet updates the context in Reliable mode ๐.
R-mode is built to be as robust as possible against loss and damage propagation, meaning it works hard to minimize context invalidation even during header loss or error bursts ๐ช. it may have a lower chance of context invalidation than O-mode, but when the context does get invalidated, more damaged headers can slip through ๐ญ. check section 4.7 for more details ๐.
4.5. Encoding methods ๐งฉ
this chapter covers the encoding methods used for header fields ๐. how each method gets applied to each field (like the values of associated parameters) is in section 5.7 ๐.
4.5.1. Least Significant Bits (LSB) encoding ๐
LSB encoding is for header fields whose values usually change only a little ๐ฑ. instead of sending the whole field value, the compressor sends just the k least significant bits, where k is a positive integer ๐ข. once the decompressor has those k bits, it works out the original value using a previously received value as a reference, called v_ref ๐ง .
the scheme is guaranteed to be correct as long as the compressor and decompressor each use these two intervals:
- the interval where the original value lives, and
- the interval where the original value is the only value with the exact same k least significant bits as the ones that were sent ๐ฏ.
the interpretation interval can be described by a function f(v_ref, k). so let's say
f(v_ref, k) = [v_ref - p, v_ref + (2^k - 1) - p]
where p is an integer ๐.
<------- interpretation interval (size is 2^k) ------->
|-------------+---------------------------------------|
v_ref - p v_ref v_ref + (2^k-1) - p
the f function has a really cute property: for any value of k, the k least significant bits will uniquely identify one value in f(v_ref, k) ๐.
the parameter p is there so the interpretation interval can be shifted around v_ref ๐. picking a good value for p gives you a more efficient encoding for fields with certain characteristics โจ. here are some examples:
- a) For field values that are expected always to increase, p can be set to -1. The interpretation interval becomes
[v_ref + 1, v_ref + 2^k]. ๐ - b) For field values that stay the same or increase, p can be set to 0. The interpretation interval becomes
[v_ref, v_ref + 2^k - 1]. ๐ฌ - c) For field values that are expected to deviate only slightly from a constant value, p can be set to 2^(k-1) - 1. The interpretation interval becomes
[v_ref - 2^(k-1) + 1, v_ref + 2^(k-1)]. ๐ซง - d) For field values that are expected to undergo small negative changes and larger positive changes, such as the RTP TS for video, or RTP SN when there is misordering, p can be set to 2^(k-2) - 1. The interval becomes
[v_ref - 2^(k-2) + 1, v_ref + 3 * 2^(k-2)], i.e., 3/4 of the interval is used for positive changes. ๐ฌ
here's the simplified procedure for LSB compression and decompression ๐ง. it gets modified for robustness and damage propagation protection in the next subsection:
- The compressor (decompressor) always uses v_ref_c (v_ref_d), the last value that has been compressed (decompressed), as v_ref;
- When compressing a value v, the compressor finds the minimum value of k such that v falls into the interval f(v_ref_c, k). Call this function k = g(v_ref_c, v). When only a few distinct values of k are possible, for example due to limitations imposed by packet formats (see section 5.7), the compressor will instead pick the smallest k that puts v in the interval f(v_ref_c, k).
- When receiving m LSBs, the decompressor uses the interpretation interval f(v_ref_d, m), called interval_d. It picks as the decompressed value the one in interval_d whose LSBs match the received m bits.
heads up, the values have a finite range ๐. for example, the RTP SN goes from 0 to 0xFFFF. when the SN value is close to 0 or 0xFFFF, the interpretation interval can straddle the wraparound boundary between 0 and 0xFFFF ๐.
โ ๏ธ the scheme gets complicated because of two things: packet loss between compressor and decompressor, and transmission errors that the lower layer doesn't catch ๐ฑ. with packet loss, the compressor and decompressor lose sync on v_ref, and so also on the interpretation interval. if v is still in the intersection(interval_c, interval_d), decompression is correct. otherwise, you get incorrect decompression ๐. the next section digs into this more ๐.
with undetected transmission errors, the corrupted LSBs produce an incorrectly decompressed value that then gets used as v_ref_d, which is likely to cause damage propagation ๐ซ . the fix is a secure reference: a reference value whose correctness is verified by a protecting CRC ๐ก๏ธ. so procedure 1) above gets modified like this:
- a) the compressor always uses as v_ref_c the last value that has been compressed and sent with a protecting CRC. b) the decompressor always uses as v_ref_d the last correct value, as verified by a successful CRC.
one more thing: in U/O-mode, 1) b) is modified so that if decompression of the SN fails using the last verified SN reference, another decompression attempt is made using the last but one verified SN reference ๐. this helps limit damage propagation when a small CRC fails to detect a damaged value ๐งฏ. see section 5.3.2.2.3 for further details ๐.
4.5.2. Window-based LSB encoding (W-LSB encoding) ๐ฆ
okay so this section takes the simple LSB trick from 4.5.1 and makes it sturdy enough for a messy network ๐ช The big problem is that the compressor can't always know which reference value the decompressor is actually using for a given v, because some candidate references may have been lost or damaged on the way ๐ญ The fix is to shrink the candidate list using feedback or some reasonable guesses. Then the compressor picks a k that works no matter which candidate the decompressor ends up with, so v is always covered by the resulting interval_d ๐ฏ
Here's the bestie part: the decompressor always uses the last value that passed its CRC as the reference. So the compressor keeps a sliding window of possible reference values. The window starts out empty โจ The compressor runs these three operations on it:
- After it sends a value
v(compressed or uncompressed) that's protected by a CRC, the compressor addsvto the sliding window. ๐ - For each value
vit's compressing, the compressor setsk = max(g(v_min, v), g(v_max, v)), wherev_minandv_maxare the minimum and maximum values in the sliding window, andgis the function from the previous section. ๐งฎ - Once the compressor is sufficiently confident that a certain value
v, and every value older than it, won't be used as a reference by the decompressor, it advances the window by removing those values (includingv) ๐งน Confidence can come from different places. In R-mode, an ACK from the decompressor means values older than the ACKed one can be dropped from the sliding window. In U/O-mode there's always a CRC to check that decompression worked, so a sliding window with a limited maximum width is used instead. The window width is an implementation-dependent optimization parameter, so it's up to whoever builds it ๐ธ
Note that the decompressor follows the procedure from the previous section, except that in R-mode it MUST ACK each header received with a succeeding CRC (see also section 5.5). That ACK is the thing that keeps the whole window system honest ๐ซถ
4.5.3. Scaled RTP Timestamp encoding โฑ๏ธ
okay the RTP Timestamp (TS) usually doesn't jump by some random amount from packet to packet ๐ It goes up in a neat multiple of some unit, called TS_STRIDE. For audio, the sample rate is normally 8 kHz and one voice frame may cover 20 ms. Each voice frame is often sent in one RTP packet too, so the RTP increment is always n * 160 (= 8000 * 0.02) for some integer n. Silence periods don't change this, because the sample clock at the source normally keeps running without changing the frame rate or frame boundaries ๐ง
Video has a TS_STRIDE too when you look at the video frame level ๐ฌ The sample rate for most video codecs is 90 kHz. If the frame rate is fixed at, say, 30 frames/second, the TS goes up by n * 3000 (= n * 90000 / 30) between video frames. A video frame is often split across several RTP packets for extra robustness against packet loss, and in that case several RTP packets carry the same TS ๐งฉ
When scaled RTP Timestamp encoding is in use, the TS gets downscaled by a factor of TS_STRIDE before compression. This saves the following number of bits for each compressed TS:
floor(log2(TS_STRIDE))
TS and TS_SCALED satisfy this equality:
TS = TS_SCALED * TS_STRIDE + TS_OFFSET
TS_STRIDE is sent explicitly, and TS_OFFSET is communicated implicitly ๐ฆ Here's how the algorithm goes:
- Initialization: The compressor sends the value of TS_STRIDE and the absolute value of one or several TS fields. The decompressor uses those absolute values to set TS_OFFSET to (absolute value) modulo TS_STRIDE. TS_OFFSET comes out the same no matter which absolute value is used, as long as the unscaled TS doesn't wrap around (see 4) below) ๐
- Compression: After initialization, the compressor stops compressing the original TS values. Instead, it compresses the downscaled values: TS_SCALED = TS / TS_STRIDE. The compression method can be either W-LSB encoding or the timer-based encoding from the next section โก
- Decompression: When the compressed value of TS_SCALED arrives, the decompressor first works out the original TS_SCALED. Then it calculates the original RTP TS as:
TS = TS_SCALED * TS_STRIDE + TS_OFFSET
- Offset at wraparound: When the unscaled 32-bit TS wraps around, the current TS_OFFSET used in the equation above stops being valid ๐ฑ Let's say TS_STRIDE = 160 = 0xA0 and the current TS = 0xFFFFFFF0. Then TS_OFFSET is 0x50 = 80. If the next RTP TS is 0x00000130 (so the increment is 160 * 2 = 320), the new TS_OFFSET should be 0x00000130 modulo 0xA0 = 0x90 = 144. The compressor is not required to re-initialize TS_OFFSET at wraparound. Instead, the decompressor MUST detect the wraparound of the unscaled TS (which is trivial) and update TS_OFFSET to:
TS_OFFSET = (Wrapped around unscaled TS) modulo TS_STRIDE
- Interpretation interval at wraparound: The interpretation interval for the scaled TS at wraparound needs special rules ๐ The reason is that the maximum scaled TS, TSS_MAX (0xFFFFFFFF / TS_STRIDE), might not look like 2^m - 1. For example, when TS_STRIDE is 160, the scaled TS is at most 26843545, which has LSBs 10011001. So the wraparound boundary between TSS_MAX and zero might not line up with a natural boundary between LSBs:
interpretation interval
|<------------------------------>|
unused scaled TS
------------|--------------|---------------------->
TSS_MAX zero
When TSS_MAX is part of the interpretation interval, some unused values get inserted after TSS_MAX so their LSBs follow on naturally from each other. For example, for TS_STRIDE = 160 and k = 4, the values with LSBs 1010 through 1111 are inserted. How many values get inserted depends on k and the LSBs of the maximum scaled TS. The number of valid values in the interpretation interval should be high enough to keep things robust, and this rule makes sure of that:
Let a be the number of LSBs needed if there was no
wraparound, and let b be the number of LSBs needed to
disambiguate between TSS_MAX and zero where the a LSBs of
TSS_MAX are set to zero. The number of LSB bits to send
while TSS_MAX or zero is part of the interpretation interval
is b.
This scaling method works for lots of frame-based codecs ๐ The catch is that TS_STRIDE might change during a session, for example because of adaptation strategies ๐ If that happens, the unscaled TS gets compressed until the new TS_STRIDE and TS_OFFSET have been re-initialized. Slay, that's the whole thing ๐
4.5.4. Timer-based compression of RTP Timestamp โฐ
okay bestie, so the RTP Timestamp [RFC 1889] is defined to identify the number of the first sample used to generate the payload ๐ When 1) RTP packets carry payloads for a fixed sampling interval, 2) the sampling happens at a constant rate, and 3) packets are generated in lock-step with sampling, the timestamp value will closely approximate a linear function of the time of day โจ This is the case for conversational media, like interactive speech ๐ฃ๏ธ The linear ratio is set by the source sample rate. The linear pattern can get messy with packetization (like video, where one video frame is usually several RTP packets) or frame rearrangement (like B-frames getting sent out-of-order by some video codecs) ๐
With a fixed sample rate of 8 kHz, 20 ms in the time domain is the same as an increment of 160 in the unscaled TS domain, and an increment of 1 in the scaled TS domain with TS_STRIDE = 160 ๐ง
So the (scaled) TS of headers arriving at the decompressor is a linear function of time of day, with some wobble from delay jitter (and clock inaccuracies) between the source and the decompressor ๐ In normal operation, meaning no crashes or failures, the delay jitter stays bounded so it can meet the requirements of conversational real-time traffic ๐ซถ That means the decompressor can use a local clock to get an approximation of the (scaled) TS in the header it's decompressing, just by looking at arrival time ๐ฐ๏ธ Then the k LSBs of the (scaled) TS carried in the header refine that approximation. The value of k needed for correct decompression depends on the jitter between the source and the decompressor ๐
If the compressor knows the potential jitter between compressor and decompressor, it can work out k by using a local clock to estimate jitter in packet arrival times. Or it can use a fixed k and just discard packets that arrive too far out of time ๐ฆ
The advantages of this scheme include:
- a) The size of the compressed TS is constant and small. Specifically, it does NOT depend on the length of silence intervals. Other TS compression techniques have to send a number of bits that depends on the duration of the preceding silence interval at the start of a talkspurt, which is so much more drama ๐
- b) No synchronization is required between the clock local to the compressor and the clock local to the decompressor ๐ธ
Note that although this scheme can work with both scaled and unscaled TS, in practice it's always combined with scaled TS encoding, because the clock resolution requirement is way less demanding (e.g., 20 ms instead of 1/8 ms) ๐ So the algorithm below assumes the clock-based encoding scheme operates on the scaled TS. The unscaled TS case would be similar, just with changes to the scale factors ๐
The major job of the compressor is to determine the value of k ๐ง Its sliding window now holds not only potential reference values for the TS, but also their times of arrival at the compressor ๐
- The compressor maintains a sliding window
{(T_j, a_j), for each header j that can be used as a reference},
where T_j is the scaled TS for header j, and a_j is the arrival time of header j. The sliding window serves the same purpose as the W-LSB sliding window of section 4.5.2 ๐ช
- When a new header n arrives with T_n as the scaled TS, the compressor notes the arrival time a_n. It then calculates
Max_Jitter_BC =
max {|(T_n - T_j) - ((a_n - a_j) / TIME_STRIDE)|,
for all headers j in the sliding window},
where TIME_STRIDE is the time interval equivalent to one TS_STRIDE, e.g., 20 ms. Max_Jitter_BC is the maximum observed jitter before the compressor, in units of TS_STRIDE, for the headers in the sliding window ๐
- k is calculated as
k = ceiling(log2(2 * J + 1),
where J = Max_Jitter_BC + Max_Jitter_CD + 2.
Max_Jitter_CD is the upper bound of jitter expected on the communication channel between compressor and decompressor (CD-CC). It depends only on the characteristics of CD-CC ๐ฐ๏ธ
The constant 2 accounts for the quantization error introduced by the clocks at the compressor and decompressor, which can be +/-1 โฑ๏ธ
Note that the calculation of k follows the compression algorithm described in section 4.5.1, with p = 2^(k-1) - 1 ๐งฎ
- The sliding window is subject to the same window operations as in section 4.5.2, 1) and 3), except that the values added and removed are paired with their arrival times ๐ฏ
Decompressor:
- The decompressor uses as its reference header the last correctly (as verified by CRC) decompressed header. It maintains the pair (T_ref, a_ref), where T_ref is the scaled TS of the reference header, and a_ref is the arrival time of the reference header ๐
- When receiving a compressed header n at time a_n, the approximation of the original scaled TS is calculated as:
T_approx = T_ref + (a_n - a_ref) / TIME_STRIDE.
- The approximation is then refined by the k least significant bits carried in header n, following the decompression algorithm of section 4.5.1, with p = 2^(k-1) - 1 ๐
Note: The algorithm does not assume any particular pattern in the packets arriving at the compressor. In other words, it tolerates reordering before the compressor and nonincreasing RTP Timestamp behavior ๐
Note: Integer arithmetic is used in all equations above. If TIME_STRIDE is not equal to an integral number of clock ticks, time must be normalized so that TIME_STRIDE is an integral number of clock ticks. For example, if a clock tick is 20 ms and TIME_STRIDE is 30 ms, (a_n - a_ref) in 2) can be multiplied by 3 and TIME_STRIDE can have the value 2 ๐ข
Note: The clock resolution of the compressor or decompressor can be worse than TIME_STRIDE. In that case, the difference, i.e., actual resolution - TIME_STRIDE, is treated as additional jitter in the calculation of k ๐ซ
Note: The clock resolution of the decompressor may be communicated to the compressor using the CLOCK feedback option ๐ก
Note: The decompressor may observe the jitter and report this to the compressor using the JITTER feedback option. The compressor may use this information to refine its estimate of Max_Jitter_CD ๐
4.5.5. Offset IP-ID encoding ๐ช
okay so, as all IPv4 packets have an IP Identifier to allow for fragmentation, ROHC provides for transparent compression of this ID ๐ฆ There is no explicit support in ROHC for the IPv6 fragmentation header, so there is never a need to discuss IP IDs outside the context of IPv4 ๐ โโ๏ธ
This section assumes (initially) that the IPv4 stack at the source host assigns IP-ID according to the value of a 2-byte counter, which goes up by one after each assignment to an outgoing packet ๐ข So the IP-ID field of a particular IPv4 packet flow increments by 1 from packet to packet, except when the source has emitted intermediate packets that don't belong to that flow ๐
For these IPv4 stacks, the RTP SN increases by 1 for each packet emitted, and the IP-ID increases by at least the same amount โจ So it's way more efficient to compress the offset, i.e., (IP-ID - RTP SN), instead of the IP-ID itself ๐ง
The rest of section 4.5.5 describes how to compress and decompress the sequence of offsets using W-LSB encoding and decoding, with p = 0 (see section 4.5.1). All IP-ID arithmetic is done using unsigned 16-bit quantities, i.e., modulo 2^16 ๐ธ
Compressor:
The compressor uses W-LSB encoding (section 4.5.2) to compress a sequence of offsets
Offset_i = ID_i - SN_i,
where ID_i and SN_i are the values of the IP-ID and RTP SN of header i. The sliding window contains such offsets and not the values of header fields, but the rules for adding and deleting offsets from the window otherwise follow section 4.5.2 ๐ช
Decompressor:
The reference header is the last correctly (as verified by CRC) decompressed header ๐
When receiving a compressed packet m, the decompressor calculates Offset_ref = ID_ref - SN_ref, where ID_ref and SN_ref are the values of IP-ID and RTP SN in the reference header, respectively.
Then W-LSB decoding is used to decompress Offset_m, using the received LSBs in packet m and Offset_ref. Note that m may contain zero LSBs for Offset_m, in which case Offset_m = Offset_ref ๐ซถ
Finally, the IP-ID for packet m is regenerated as
IP-ID for m = decompressed SN of packet m + Offset_m
Network byte order:
Some IPv4 stacks do use a counter to generate IP ID values as described, but they don't transmit the contents of this counter in network byte order. Instead, they send the two octets reversed ๐ In this case, the compressor can compress the IP-ID field after swapping the bytes. Consequently, the decompressor also swaps the bytes of the IP-ID after decompression to regenerate the original IP-ID ๐ This requires that the compressor and the decompressor synchronize on the byte order of the IP-ID field using the NBO or NBO2 flag (see section 5.7) ๐ค
Random IP Identifier:
Some IPv4 stacks generate IP Identifier values using a pseudo-random number generator. While this may provide some security benefits, it makes it pointless to attempt compressing the field ๐ฒ Therefore, the compressor should detect such random behavior of the field. After detection and synchronization with the decompressor using the RND or RND2 flag, the field is sent as-is in its entirety, as additional octets after the compressed header ๐
4.5.6. Self-describing variable-length values ๐งฉ
The values of TS_STRIDE and a few other compression parameters can vary widely. TS_STRIDE can be 160 for voice and 90 000 for 1 f/s video ๐ฌ To optimize the transfer of such values, a variable number of octets is used to encode them. The number of octets used is determined by the first few bits of the first octet ๐ฐ
First bit is 0: 1 octet.
7 bits transferred.
Up to 127 decimal.
Encoded octets in hexadecimal: 00 to 7F
First bits are 10: 2 octets.
14 bits transferred.
Up to 16 383 decimal.
Encoded octets in hexadecimal: 80 00 to BF FF
First bits are 110: 3 octets.
21 bits transferred.
Up to 2 097 151 decimal.
Encoded octets in hexadecimal: C0 00 00 to DF FF FF
First bits are 111: 4 octets.
29 bits transferred.
Up to 536 870 911 decimal.
Encoded octets in hexadecimal: E0 00 00 00 to FF FF FF FF
4.5.7. Encoded values across several fields in compressed headers โจ
okay so when a compressed header has an extension, one encoded value can be split across more than one field ๐งฉ when that happens, the more significant bits live closer to the start of the header. and if the fields have more bits than the value needs, the most significant field gets padded with zeroes in its most significant bits ๐ซถ
let me give you an example bc it's giving clarity ๐ an unscaled TS value can ride in a UOR-2 header (see section 5.7) with an extension of type 3. the Tsc bit of the extension is unset (zero), and the variable length TS field of the extension is 4 octets, with 29 bits available for the TS (see section 4.5.6). the UOR-2 TS field carries the three most significant bits of the unscaled TS, and the 4-octet TS field in the extension carries the remaining 29 bits ๐ธ
4.6. Errors caused by residual errors ๐
ROHC is built on the idea that packets can get damaged between the compressor and the decompressor, and that those damaged packets can still get delivered to the decompressor ("residual errors") ๐
residual errors can mess with the SN in compressed headers ๐ญ the result is a header that upper layers might not be able to tell apart from a correct one. if the compressed header has a CRC, the CRC catches the bad header with a probability that depends on how big the CRC is. if ROHC doesn't detect it, the bad header gets passed up to upper layers.
the SN isn't the only thing that can get hurt though:
- (a) damage to packet type indication bits can make a header get interpreted as having a different packet type.
- (b) damage to CID information can make a packet get interpreted against another context and possibly another profile too ๐ this is worse when a large part of the CID space is in use, because the damaged CID is then likely to match an active context.
- (c) feedback can get residual errors too, whether it's piggybacked or sent in separate ROHC packets. ROHC uses sanity checks and adds CRCs to vital feedback info so some damaged feedback can be spotted ๐ฅ
Note that context damage can also result in generation of incorrect headers; section 4.7 elaborates further on this. ๐ง
4.7. Impairment considerations ๐
impairments to headers can be sorted into these types:
- (1) the lower layer was not able to decode the packet and did not deliver it to ROHC,
- (2) the lower layer was able to decode the packet, but discarded it because of a detected error,
- (3) ROHC detected an error in the generated header and discarded the packet, or
- (4) ROHC did not detect that the regenerated header was damaged and delivered it to upper layers.
impairments cause loss or damage of individual headers ๐ฅบ some impairment scenarios also invalidate context, and that leads to loss propagation and damage propagation. damage propagation and undetected residual errors both add to the number of damaged headers that reach upper layers. loss propagation, plus impairments that lose or discard single packets, both add to the packet loss upper layers see.
here are some examples of context-invalidating scenarios ๐
- (a) an impairment of type (4) on the forward channel, which makes the decompressor update its context with incorrect information;
- (b) a loss/error burst of pattern update headers: impairments of types (1), (2) and (3) on consecutive pattern update headers. a pattern update header is one carrying new pattern information, e.g., at the beginning of a new talk spurt. this makes the decompressor lose the pattern update information;
- (c) a loss/error burst of headers: impairments of types (1), (2) and (3) on enough consecutive headers to make the decompressor lose the SN synchronization;
- (d) an impairment of type (4) on the feedback channel that mimics a valid ACK and makes the compressor update its context;
- (e) a burst of damaged headers (3) erroneously triggers the "k-out-of-n" rule for detecting context invalidation, which results in a NACK/update sequence during which headers are discarded.
scenario (a) is mitigated by the CRC carried in all context updating headers ๐ก๏ธ the larger the CRC, the lower the chance of context invalidation caused by (a). in R-mode, the CRC of context updating headers is always 7 bits or more. in U/O-mode, it is usually 3 bits and sometimes 7 or 8 bits.
scenario (b) is almost completely eliminated when the compressor uses ACKs to make sure no context updating headers are lost, as in R-mode โจ
scenario (c) is almost completely eliminated when the compressor uses ACKs to make sure the decompressor will always detect the SN wraparound, as in R-mode. it's also mitigated by the SN repair mechanisms in U/O-mode ๐
scenario (d) only happens when the compressor receives a damaged header that mimics an ACK of some header in the W-LSB window, say an ACK of header 2, while in reality header 2 was never received or accepted by the decompressor. in other words, header 2 was hit by impairment (1), (2) or (3). the damaged header has to mimic the feedback packet type, the ACK feedback type, and the SN LSBs of some header in the W-LSB window ๐ณ
scenario (e) happens when a burst of residual errors makes the CRC check fail in k out of the last n headers carrying CRCs. a large k and n lowers the probability of scenario (e), but also increases the number of headers lost or damaged as a consequence of any context invalidation ๐
ROHC detects damaged headers using CRCs over the original headers ๐ the smallest headers in this document either include a 3-bit CRC (U/O-mode) or do not include a CRC (R-mode). so for the smallest headers, damage is detected with a probability of roughly 7/8 for U/O-mode. for R-mode, damage to the smallest headers is not detected.
all other things (coding scheme at lower layers, etc.) being equal, the rate of headers damaged by residual errors will be lower when headers are compressed than when they are not, since fewer bits are transmitted. so for a given ROHC CRC setup, the rate of incorrect headers delivered to applications will also go down ๐
the analysis above suggests that U/O-mode may be more prone than R-mode to context invalidation. on the other hand, the CRC present in all U/O-mode headers keeps screening out residual errors coming from lower layers, reduces the number of damaged headers delivered to upper layers when context is invalidated, and permits quick detection of context invalidation ๐ฆ
R-mode always uses a stronger CRC on context updating headers, but no CRC in other headers. a residual error on a header that carries no CRC will result in a damaged header being delivered to upper layers (4). the number of damaged headers delivered to upper layers depends on the ratio of headers with a CRC to headers without one, which is a compressor parameter ๐
5. The protocol ๐
5.1. Data structures ๐งฉ
the ROHC protocol is based on a number of parameters that form part of the negotiated channel state and the per-context state. this section describes some of this state information in an abstract way. implementations can use a different structure for and representation of this state. in particular, negotiation protocols that set up the per-channel state need to establish the information that constitutes the negotiated channel state, but it is not necessary to exchange it in the form described here ๐ท
5.1.1. Per-channel parameters ๐
MAX_CID: Nonnegative integer; highest context ID number to be used by the compressor (note that this parameter is not coupled to, but in effect further constrained by, LARGE_CIDS).
LARGE_CIDS: Boolean; if false, the short CID representation (0 bytes or 1 prefix byte, covering CID 0 to 15) is used; if true, the embedded CID representation (1 or 2 embedded CID bytes covering CID 0 to 16383) is used.
PROFILES: Set of nonnegative integers, each integer indicating a profile supported by the decompressor. The compressor MUST NOT compress using a profile not in PROFILES. ๐ซ
FEEDBACK_FOR: Optional reference to a channel in the reverse direction. If provided, this parameter indicates which channel any feedback sent on this channel refers to (see 5.7.6.1). ๐
MRRU: Maximum reconstructed reception unit. This is the size of the largest reconstructed unit in octets that the decompressor is expected to reassemble from segments (see 5.2.5). Note that this size includes the CRC. If MRRU is negotiated to be 0, no segment headers are allowed on the channel. ๐งฉ
5.1.2. Per-context parameters, profiles ๐ฆ
per-context parameters are established with IR headers (see section 5.2.3). an IR header contains a profile identifier, which determines how the rest of the header is to be interpreted. the profile parameter determines the syntax and semantics of the packet type identifiers and packet types used in conjunction with a specific context. this document describes profiles 0x0000, 0x0001, 0x0002, and 0x0003; further profiles may be defined when ROHC is extended in the future โจ
profile 0x0000 is for sending uncompressed IP packets. See section 5.10.
profile 0x0001 is for RTP/UDP/IP compression, see sections 5.3 through 5.9.
profile 0x0002 is for UDP/IP compression, i.e., compression of the first 12 octets of the UDP payload is not attempted. See section 5.11.
profile 0x0003 is for ESP/IP compression, i.e., compression of the header chain up to and including the first ESP header, but not subsequent subheaders. See section 5.12. ๐
initially, all contexts are in no context state, i.e., all packets referencing this context except IR packets are discarded ๐
โโ๏ธ if defined by a "ROHC over X" document, per-channel negotiation can be used to pre-establish state information for a context (e.g., negotiating profile 0x0000 for CID 15). such state information can also be marked read-only in the negotiation, which would cause the decompressor to discard any IR packet attempting to modify it ๐
5.1.3. Contexts and context identifiers ๐
Okay so every compressed flow has a context ๐, which is basically the state the compressor and decompressor both keep so they can squeeze and unsqueeze headers correctly. Each context gets a context identifier (CID), and the CID rides along with compressed headers and feedback. Main character energy ๐
CID spaces are per channel. CID 3 over channel A and CID 3 over channel B are NOT the same context, even if A and B connect the exact same two nodes ๐. Forward and reverse channels are totally unrelated too, and they don't even need CID spaces of the same size. Independent and iconic ๐
Context info lives in a table, indexed by the CID. The CID space can be negotiated as small (CIDs 0 through 15) or large (CIDs between 0 and 2^14 - 1 = 16383). Whether it's small or large gets sorted out no later than when the channel is established ๐ธ
Here's how the bits work. A small CID of 0 takes zero bits, which is so efficient it's giving ๐ง. A small CID from 1 to 15 is a four-bit field that takes the place of a packet type field (that's the Add-CID) plus four more bits. A large CID uses the encoding from section 4.5.6, capped at two octets โจ
5.2. ROHC packets and packet types ๐ฆ
The packet type indication scheme for ROHC was designed around a few constraints:
- a) it must be possible to use only a limited number of packet sizes;
- b) it must be possible to send feedback information in separate ROHC packets as well as piggybacked on forward packets;
- c) it is desirable to allow elimination of the CID for one packet stream when few packet streams share a channel;
- d) it is anticipated that some packets with large headers may be larger than the MTU of very constrained lower layers.
Those constraints led to a design with a few key pieces ๐:
- optional padding,
- a feedback packet type,
- an optional Add-CID octet, which provides 4 bits of CID, and
- a simple segmentation and reassembly mechanism.
A ROHC packet has the following general format (in the diagram, colons ":" show that a part is optional):
--- --- --- --- --- --- --- ---
: Padding : variable length
--- --- --- --- --- --- --- ---
: Feedback : 0 or more feedback elements
--- --- --- --- --- --- --- ---
: Header : variable, with CID information
--- --- --- --- --- --- --- ---
: Payload :
--- --- --- --- --- --- --- ---
Padding is any number (zero or more) of padding octets. Either of Feedback or Header must be present.
Feedback elements always start with a packet type indication, and they carry internal CID information. You can read all about feedback in section 5.2.2 ๐ซถ.
Header is either a profile-specific header or an IR or IR-DYN header (see sections 5.2.3 and 5.2.4). A Header either:
- does not carry any CID information (which means CID zero), or
- includes one Add-CID Octet (see below), or
- contains embedded CID information of length one or two octets.
Alternatives 1 and 2 only apply to compressed headers on channels where the CID space is small. Alternative 3 only applies to compressed headers on channels where the CID space is large.
Here's the Padding Octet ๐:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 1 0 0 0 0 0 |
+---+---+---+---+---+---+---+---+
And the Add-CID Octet ๐ฆ:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 1 0 | CID |
+---+---+---+---+---+---+---+---+
CID: 0x1 through 0xF means CIDs 1 through 15.
Note: The Padding Octet looks like an Add-CID octet for CID 0. Lowkey confusing, but that's just how it is ๐ญ
A Header either starts with a packet type indication, or it has a packet type indication right after an Add-CID Octet. All Header packet types follow this general format (in the diagram, slashes "/" show variable length) ๐ :
0 x-1 x 7
--- --- --- --- --- --- --- ---
: Add-CID octet : if (CID 1-15) and (small CIDs)
+---+--- --- --- ---+--- --- ---+
| type indication | body | 1 octet (8-x bits of body)
+---+--- ---+---+---+--- --- ---+
: :
/ 0, 1, or 2 octets of CID / 1 or 2 octets if (large CIDs)
: :
+---+---+---+---+---+---+---+---+
/ body / variable length
+---+---+---+---+---+---+---+---+
If a large CID is present, it gets encoded according to section 4.5.6.
5.2.1. ROHC feedback ๐
Feedback carries information from the decompressor back to the compressor. The following main kinds of feedback are supported. Beyond the kind of feedback, extra info can go in profile-specific feedback information too ๐ง.
ACK: acknowledges successful decompression of a packet, which means the context is up-to-date with a high probability โจ.NACK: indicates the decompressor's dynamic context is out of sync. It gets generated when several successive packets fail to decompress correctly ๐ญ.STATIC-NACK: indicates the decompressor's static context isn't valid or hasn't been established yet ๐.
It's anticipated that feedback to the compressor can be realized in many ways, depending on the lower layer. The exact details of how feedback is realized are going to be specified in a "ROHC over X" document, one for each lower layer X in question. For example, feedback might be realized using:
- lower-layer specific mechanisms,
- a dedicated feedback-only channel, realized for example by the lower layer providing a way to indicate that a packet is a feedback packet,
- a dedicated feedback-only channel, where the timing of the feedback provides information about which compressed packet caused the feedback,
- interspersing feedback packets among normal compressed packets going in the same direction as the feedback (lower layers do not indicate feedback),
- piggybacking feedback information in compressed packets going in the same direction as the feedback (this technique may reduce the per-feedback overhead),
- interspersing and piggybacking on the same channel, i.e., both 4 and 5.
Alternatives 1-3 don't put any particular requirements on the ROHC packet type scheme. Alternatives 4-6 do, though. The ROHC packet type scheme was designed to allow alternatives 4-6 (these may be used for example over PPP) ๐ซง:
- a) The ROHC scheme provides a feedback packet type. That packet type is able to carry variable-length feedback information.
- b) The feedback information sent on a particular channel is passed to, and interpreted by, the compressor associated with feedback on that channel. So the feedback information must contain CID information if the associated compressor can use more than one context. The ROHC feedback scheme requires that a channel carries feedback to at most one compressor. How a compressor gets associated with feedback on a particular channel needs to be defined in a "ROHC over X" document ๐.
- c) The ROHC feedback information format is octet-aligned, i.e., it starts at an octet boundary, so it can be used over a dedicated feedback channel, as in 2 above ๐.
- d) To allow piggybacking (5 above), you can work out the length of feedback information by looking at the first few octets of the feedback. This lets the decompressor pass piggybacked feedback information to the associated same-side compressor without understanding its format. The length information decouples the decompressor from the compressor, so the decompressor can process the compressed header right away without waiting for the compressor to hand it back after parsing the feedback information ๐ธโจ
5.2.2. ROHC feedback format โจ
okay so feedback on a ROHC channel is a bunch of feedback elements stuck together back-to-back ๐ซถ Each one is laid out like this:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 1 1 0 | Code | feedback type octet
+---+---+---+---+---+---+---+---+
: Size : if Code = 0
+---+---+---+---+---+---+---+---+
/ feedback data / variable length
+---+---+---+---+---+---+---+---+
- Code:
0means aSizeoctet is present.1-7means the size of the feedback data field in octets. - Size: an optional octet that gives the size of the feedback data field in octets.
- feedback data: profile-specific feedback info. It includes CID information.
The total size of the feedback data field can be figured out by the decompressor when it arrives, just by looking at the Code field and maybe the Size field too ๐ That explicit length info is what lets you piggyback feedback and stack more than one feedback element in a single packet ๐
When the decompressor knows the size, it strips off the feedback type octet and the Size field (if there is one) and hands the rest to the same-side associated compressor, along with the size. The feedback data the compressor gets has this structure (feedback sent on a dedicated feedback channel MAY also use this format):
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
: Add-CID octet : if for small CIDs and (CID != 0)
+---+---+---+---+---+---+---+---+
: :
/ large CID (4.5.6 encoding) / 1-2 octets if for large CIDs
: :
+---+---+---+---+---+---+---+---+
/ feedback /
+---+---+---+---+---+---+---+---+
The large CID, if there is one, is encoded according to section 4.5.6. The CID info in feedback data tells you which CID the packet stream that feedback is for belongs to. Heads up: the LARGE_CIDS parameter that decides whether a large CID shows up is taken from the channel state of the receiving compressor's channel, NOT from the channel carrying the feedback ๐
It is REQUIRED that the feedback field have either of the following two formats:
FEEDBACK-1
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| profile specific information | 1 octet
+---+---+---+---+---+---+---+---+
FEEDBACK-2
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
|Acktype| |
+---+---+ profile specific / at least 2 octets
/ information |
+---+---+---+---+---+---+---+---+
The Acktype values are:
Acktype: 0 = ACK
1 = NACK
2 = STATIC-NACK
3 is reserved (MUST NOT be used. Otherwise unparseable.)
Here's the logic the compressor can use to parse the feedback field ๐
- If for large CIDs, the feedback will always start with a CID encoded according to section 4.5.6. If the first bit is 0, the CID uses one octet. If the first bit is 1, the CID uses two octets.
- If for small CIDs, and the size is one octet, the feedback is a FEEDBACK-1.
- If for small CIDs, and the size is larger than one octet, and the feedback starts with the two bits 11, the feedback starts with an Add-CID octet. If the size is 2, it is followed by FEEDBACK-1. If the size is larger than 2, the Add-CID is followed by FEEDBACK-2.
- Otherwise, there is no Add-CID octet, and the feedback starts with a FEEDBACK-2.
5.2.3. ROHC IR packet type ๐ง
The IR header is the one that ties a CID to a profile, and it usually initializes the context too โจ It can also refresh (parts of) the context. Here's the general format:
0 1 2 3 4 5 6 7
--- --- --- --- --- --- --- ---
: Add-CID octet : if for small CIDs and (CID != 0)
+---+---+---+---+---+---+---+---+
| 1 1 1 1 1 1 0 | x | IR type octet
+---+---+---+---+---+---+---+---+
: :
/ 0-2 octets of CID / 1-2 octets if for large CIDs
: :
+---+---+---+---+---+---+---+---+
| Profile | 1 octet
+---+---+---+---+---+---+---+---+
| CRC | 1 octet
+---+---+---+---+---+---+---+---+
| |
/ profile specific information / variable length
| |
+---+---+---+---+---+---+---+---+
- x: profile specific information. It's interpreted according to the profile named in the
Profilefield. - Profile: the profile to associate with the CID. In the IR packet, the profile identifier is shortened to the 8 least significant bits. It picks the highest-numbered profile in the channel state parameter
PROFILESthat matches those 8 LSBs. - CRC: an 8-bit CRC computed with the polynomial from section 5.9.1. Its coverage depends on the profile, but it MUST cover at least the initial part of the packet, ending with the
Profilefield. Any info that initializes the decompressor's context should be protected by the CRC ๐ - Profile specific information: the contents of this part of the IR packet are defined by each individual profile. It's interpreted according to the profile named in the
Profilefield.
5.2.4. ROHC IR-DYN packet type ๐ธ
Unlike the IR header, the IR-DYN header can never initialize an uninitialized context ๐ โโ๏ธ However, it can redefine which profile is associated with a context. See for example 5.11 (ROHC UDP) and 5.12 (ROHC ESP). So this type needs to be reserved at the framework level. The IR-DYN header usually initializes or refreshes parts of a context, typically the dynamic part. Here's the general format:
0 1 2 3 4 5 6 7
--- --- --- --- --- --- --- ---
: Add-CID octet : if for small CIDs and (CID != 0)
+---+---+---+---+---+---+---+---+
| 1 1 1 1 1 0 0 0 | IR-DYN type octet
+---+---+---+---+---+---+---+---+
: :
/ 0-2 octets of CID / 1-2 octets if for large CIDs
: :
+---+---+---+---+---+---+---+---+
| Profile | 1 octet
+---+---+---+---+---+---+---+---+
| CRC | 1 octet
+---+---+---+---+---+---+---+---+
| |
/ profile specific information / variable length
| |
+---+---+---+---+---+---+---+---+
- Profile: the profile to associate with the CID. It's abbreviated the same way as in IR packets.
- CRC: an 8-bit CRC computed with the polynomial from section 5.9.1. Its coverage depends on the profile, but it MUST cover at least the initial part of the packet, ending with the
Profilefield. Any info that initializes the decompressor's context should be protected by the CRC ๐ก๏ธ - Profile specific information: this part of the IR packet is defined by each individual profile. It's interpreted according to the profile named in the
Profilefield.
5.2.5. ROHC segmentation ๐ฆ
Some link layers work a whole lot better when the set of different packet sizes they carry stays small ๐ฆ For those links, the sizes are usually picked to carry frequently occurring packets efficiently, and less frequent packets may get padded up to the next larger size. The link layer might be limited in how big a packet it can offer in this efficient mode, or you might want to ask for only a limited largest size. To handle the occasional packet that's bigger than that negotiated largest size, ROHC defines a simple segmentation protocol ๐
5.2.5.1. Segmentation usage considerations
The segmentation protocol defined in ROHC is not particularly efficient ๐ It isn't meant to replace link layer segmentation functions. Those SHOULD be used whenever they're available and efficient for the task at hand.
ROHC segmentation should only be used for occasional packets whose sizes are bigger than what's efficient to carry, for example because of exceptionally large ROHC headers. The segmentation scheme was designed to reduce packet size variations that can come from outliers in the header size distribution. In other cases, segmentation should be done at lower layers. The segmentation scheme should only be used for packet sizes larger than the maximum size in the allowed set of sizes from the lower layers.
In summary: ROHC segmentation should be used with a relatively low frequency in the packet flow. If that can't be guaranteed, segmentation should be done at lower layers ๐
5.2.5.2. Segmentation protocol
Segment Packet
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 1 1 1 1 1 | F |
+---+---+---+---+---+---+---+---+
/ Segment / variable length
+---+---+---+---+---+---+---+---+
- F: Final bit. If set, it indicates that this is the last segment of a reconstructed unit.
The segment header may be preceded by padding octets and/or feedback. It never carries a CID.
All segment header packets for one reconstructed unit have to be sent consecutively on a channel. That means any non-segment-header packet that follows a nonfinal segment header aborts the reassembly of the current reconstructed unit, and makes the decompressor throw away the nonfinal segments it has received on this channel so far ๐๏ธ When a final segment header arrives, the decompressor glues together the segment carried in that packet and any nonfinal segments that came right before it into a single reconstructed unit, in the order they were received โจ The reconstructed unit looks like this:
Reconstructed Unit
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| |
/ Reconstructed ROHC packet / variable length
| |
+---+---+---+---+---+---+---+---+
/ CRC / 4 octets
+---+---+---+---+---+---+---+---+
The decompressor uses the CRC to validate the reconstructed unit. It uses the FCS-32 algorithm with this generator polynomial: x^0 + x^1 + x^2 + x^4 + x^5 + x^7 + x^8 + x^10 + x^11 + x^12 + x^16 + x^22 + x^23 + x^26 + x^32 [HDLC]. If the reconstructed unit is 4 octets or less, or if the CRC fails, or if it's larger than the channel parameter MRRU (see 5.1.1), the decompressor MUST discard the reconstructed unit ๐ซ
If the CRC succeeds, the reconstructed ROHC packet is read as a ROHC Header, optionally followed by a payload. Note that this means there can be no padding and no feedback in the reconstructed unit, and that the CID comes from the initial octets of the reconstructed unit ๐ง
(It should be noted that the ROHC segmentation protocol was inspired by SEAL by Steve Deering et al., which later became ATM AAL5. The same arguments for not having sequence numbers in the segments, but instead providing a strong CRC in the reconstructed unit, apply here as well. Note that, as a result of this protocol, there is no way in ROHC to make any use of a segment that has residual bit errors.) ๐
5.2.6. ROHC initial decompressor processing ๐โจ
okay so, the framework reserves some packet types for itself, and profiles get to use whatever is left over ๐
1110: Padding or Add-CID octet
11110: Feedback
11111000: IR-DYN packet
1111110: IR packet
1111111: Segment
Other packet types can be used at will by individual profiles ๐ฆ
Next up is the initial decompressor processing outline. It's basically the step-by-step way a decompressor figures out what a received ROHC packet actually contains when it first lands ๐ The steps are:
- If the first octet is a Padding Octet (11100000), strip away all initial Padding Octets and goto next step.
- If the first remaining octet starts with 1110, it is an Add-CID octet: remember the Add-CID octet; remove the octet.
- If the first remaining octet starts with 11110, and an Add-CID octet was found in step 2), an error has occurred; the header MUST be discarded without further action. ๐ญ
- If the first remaining octet starts with 11110, and an Add-CID octet was not found in step 2), this is feedback: find the size of the feedback data, call it s; remove the feedback type octet; remove the Size octet if Code is 0; send feedback data of length s to the same-side associated compressor; if packet exhausted, stop; otherwise goto 2).
- If the first remaining octet starts with 1111111, this is a segment: attempt reconstruction using the segmentation protocol (5.2.5). If a reconstructed packet is not produced, this finishes the processing of the original packet. If a reconstructed packet is produced, it is fed into step 1) above. Padding, segments, and feedback are not allowed in reconstructed packets, so when processing them, steps 1), 4), and 5) are modified so that the packet is discarded without further action when their conditions match. ๐งฉ
- Here, it is known that the rest is forward information (unless the header is damaged). ๐
- If the forward traffic uses small CIDs, there is no large CID in the packet. If an Add-CID immediately preceded the packet type (step 2), it has the CID of the Add-CID; otherwise it has CID 0.
- If the forward traffic uses large CIDs, the CID starts with the second remaining octet. If the first bit(s) of that octet are not 0 or 10, the packet MUST be discarded without further action. If an Add-CID octet immediately preceded the packet type (step 2), the packet MUST be discarded without further action. ๐ซ
- Use the CID to find the context. ๐
- If the packet type is IR, the profile indicated in the IR packet determines how it is to be processed. If the CRC fails to verify the packet, it MUST be discarded. If a profile is indicated in the context, the logic of that profile determines what, if any, feedback is to be sent. If no profile is noted in the context, no further action is taken.
- If the packet type is IR-DYN, the profile indicated in the IR-DYN packet determines how it is to be processed.
a) If the CRC fails to verify the packet, it MUST be discarded. If a profile is indicated in the context, the logic of that profile determines what, if any, feedback is to be sent. If no profile is noted in the context, no further action is taken.
b) If the context has not been initialized by an IR packet, the packet MUST be discarded. The logic of the profile indicated in the IR-DYN header (if verified by the CRC), determines what, if any, feedback is to be sent.
- Otherwise, the profile noted in the context determines how the rest of the packet is to be processed. If the context has not been initialized by an IR packet, the packet MUST be discarded without further action. ๐ธ
The procedure for finding the size of the feedback data goes like this. Look at the three bits right after the feedback packet type. When those bits are the values below, the result is:
1-7: the size of the feedback data is given by the bits;
0: a Size octet, which explicitly gives the size of the
feedback data, is present after the feedback type octet.
5.2.7. ROHC RTP packet formats from compressor to decompressor ๐
ROHC RTP uses three packet types to identify compressed headers, and two for initialization/refresh. The format of a compressed packet can depend on the mode. So, when it's needed, there's a naming scheme to uniquely identify the format:
<modes format is used in>-<packet type number>-<some property>
Examples are UOR-2 and R-1. For exact formats of the packet types, see section 5.7 ๐
Packet type zero: R-0, R-0-CRC, UO-0.
This is the minimal one ๐ท It's used when the decompressor already knows the parameters of all SN-functions, and the header being compressed sticks to those functions. So only the W-LSB encoded RTP SN has to be communicated.
R-mode: Only if a CRC is present (packet type R-0-CRC) may the header be used as a reference for subsequent decompression.
U-mode and O-mode: A small CRC is present in the UO-0 packet.
Packet type 1: R-1, R-1-ID, R-1-TS, UO-1, UO-1-ID, UO-1-TS.
This one shows up when the number of bits needed for the SN is more than packet type zero can hold, or when the parameters of the SN-functions for RTP TS or IP-ID change ๐
R-mode: R-1-* packets are not used as references for subsequent decompression. Values for other fields than the RTP TS or IP-ID can be communicated using an extension, but they do not update the context.
U-mode and O-mode: Only the values of RTP SN, RTP TS and IP-ID can be used as references for future compression. Nonupdating values can be provided for other fields using an extension (UO-1-ID).
Packet type 2: UOR-2, UOR-2-ID, UOR-2-TS
This packet type can change the parameters of any SN-function, except those for most static fields. Headers of packets sent using packet type 2 can be used as references for subsequent decompression ๐
Packet type: IR
This packet type communicates the static part of the context, i.e., the value of the constant SN-functions. It can optionally also communicate the dynamic part of the context, i.e., the parameters of the nonconstant SN-functions.
Packet type: IR-DYN
This packet type communicates the dynamic part of the context, i.e., the parameters of nonconstant SN-functions.
5.2.8. Parameters needed for mode transition in ROHC RTP ๐ฆ
The packet types IR (with dynamic information), IR-DYN, and UOR-2 are common for all modes. They can carry a mode parameter which can take the values U = Unidirectional, O = Bidirectional Optimistic, and R = Bidirectional Reliable.
Feedback of types ACK, NACK, and STATIC-NACK carry sequence numbers, and feedback packets can also carry a mode parameter indicating the desired compression mode: U, O, or R.
As a shorthand, the notation PACKET(mode) is used to indicate which mode value a packet carries. For example, an ACK with mode parameter R is written ACK(R), and an UOR-2 with mode parameter O is written UOR-2(O). โจ
5.3. Operation in Unidirectional mode ๐
5.3.1. Compressor states and logic (U-mode) ๐ง
Below is the state machine for the compressor in Unidirectional mode. The details of the transitions between states and the compression logic come after the figure.
Optimistic approach
+------>------>------>------>------>------>------>------>------+
| |
| Optimistic approach Optimistic approach |
| +------>------>------+ +------>------>------+ |
| | | | | |
| | v | v v
+----------+ +----------+ +----------+
| IR State | | FO State | | SO State |
+----------+ +----------+ +----------+
^ ^ | ^ | |
| | Timeout | | Timeout / Update | |
| +------<------<------+ +------<------<------+ |
| |
| Timeout |
+------<------<------<------<------<------<------<------<------+
5.3.1.1. State transition logic (U-mode) ๐
The transition logic for compression states in Unidirectional mode rests on three principles: the optimistic approach principle, timeouts, and the need for updates.
5.3.1.1.1. Optimistic approach, upwards transition ๐
Moving up to a higher compression state in Unidirectional mode follows the optimistic approach principle. This means the compressor transits to a higher compression state when it is fairly confident that the decompressor has received enough information to correctly decompress packets sent according to the higher compression state.
When the compressor is in the IR state, it stays there until it assumes that the decompressor has correctly received the static context information. For transition from the FO to the SO state, the compressor should be confident that the decompressor has all parameters needed to decompress according to a fixed pattern.
The compressor normally gets its confidence about decompressor status by sending several packets with the same information according to the lower compression state. If the decompressor receives any of these packets, it will be in sync with the compressor. The number of consecutive packets to send for confidence is not defined in this document. ๐คทโโ๏ธ
5.3.1.1.2. Timeouts, downward transition โฐ
When the optimistic approach is taken as described above, there is always a possibility of failure, since the decompressor may not have received sufficient information for correct decompression. Therefore, the compressor MUST periodically transit to lower compression states. Periodic transition to the IR state SHOULD be carried out less often than transition to the FO state. Two different timeouts SHOULD therefore be used for these transitions. For an example of how to implement periodic refreshes, see [IPHC] chapters 3.3.1-3.3.2. ๐
5.3.1.1.3. Need for updates, downward transition ๐ฝ
In addition to the downward state transitions carried out due to periodic timeouts, the compressor must also immediately transit back to the FO state when the header to be compressed does not conform to the established pattern. ๐จ
5.3.1.2. Compression logic and packets used (U-mode) ๐ฏ
The compressor chooses the smallest possible packet format that can communicate the desired changes, and has the required number of bits for W-LSB encoded values. ๐
5.3.1.3. Feedback in Unidirectional mode ๐
The Unidirectional mode of operation is designed for links where a feedback channel is not available. If a feedback channel is available, however, the decompressor MAY send an acknowledgment of successful decompression with the mode parameter set to U (send an ACK(U)). When the compressor receives such a message, it MAY disable (or increase the interval between) periodic IR refreshes. ๐ซถ
5.3.2. Decompressor states and logic (U-mode) ๐ฆ
Here's the state machine for the decompressor in Unidirectional mode ๐ . The transitions and decompression logic get explained right after the diagram โจ
Success
+-->------>------>------>------>------>--+
| |
No Static | No Dynamic Success | Success
+-->--+ | +-->--+ +--->----->---+ +-->--+
| | | | | | | | |
| v | | v | v | v
+--------------+ +----------------+ +--------------+
| No Context | | Static Context | | Full Context |
+--------------+ +----------------+ +--------------+
^ | ^ |
| k_2 out of n_2 failures | | k_1 out of n_1 failures |
+-----<------<------<-----+ +-----<------<------<-----+
5.3.2.1. State transition logic (U-mode)
Okay so successful decompression always moves the decompressor up to the Full Context state ๐. If decompression keeps failing, though, the decompressor gets forced to drop down to a lower state ๐. Also, in the No Context and Static Context states, it doesn't even try to decompress headers unless the packet itself carries enough info to do it on its own ๐.
5.3.2.2. Decompression logic (U-mode)
Decompression in Unidirectional mode happens in three steps, and each one gets its own section below ๐ง
5.3.2.2.1. Decide whether decompression is allowed
In the Full Context state, decompression can be attempted no matter what kind of packet shows up ๐. The other states are pickier though. In the No Context state, only IR packets (the ones carrying the static information fields) can be decompressed ๐ง. In the Static Context state, only packets with a 7- or 8-bit CRC can be decompressed, which means IR, IR-DYN, or UOR-2 packets ๐. If decompression isn't allowed, the packet gets thrown out, unless the optional delayed decompression mechanism is in use, which you can read about in section 6.1 ๐
5.3.2.2.2. Reconstruct and verify the header
Time to rebuild the header ๐ ๏ธ The decompressor grabs the header info it already has stored in the context, then updates it with whatever came in the current header. If the rebuilt header fails the CRC check, these updates MUST be undone.
The sequence number gets rebuilt by swapping the SN LSBs in the context for the ones received in the header. Then it gets checked to make sure it sits inside the interpretation interval, by comparing it with a previously reconstructed reference value v_ref (see section 4.5.1) ๐. If it's outside that interval, the decompressor adds N x interval_size to the reconstructed value to bring it back inside. Note that N can be negative ๐.
If the RTP Timestamp and IP Identification fields aren't in the received header, they're supposed to get calculated from the sequence number โจ The IP Identifier usually goes up by the same delta as the sequence number, and the timestamp goes up by that same delta times a fixed value ๐. For more on how these fields are encoded in compressed headers, check chapters 4.5.3 and 4.5.5 ๐ฆ
When working in Unidirectional mode, every compressed header carries a CRC, and that CRC MUST be used to verify decompression ๐ฅ
5.3.2.2.3. Actions upon CRC failure
This section is written so that it applies to all modes ๐
A CRC mismatch can come from one or more of these things ๐
- residual bit errors in the current header
- a damaged context due to residual bit errors in previous headers
- many consecutive packets being lost between compressor and decompressor (this may cause the LSBs of the SN in compressed packets to be interpreted wrongly, because the decompressor has not moved the interpretation interval for lack of input -- in essence, a kind of context damage).
Cases 2 and 3 don't apply to IR packets, and case 3 doesn't apply to IR-DYN packets ๐ โโ๏ธ The 3-bit CRC that shows up in some header formats will eventually catch context damage reliably, because the chance of undetected context damage drops off exponentially with each new header processed ๐ Residual bit errors in the current header, though, only get caught with good probability, not reliably ๐ฌ
When the CRC mismatch comes from residual bit errors in the current header (case 1), the decompressor should stay in its current state so it doesn't lose subsequent packets for no reason ๐ซถ When the mismatch comes from a damaged context (case 2), the decompressor should try to repair the context locally. If that local repair fails, it must move to a lower state so it doesn't deliver incorrect headers ๐ซ When the mismatch comes from prolonged loss (case 3), the decompressor might try some extra decompression attempts. Note that case 3 never happens in R-mode ๐
Here's what MUST happen when a CRC check fails ๐
First, try to figure out whether SN LSB wraparound (case 3) is likely, and if it is, attempt a correction. The algorithm in section 5.3.2.2.4 MAY be used for this. If a different algorithm is used instead, it MUST have at least as high a rate of correct repairs as the one in 5.3.2.2.4. (This step doesn't apply to R-mode ๐)
Second, if the previous step didn't attempt a correction, the decompressor should try a repair under the assumption that the reference SN got updated incorrectly. The algorithm in section 5.3.2.2.5 MAY be used for this. If a different algorithm is used, it MUST have at least as high a rate of correct repairs as the one in 5.3.2.2.5. (This step doesn't apply to R-mode either ๐ธ)
If both of those steps fail, additional decompression attempts SHOULD NOT be made ๐ There are two possible reasons for the CRC failure: case 1, or unrecoverable context damage. There's no way to know for sure which one it is ๐คทโโ๏ธ So these rules apply:
- a. When CRC checks only fail occasionally, assume residual errors in the current header and just discard the packet. NACKs SHOULD NOT be sent at this time ๐
- b. In the
Full Contextstate: when the CRC check of k_1 out of the last n_1 decompressed packets has failed, context damage SHOULD be assumed and a NACK SHOULD be sent in O- and R-mode. The decompressor moves to theStatic Contextstate and throws away all packets until an update (IR, IR-DYN, UOR-2) that passes the CRC check comes in ๐๏ธ - c. In the
Static Contextstate: when the CRC check of k_2 out of the last n_2 updates (IR, IR-DYN, UOR-2) has failed, static context damage SHOULD be assumed and a STATIC-NACK is sent in O- and R-mode. The decompressor moves to theNo Contextstate ๐ - d. In the
No Contextstate: the decompressor discards all packets until a static update (IR) that passes the CRC check comes in ๐ฅบ (In O-mode and R-mode, feedback gets sent according to sections 5.4.2.2 and 5.5.2.2, respectively.)
Quick note: the right values for k_1, n_1, k_2, and n_2 depend on the residual error rate of the link ๐ก When that error rate is close to zero, k_1 = n_1 = k_2 = n_2 = 1 may be a good fit โจ
5.3.2.2.4. Correction of SN LSB wraparound
When lots of consecutive packets get lost, there's a risk of sequence number LSB wraparound ๐ฑ That means the SN LSBs get interpreted wrongly, because the interpretation interval hasn't moved since there was no input. The decompressor might be able to spot this and avoid context damage by using a local clock โฐ The following algorithm MAY be used:
- a. The decompressor notes the arrival time,
a(i), of each incoming packeti. Arrival times of packets where decompression fails get thrown out ๐๏ธ - b. When decompression fails, the decompressor computes
INTERVAL = a(i) - a(i - 1), which is the time that passed between the arrival of the previous, correctly decompressed packet and the current one โณ - c. If wraparound has happened,
INTERVALwill match at least2^kinter-packet times, wherekis the number of SN bits in the current header. Using an estimate of the packet inter-arrival time (found with something like a moving average of arrival times,TS_STRIDE, orTS_TIME), the decompressor judges whetherINTERVALcan actually correspond to2^kinter-packet times ๐งฎ - d. If
INTERVALis judged to be at least2^kpacket inter-arrival times, the decompressor adds2^kto the reference SN and tries to decompress the packet using that new reference SN ๐ - e. If this decompression succeeds, the decompressor updates the context but SHOULD NOT deliver the packet to upper layers. The following packet also gets decompressed and updates the context if its CRC succeeds, but SHOULD be discarded. If decompressing the third packet with the new context also succeeds, the context repair counts as successful, and this packet and all the subsequent decompressed packets get delivered to upper layers ๐
- f. If any of the three decompression attempts in d. and e. fails, the decompressor discards the packets and acts according to rules a) through c) of section 5.3.2.2.3 ๐ต
With this mechanism, the decompressor may be able to repair the context after excessive loss, at the cost of discarding two packets ๐
5.3.2.2.5. Repair of incorrect SN updates
okay so the CRC is only so long, which means it can miss residual errors in the compressed header ๐ญ The broken decompressed packet can literally end up with the same CRC as the real uncompressed packet ๐ Then the wrong header updates the context, and the reference SN used in W-LSB decoding goes off track, because it gets updated for every successfully decompressed header of certain types. Yikes ๐
In this situation, the decompressor will catch the incorrect decompression of the following packet with high probability ๐ It just doesn't know why it failed. This mechanism lets the decompressor figure out whether an earlier packet updated the context incorrectly and, if so, try to repair it ๐ฉนโจ
- a. The decompressor keeps two decompressed sequence numbers: the last one (
ref 0) and the one before that (ref -1) ๐ - b. When a compressed header arrives, the SN (
SN curr1) is decompressed usingref 0as the reference. The other header fields are decompressed using this decompressedSN curr1. (This is part of the normal decompression procedure before any CRC test failures.) ๐ - c. If the decompressed header from b. passes the CRC test, the references shift like this:
ref -1 = ref 0
ref 0 = SN curr1
- d. If the header from b. does not pass the CRC test, and the SN (
SN curr2) generated usingref -1as the reference is different fromSN curr1, an extra decompression attempt is made based onSN curr2as the decompressed SN ๐ - e. If the header from b. does not pass the CRC test and
SN curr2is the same asSN curr1, another attempt isn't useful, so it isn't made ๐ โโ๏ธ - f. If the decompressed header from d. passes the CRC test,
ref -1stays the same andref 0is set toSN curr2๐ฆ - g. If the decompressed header from d. does not pass the CRC test, the decompressor acts according to rules a) through c) of section 5.3.2.2.3 ๐ซถ
The whole point of this algorithm is to repair the context ๐ ๏ธโจ If the header from d. passes the CRC test, the references are updated according to f., but two more headers MUST also be successfully decompressed before the repair counts as a success ๐ Of those three successful headers, the first two SHOULD be discarded and only the third gets delivered to upper layers ๐ And if decompression of any of the three headers fails, the decompressor MUST discard that header and the previously generated headers, then act according to rules a) through c) of section 5.3.2.2.3. Periodt ๐
5.3.2.3. Feedback in Unidirectional mode
To boost performance in Unidirectional mode over a link that does have a feedback channel ๐, the decompressor MAY send an acknowledgment when decompression succeeds. Setting the mode parameter in the ACK packet to U tells the compressor to stay in Unidirectional mode ๐ฆ When it receives an ACK(U), the compressor should reduce how often it sends IR packets, since the static information has been received correctly. It is not required to stop sending IR packets though ๐ If IR packets keep arriving, the decompressor MAY repeat the ACK(U), but it SHOULD NOT repeat the ACK(U) continuously ๐ซ
5.4. Operation in Bidirectional Optimistic mode
5.4.1. Compressor states and logic (O-mode)
Here's the state machine for the compressor in Bidirectional Optimistic mode ๐ง The details of each state, state transitions, and compression logic come right after the figure ๐
Optimistic approach / ACK
+------>------>------>------>------>------>------>------>------+
| |
| Optimistic appr. / ACK Optimistic appr. /ACK ACK |
| +------>------>------+ +------>--- -->-----+ +->--+
| | | | | | |
| | v | v | v
+----------+ +----------+ +----------+
| IR State | | FO State | | SO State |
+----------+ +----------+ +----------+
^ ^ | ^ | |
| | STATIC-NACK | | NACK / Update | |
| +------<------<------+ +------<------<------+ |
| |
| STATIC-NACK |
+------<------<------<------<------<------<------<------<------+
5.4.1.1. State transition logic
The transition logic for compression states in Bidirectional Optimistic mode has a lot in common with Unidirectional mode ๐ค The optimistic approach principle and the transitions caused by the need for updates work the same way as described in chapter 5.3.1. The big difference is that Optimistic mode has no timeouts ๐ โโ๏ธ Instead, it uses feedback from decompressor to compressor for transitions in the backward direction, plus an OPTIONAL improved forward transition โจ
5.4.1.1.1. Negative acknowledgments (NACKs), downward transition
Negative acknowledgments (NACKs), also called context requests, make the periodic updates from Unidirectional mode unnecessary ๐ When a NACK arrives, the compressor goes back to the FO state and sends updates (IR-DYN, UOR-2, or possibly IR) to the decompressor ๐ NACKs carry the SN of the latest packet successfully decompressed, and the compressor MAY use this info to figure out which fields need updating ๐ต๏ธโโ๏ธ
Similarly, when a STATIC-NACK packet arrives, the compressor transits back to the IR state ๐
5.4.1.1.2. Optional acknowledgments, upwards transition
On top of NACKs, positive feedback (ACKs) MAY also be used for UOR-2 packets in Bidirectional Optimistic mode ๐ธ When an ACK for an updating packet comes in, the compressor knows the decompressor got that packet, so it can move to a higher compression state right away ๐ This is optional, so a compressor MUST NOT expect to get such ACKs at the start ๐
The compressor MAY use this algorithm to decide when to expect ACKs for UOR-2 packets. An update event is when a run of UOR-2 headers gets sent to communicate an irregularity in the packet stream. When ACKs have arrived for k_3 out of the last n_3 update events, the compressor will expect ACKs. A compressor that expects ACKs will repeat updates (possibly not in every packet) until an ACK shows up ๐
5.4.1.2. Compression logic and packets used
The compression logic is the same for Bidirectional Optimistic mode as it is for Unidirectional mode (see section 5.3.1.2) ๐
5.4.2. Decompressor states and logic (O-mode)
The decompression states and state transition logic are the same as the Unidirectional case (see section 5.3.2) ๐ฆ What's different is the decompression and feedback logic ๐ซถ
5.4.2.1. Decompression logic, timer-based timestamp decompression
In Bidirectional mode (or if the compressor has some other way to get the decompressor's clock resolution and the link's jitter), timer-based timestamp decompression may be used to improve compression efficiency when RTP Timestamp values are proportional to wall-clock time โฐ It uses the mechanisms described in 4.5.4 ๐ง
5.4.2.2. Feedback logic (O-mode)
The feedback logic defines what feedback to send for different events in each state ๐ As mentioned above, there are three main kinds of feedback: ACK, NACK and STATIC-NACK ๐ The logic also refers to the different kinds of packets the decompressor can receive: Initialization and Refresh (IR) packets, IR packets without static information (IR-DYN), and type 2 packets (UOR-2), or type 1 (UO-1) and type 0 packets (UO-0) ๐ฆ A type 0 packet carries a header compressed according to a fixed pattern, while type 1, 2 and IR-DYN packets are used when that pattern is broken ๐งฉ
Below are the rules for which feedback to use when ๐ If the optional feedback is used even once, the decompressor is REQUIRED to keep sending optional feedback for the lifetime of the packet stream ๐
State Actions
NC: - When an IR packet passes the CRC check, send an ACK(O).
- When receiving a type 0, 1, 2 or IR-DYN packet, or an IR
packet has failed the CRC check, send a STATIC-NACK(O),
subject to the considerations at the beginning of section
5.7.6.
SC: - When an IR packet is correctly decompressed, send an ACK(O).
- When a type 2 or an IR-DYN packet is correctly decompressed,
optionally send an ACK(O).
- When a type 0 or 1 packet is received, treat it as a
mismatching CRC and use the logic of section 5.3.2.2.3 to
decide if a NACK(O) should be sent.
- When decompression of a type 2 packet, an IR-DYN packet or an
IR packet has failed, use the logic of section 5.3.2.2.3 to
decide if a STATIC-NACK(O) should be sent.
FC: - When an IR packet is correctly decompressed, send an ACK(O).
- When a type 2 or an IR-DYN packet is correctly decompressed,
optionally send an ACK(O).
- When a type 0 or 1 packet is correctly decompressed, no
feedback is sent.
- When any packet fails the CRC check, use the logic of
5.3.2.2.3 to decide if a NACK(O) should be sent.
5.5. Operation in Bidirectional Reliable mode
5.5.1. Compressor states and logic (R-mode)
Here's the state machine for the compressor in Bidirectional Reliable mode ๐ The details of each state, state transitions, and compression logic come right after the figure ๐
ACK
+------>------>------>------>------>------>------>------+
| |
| ACK ACK | ACK
| +------>------>------+ +------>------>------+ +->-+
| | | | | | |
| | v | v | v
+----------+ +----------+ +----------+
| IR State | | FO State | | SO State |
+----------+ +----------+ +----------+
^ ^ | ^ | |
| | STATIC-NACK | | NACK / Update | |
| +------<------<------+ +------<------<------+ |
| |
| STATIC-NACK |
+------<------<------<------<------<------<------<------<------+
5.5.1.1. State transition logic (R-mode)
The transition logic for compression states in Reliable mode is based on three principles: the secure reference principle, the need for updates, and negative acknowledgments โจ Let's get into it ๐
5.5.1.1.1. Upwards transition โจ
Okay so going up is decided by the secure reference principle ๐. It's basically the same vibe as section 5.3.1.1.1, with one big difference: the compressor only builds its confidence from ACKs that actually make it back from the decompressor ๐ . That's what keeps the compression context and the decompression context in sync, so sync can never get lost just because some packets went missing ๐ซถ.
5.5.1.1.2. Downward transition ๐ง๏ธ
Downward transitions happen when there's a need for updates, or when a NACK or a STATIC_NACK shows up (see sections 5.3.1.1.3 and 5.4.1.1.1, respectively). Ngl, NACKs should rarely happen in R-mode because of the secure reference that's being used, so check out the fourth paragraph of the next section for the deets ๐.
5.5.1.2. Compression logic and packets used (R-mode) ๐
Okay bestie, the compressor starts in the IR state and sends IR packets ๐ฆ. It moves to the FO state once it gets a valid ACK for an IR packet it sent. An ACK only counts as valid if it refers to an SN that was sent earlier, so no sneaky ACKs allowed ๐
โโ๏ธ. In the FO state, it sends the smallest packets that can communicate the changes, following W-LSB or other encoding rules. Those packets could be R-1*, UOR-2, or even IR-DYN โจ.
The compressor moves to the SO state once it has figured out that a string is present (see section 2), and it's also confident that the decompressor has the string parameters ๐ง . That confidence can come from ACKs. For example, in a typical case where the string pattern looks like non-SN-field = SN * slope + offset, one ACK is enough if the decompressor already figured out the slope. In that case only the new offset needs to be synced. Otherwise, two ACKs are needed, because the decompressor needs two headers to learn both the new slope and the new offset ๐. In the SO state, R-0* packets get sent ๐.
A direct jump from the IR state to the SO state is also possible, which is giving main character energy ๐.
The secure reference principle gets enforced in both the compression and decompression logic ๐. This means only a packet carrying a 7- or 8-bit CRC can update the decompression context and be used as a reference for later decompression. So only the field values of update packets need to go into the encoding sliding windows (see 4.5) that the compressor keeps ๐ช.
Here are the reasons the compressor might send update packets:
- The update could bump things up to better compression efficiency (a higher compression state, or smaller packets in the same state) ๐.
- It's desirable to shrink sliding windows. Windows only get shrunk when an ACK is received ๐ช.
The generation of a CRC is infrequent, since it's only needed for an update packet ๐งฎ.
One algorithm for sending update packets could be:
- Let
pRTTbe the number of packets that get sent during one round-trip time. In the SO state, when(64 - pRTT)headers have been sent since the last acked reference, the compressor will sendm1consecutiveR-0-CRCheaders, then send(pRTT - m1)R-0headers. After these headers have been sent, if the compressor hasn't received an ACK for at least one of the previously sentR0-CRC, it keeps sendingR-0-CRCheaders nonstop until it gets a matching ACK ๐.m1is an implementation parameter, and it can be as large aspRTT. - In the FO state,
m2UOR-2headers get sent when there's a pattern change, and after that the compressor sends(pRTT - m2)R-1-*headers ๐.m2is an implementation parameter, and it can be as large aspRTT. At that point, if the compressor hasn't received enough ACKs for the previously sentUOR-2packets to move to the SO state, it can repeat the cycle with the samem2, or repeat the cycle with a largerm2, or sendUOR-2headers continuously (m2=pRTT). The operation stops once the compressor has received enough ACKs to make the transition ๐.
An algorithm for processing ACKs could be:
- When an ACK comes in, the compressor first derives the complete SN (see section 5.7.6.1). Then it searches the sliding window for an update packet with that same SN. If it finds one, that packet is the one being ACKed ๐. Otherwise, the ACK is invalid and MUST be discarded ๐๏ธ.
- It's possible, although unlikely, that residual errors on the reverse channel could make a packet look like a valid ACK feedback. Ya know, the kind of plot twist nobody wants ๐. The compressor may use a local clock to lower the chances of processing one of those mistaken ACKs. After finding the update packet as described above, the compressor can check how much time has passed since that packet was sent. If that time is longer than
RTT_U, or shorter thanRTT_L, the compressor may choose to discard the ACK.RTT_UandRTT_Lcorrespond to the upper bound and lower bound of the RTT, respectively. (These bounds should be chosen appropriately to allow some variation of RTT ๐.) Note that the only side effect of discarding a good ACK is slightly reduced compression efficiency ๐ญ.
5.5.2. Decompressor states and logic (R-mode) ๐
The decompression states and the state transition logic are the same as for the Unidirectional case (see section 5.3.2). What's different is the decompression and feedback logic ๐.
5.5.2.1. Decompression logic (R-mode) ๐งฉ
The rules for when decompression is allowed are the same as for U-mode. The acking scheme in R-mode guarantees that non-decompressible packets are never sent by the compressor, but residual errors can still cause unexpected packets to show up, and decompression should not be attempted on those ๐.
Decompression MUST follow the secure reference principle as described in 5.5.1.2 ๐.
CRC verification is infrequent, since only update packets carry CRCs. A CRC mismatch can only happen because of 1) residual bit errors in the current header, and/or 2) a damaged context from residual bit errors in previous headers or feedback. It's impossible to tell which one is the actual cause, but case 1 is more likely ๐ต๏ธโโ๏ธ. That's because a previous header rebuilt from a damaged packet is unlikely to pass the 7- or 8-bit CRC, and damaged packets are unlikely to produce feedback that damages the context. The decompressor SHOULD act according to section 5.3.2.2.3 when CRCs fail, except that no local repair is performed. Also, note that all the parameter numbers, k_1, n_1, k_2, and n_2, only apply to the update packets (i.e., they exclude R-0 and R-1*) ๐.
5.5.2.2. Feedback logic (R-mode) ๐
The feedback logic for the Bidirectional Reliable mode goes like this:
- When an updating packet (i.e., a packet carrying a 7- or 8-bit CRC) gets decompressed correctly, send an
ACK(R), subject to the sparse ACK mechanism described below โ . - When context damage is detected, send a
NACK(R)if you're in the Full Context state, or aSTATIC-NACK(R)if you're in the Static Context state ๐จ. - In the No Context state, send a
STATIC-NACK(R)when a non-IR packet comes in, subject to the considerations at the beginning of section 5.7.6. The decompressor SHOULD NOT sendSTATIC-NACK(R)when it receives an IR packet that fails the CRC check, because the compressor will stay in the IR state and keep sending IR packets until it gets a valid ACK (see section 5.5.1.2) ๐. - Feedback is never sent for packets that don't update the context (i.e., packets that don't carry a CRC) ๐ โโ๏ธ
A mechanism called "Sparse ACK" can be applied to cut down on the feedback overhead caused by a large RTT ๐ฆ. For a sequence of ACK-triggering events, a minimal set of ACKs MUST be sent:
- For a sequence of
R-0-CRCpackets, the first one MUST be ACKed. - For a sequence of
UOR-2,IR, orIR-DYNpackets, the firstNof them MUST be ACKEd, whereNis the number of ACKs needed to give the compressor confidence that the decompressor has acquired the new string parameters (see second paragraph of 5.5.1.2). If the decompressor can't determine the value ofN, the default value 2 SHOULD be used. If the packets that come after continue the same change pattern of header fields, sparse ACK can be applied. Otherwise, each new pattern MUST be treated as a new sequence, i.e., the firstNpackets that show a new pattern MUST be ACKed ๐งต.
After sending these minimal ACKs, the decompressor MAY choose to ACK only k subsequent packets per RTT ("Sparse ACKs"), where k is an implementation parameter ๐. To be robust against loss of ACKs, k SHOULD be at least 1 ๐ซถ.
To avoid ambiguity at the compressor, the decompressor MUST use the feedback format whose SN field length is equal to or larger than the one in the compressed packet that triggered the feedback ๐.
Context damage is detected according to the principles in 5.3.2.2.3 ๐ต๏ธโโ๏ธ.
When the decompressor can do timer-based compression of the RTP Timestamp (e.g., it has access to a clock with sufficient resolution, and the jitter introduced internally in the receiving node is sufficiently small), it SHOULD signal that it's ready to do timer-based compression of the RTP Timestamp โฐ. The compressor will then make a decision based on what it knows about the channel and the observed properties of the packet stream ๐ง .
5.6. Mode transitions ๐ฆ
The decision to move from one compression mode to another is taken by the decompressor, and the possible mode transitions are shown in the figure below ๐บ๏ธ. Subsequent chapters describe how the transitions are performed, along with the exceptions for the compression and decompression functionality during transitions ๐ .
+-------------------------+
| Unidirectional (U) mode |
+-------------------------+
/ ^ \ ^
/ / Feedback(U) \ \ Feedback(U)
/ / \ \
/ / \ \
Feedback(O) / / Feedback(R) \ \
v / v \
+---------------------+ Feedback(R) +-------------------+
| Optimistic (O) mode | ----------------> | Reliable (R) mode |
| | <---------------- | |
+---------------------+ Feedback(O) +-------------------+
5.6.1. Compression and decompression during mode transitions โจ
okay so, for every context, the compressor and the decompressor each keep a variable holding the current compression mode ๐ That variable is the boss of the whole situation. It decides which packet types get used, which actions get taken, and so on, for that context ๐
safety first, bestie ๐ซถ All feedback sent during a mode transition MUST be protected by a CRC, which means the CRC option MUST be used. And a mode transition MUST NOT be kicked off by feedback that isn't CRC-protected. no exceptions ๐
when ROHC switches between compression modes, the compressor or decompressor sometimes has to chill and limit what it does during the switch ๐ธ Exception parameters spell out which limits apply. The transition sections later on tell you when those parameters get set and what values they take. Here's the full list of mode parameters with their possible values, explanations, and rules:
Compressor side parameters:
C_MODE: can be(U)NIDIRECTIONAL,(O)PTIMISTIC, or(R)ELIABLE๐ฆC_MODEMUST be initialized toU.C_TRANS: can be(P)ENDINGor(D)ONE๐C_TRANSMUST be initialized toD. WhenC_TRANSisP, it is REQUIRED that:- the compressor only uses packet formats that are common to all modes,
- mode information is included in the packets it sends, at least periodically,
- the compressor does not transit to the SO state,
- new mode transition requests get ignored.
Decompressor side parameters:
D_MODE: can be(U)NIDIRECTIONAL,(O)PTIMISTIC, or(R)ELIABLE๐D_MODEMUST be initialized toU.D_TRANS: can be(I)NITIATED,(P)ENDING, or(D)ONE๐งD_TRANSMUST be initialized toD. A mode transition can only be started whenD_TRANSisD. WhileD_TRANSisI, the decompressor sends a NACK or ACK carrying a CRC option for every packet it receives.
5.6.2. Transition from Unidirectional to Optimistic mode ๐
when there's a feedback channel available, the decompressor can decide at any moment to start moving from Unidirectional to Bidirectional Optimistic mode ๐
Any feedback packet carrying a CRC can be used with the mode parameter set to O. The decompressor can then start working in Optimistic mode right away. The compressor moves from Unidirectional to Optimistic mode as soon as it gets any feedback packet that has the mode parameter set to O and passes the CRC check. Here's how the handshake goes:
Compressor Decompressor
----------------------------------------------
| |
| ACK(O)/NACK(O) +-<-<-<-| D_MODE = O
| +-<-<-<-<-<-<-<-+ |
C_MODE = O |-<-<-<-+ |
| |
if that feedback packet gets lost ๐ญ the compressor just keeps going in Unidirectional mode. But the moment any feedback packet reaches it, it switches over to Optimistic mode ๐
5.6.3. From Optimistic to Reliable mode ๐ฅ
switching from Optimistic to Reliable is only allowed after at least one packet has been decompressed correctly, which means the static part of the context is set up ๐ An ACK(R) or a NACK(R) feedback packet carrying a CRC gets sent to kick off the switch. The compressor MUST NOT use packet types 0 or 1 during the transition. Here's the handshake:
Compressor Decompressor
----------------------------------------------
| |
| ACK(R)/NACK(R) +-<-<-<-| D_TRANS = I
| +-<-<-<-<-<-<-<-+ |
C_TRANS = P |-<-<-<-+ |
C_MODE = R | |
|->->->-+ IR/IR-DYN/UOR-2(SN,R) |
| +->->->->->->->-+ |
|->-.. +->->->-| D_TRANS = P
|->-.. | D_MODE = R
| ACK(SN,R) +-<-<-<-|
| +-<-<-<-<-<-<-<-+ |
C_TRANS = D |-<-<-<-+ |
| |
|->->->-+ R-0*, R-1* |
| +->->->->->->->-+ |
| +->->->-| D_TRANS = D
| |
as long as the decompressor hasn't gotten a UOR-2, IR-DYN, or IR packet with the mode transition parameter set to R, it has to stay in Optimistic mode. The compressor must not send packet types 1 or 0 while C_TRANS is P. That means it waits until it gets an ACK for a UOR-2, IR-DYN, or IR packet sent with the mode transition parameter set to R. Once the decompressor receives packet types 0 or 1 after ACKing a UOR-2, IR-DYN, or IR packet, it sets D_TRANS to D ๐
5.6.4. From Unidirectional to Reliable mode ๐ฆ
going from Unidirectional to Reliable follows exactly the same handshake as the one in Section 5.6.3 above ๐
5.6.5. From Reliable to Optimistic mode ๐ธ
either the ACK(O) or the NACK(O) feedback packet can start the switch from Reliable to Optimistic mode ๐ During the transition, the compressor MUST always run in the FO state. Here's the flow:
Compressor Decompressor
----------------------------------------------
| |
| ACK(O)/NACK(O) +-<-<-<-| D_TRANS = I
| +-<-<-<-<-<-<-<-+ |
C_TRANS = P |-<-<-<-+ |
C_MODE = O | |
|->->->-+ IR/IR-DYN/UOR-2(SN,O) |
| +->->->->->->->-+ |
|->-.. +->->->-| D_MODE = O
|->-.. |
| ACK(SN,O) +-<-<-<-|
| +-<-<-<-<-<-<-<-+ |
C_TRANS = D |-<-<-<-+ |
| |
|->->->-+ UO-0, UO-1* |
| +->->->->->->->-+ |
| +->->->-| D_TRANS = D
| |
as long as the decompressor hasn't gotten a UOR-2, IR-DYN, or IR packet with the mode transition parameter set to O, it has to stay in Reliable mode ๐ The compressor must not send packet types 0 or 1 while C_TRANS is P. That means it waits until it gets an ACK for a UOR-2, IR-DYN, or IR packet sent with the mode transition parameter set to O. When the decompressor receives packet types 0 or 1 after ACKing that UOR-2, IR-DYN, or IR packet, it sets D_TRANS to D โจ
5.6.6. Transition to Unidirectional mode ๐
the decompressor can force a switch back to Unidirectional mode whenever it wants ๐ No matter which mode the switch starts from, a three-way handshake MUST be carried out so the compressor side transitions correctly. Here's how it goes:
Compressor Decompressor
----------------------------------------------
| |
| ACK(U)/NACK(U) +-<-<-<-| D_TRANS = I
| +-<-<-<-<-<-<-<-+ |
C_TRANS = P |-<-<-<-+ |
C_MODE = U | |
|->->->-+ IR/IR-DYN/UOR-2(SN,U) |
| +->->->->->->->-+ |
|->-.. +->->->-|
|->-.. |
| ACK(SN,U) +-<-<-<-|
| +-<-<-<-<-<-<-<-+ |
C_TRANS = D |-<-<-<-+ |
| |
|->->->-+ UO-0, UO-1* |
| +->->->->->->->-+ |
| +->->->-| D_TRANS = D, D_MODE= U
after ACKing the first UOR-2(U), IR-DYN(U), or IR(U), the decompressor MUST continue to send feedback with the Mode parameter set to U until it receives packet types 0 or 1 ๐ Keep that feedback coming, bestie, because that's how the compressor knows the coast is clear ๐ซถ
5.7. Packet formats โจ
okay bestie, this is where the actual packets show up ๐ first, here's the notation we're gonna use. it's the vocab for the whole section, so lock it in ๐
bits(X) = the number of bits for field X present in the compressed
header (including extension).
field(X) = the value of field X in the compressed header.
context(X) = the value of field X as established in the context.
value(X) = field(X) if X is present in the compressed header;
= context(X) otherwise.
hdr(X) = the value of field X in the uncompressed or
decompressed header.
Updating properties: Lists the fields in the context that are
directly updated by processing the compressed header. Note
that there may be dependent fields that are implicitly also
updated (e.g., an update to context(SN) often updates
context(TS) as well). See also section 5.2.7.
the fields below show up in a bunch of headers and extensions, so they're basically the main characters of this section ๐ฆ
SN is the compressed RTP Sequence Number. it's compressed with W-LSB, and the interpretation intervals (see section 4.5.1) work like this:
p = 1 if bits(SN) <= 4
p = 2^(bits(SN)-5) - 1 if bits(SN) > 4
IP-ID is the compressed IP-ID field, and it's giving main character energy ๐ IP-ID fields in compressed base headers carry the compressed IP-ID of the innermost IPv4 header whose corresponding RND flag is not 1. the rules below assume the IP-ID is for the innermost IP header. if it's for an outer IP header, the RND2 and NBO2 flags get used instead of RND and NBO.
- if
value(RND) = 0, thenhdr(IP-ID)gets compressed using Offset IP-ID encoding (see section 4.5.5) withp = 0anddefault-slope(IP-ID offset) = 0. - if
value(RND) = 1, then IP-ID is just the uncompressedhdr(IP-ID). it gets passed as extra octets at the very end of the compressed header, after any extensions ๐ธ - if
value(NBO) = 0, the octets ofhdr(IP-ID)get swapped before compression and after decompression. and the value of NBO gets ignored whenvalue(RND) = 1๐
TS is the compressed RTP Timestamp value ๐
- if
value(TIME_STRIDE) > 0, timer-based compression of the RTP Timestamp is used (see section 4.5.4). - if
value(Tsc) = 1, Scaled RTP Timestamp encoding gets used before compression (see section 4.5.3), anddefault-slope(TS) = 1. - if
value(Tsc) = 0, the Timestamp value is compressed as-is, anddefault-slope(TS) = value(TS_STRIDE).
the interpretation intervals (see section 4.5.1) for TS are:
p = 2^(bits(TS)-2) - 1
CRC is the CRC over the original, uncompressed, header ๐ฅ
- 3-bit CRCs use the polynomial of section 5.9.2.
- 7-bit CRCs use the polynomial of section 5.9.2.
- 8-bit CRCs use the polynomial of section 5.9.1.
M is the RTP Marker bit ๐ง Context(M) starts at zero and never gets updated. value(M) = 1 only when field(M) = 1.
here's the general format for a compressed RTP header. it's a whole lineup, so take it in slowly ๐
0 1 2 3 4 5 6 7
--- --- --- --- --- --- --- ---
: Add-CID octet : if for small CIDs and CID 1-15
+---+---+---+---+---+---+---+---+
| first octet of base header | (with type indication)
+---+---+---+---+---+---+---+---+
: :
/ 0, 1, or 2 octets of CID / 1-2 octets if large CIDs
: :
+---+---+---+---+---+---+---+---+
/ remainder of base header / variable number of bits
+---+---+---+---+---+---+---+---+
: :
/ Extension (see 5.7.5) / extension, if X = 1 in base header
: :
--- --- --- --- --- --- --- ---
: :
+ IP-ID of outer IPv4 header + 2 octets, if value(RND2) = 1
: :
--- --- --- --- --- --- --- ---
/ AH data for outer list / variable (see 5.8.4.2)
--- --- --- --- --- --- --- ---
: :
+ GRE checksum (see 5.8.4.4) + 2 octets, if GRE flag C = 1
: :
--- --- --- --- --- --- --- ---
: :
+ IP-ID of inner IPv4 header + 2 octets, if value(RND) = 1
: :
--- --- --- --- --- --- --- ---
/ AH data for inner list / variable (see 5.8.4.2)
--- --- --- --- --- --- --- ---
: :
+ GRE checksum (see 5.8.4.4) + 2 octets, if GRE flag C = 1
: :
--- --- --- --- --- --- --- ---
: :
+ UDP Checksum + 2 octets,
: : if context(UDP Checksum) != 0
--- --- --- --- --- --- --- ---
the order of the fields after the optional extension is the same as the order of the fields in an uncompressed header. so nothing's getting shuffled, which is a whole vibe ๐ซถ
in the sections that come after this, the position of the large CID in the diagrams gets marked with this notation:
+===+===+===+===+===+===+===+===+
whether the UDP Checksum field is present is controlled by the value of the UDP Checksum in the context. if it's nonzero, the UDP Checksum is enabled and sent with each packet. if it's zero, the UDP Checksum is disabled and not sent. Should hdr(UDP Checksum) be nonzero when context(UDP Checksum) is zero, the header cannot be compressed. it must be sent uncompressed or the context reinitialized using an IR packet. Context(UDP Checksum) only gets updated by IR or IR-DYN headers, never by UDP checksums sent in headers of type 2, 1, or 0 ๐
โโ๏ธ
now the hard rules, and yes they're serious ๐ญ
- when an IPv4 header is in the static context, and its corresponding RND flag hasn't been established to be 1, the packet types
R-1andUO-1MUST NOT be used. - when there's no IPv4 header in the static context, or the RND flags for all IPv4 headers in the context have been established to be 1, the packet types
R-1-ID,R-1-TS,UO-1-ID, andUO-1-TSMUST NOT be used. - while in the transient state where an RND flag is being established, the packet types
R-1-ID,R-1-TS,UO-1-ID, andUO-1-TSMUST NOT be used. this means the RND flag(s) of Extension 3 may have to be inspected before you can tell the format of a base header carrying an Extension 3 ๐
5.7.1. Packet type 0: UO-0, R-0, R-0-CRC
packet type 0 is the one where the first bit is 0 ๐
R-0:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 0 | SN |
+===+===+===+===+===+===+===+===+
updating properties: R-0 packets don't update any part of the context. they're chill like that ๐
R-0-CRC:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 1 | SN |
+===+===+===+===+===+===+===+===+
|SN | CRC |
+---+---+---+---+---+---+---+---+
note: the SN field straddles the CID field, which is a little plot twist ๐ฆ
updating properties: R-0-CRC packets update context(RTP Sequence Number).
UO-0:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | SN | CRC |
+===+===+===+===+===+===+===+===+
updating properties: UO-0 packets update the current value of context(RTP Sequence Number) โจ
5.7.2. Packet type 1 (R-mode): R-1, R-1-TS, R-1-ID
packet type 1 is the one where the first bits are 10 ๐
R-1:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 0 | SN |
+===+===+===+===+===+===+===+===+
| M | X | TS |
+---+---+---+---+---+---+---+---+
note: R-1 can't be used if the context has at least one IPv4 header with value(RND) = 0. that's how it stays separate from R-1-ID and R-1-TS ๐
R-1-ID:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 0 | SN |
+===+===+===+===+===+===+===+===+
| M | X |T=0| IP-ID |
+---+---+---+---+---+---+---+---+
note: R-1-ID can't be used if there's no IPv4 header in the context, or if value(RND) and value(RND2) are both 1.
R-1-TS:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 0 | SN |
+===+===+===+===+===+===+===+===+
| M | X |T=1| TS |
+---+---+---+---+---+---+---+---+
note: R-1-TS can't be used if there's no IPv4 header in the context, or if value(RND) and value(RND2) are both 1.
- X: X = 0 means no extension is present; X = 1 means an extension is present.
- T: T = 0 means format R-1-ID; T = 1 means format R-1-TS.
updating properties: R-1* headers don't update any part of the context. so they're basically the quiet best friend ๐ธ
5.7.3. Packet type 1 (U/O-mode): UO-1, UO-1-ID, UO-1-TS
UO-1:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 0 | TS |
+===+===+===+===+===+===+===+===+
| M | SN | CRC |
+---+---+---+---+---+---+---+---+
note: UO-1 can't be used if the context has at least one IPv4 header with value(RND) = 0. this keeps it apart from UO-1-ID and UO-1-TS ๐ง
UO-1-ID:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 0 |T=0| IP-ID |
+===+===+===+===+===+===+===+===+
| X | SN | CRC |
+---+---+---+---+---+---+---+---+
note: UO-1-ID can't be used if there's no IPv4 header in the context, or if value(RND) and value(RND2) are both 1.
UO-1-TS:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 0 |T=1| TS |
+===+===+===+===+===+===+===+===+
| M | SN | CRC |
+---+---+---+---+---+---+---+---+
note: UO-1-TS can't be used if there's no IPv4 header in the context, or if value(RND) and value(RND2) are both 1.
- X: X = 0 means no extension is present; X = 1 means an extension is present.
- T: T = 0 means format UO-1-ID; T = 1 means format UO-1-TS.
updating properties: UO-1* packets update context(RTP Sequence Number). UO-1 and UO-1-TS packets update context(RTP Timestamp). UO-1-ID packets update context(IP-ID). values in extensions don't update the context, except for the ones in other SN, TS, or IP-ID fields. so only the right fields get touched ๐
5.7.4. Packet type 2: UOR-2 โจ
okay bestie, packet type 2 is the one you can spot by its first bits, which are 110 ๐ Here's the UOR-2 layout:
UOR-2
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 0 | TS |
+===+===+===+===+===+===+===+===+
|TS | M | SN |
+---+---+---+---+---+---+---+---+
| X | CRC |
+---+---+---+---+---+---+---+---+
โ ๏ธ UOR-2 can't be used if the context has at least one IPv4 header with value(RND) = 0. That's how it stays different from UOR-2-ID and UOR-2-TS ๐
๐ฆ The TS field straddles the CID field, so the bits literally cross over it. Iconic, honestly.
UOR-2-ID
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 0 | IP-ID |
+===+===+===+===+===+===+===+===+
|T=0| M | SN |
+---+---+---+---+---+---+---+---+
| X | CRC |
+---+---+---+---+---+---+---+---+
๐ UOR-2-ID can't be used if there's no IPv4 header in the context, or if value(RND) and value(RND2) are both 1.
UOR-2-TS
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 0 | TS |
+===+===+===+===+===+===+===+===+
|T=1| M | SN |
+---+---+---+---+---+---+---+---+
| X | CRC |
+---+---+---+---+---+---+---+---+
๐ UOR-2-TS can't be used if there's no IPv4 header in the context, or if value(RND) and value(RND2) are both 1.
Quick decoder ring ๐
- X = 0 means no extension is present, and X = 1 means an extension is present.
- T = 0 means the format is UOR-2-ID, and T = 1 means the format is UOR-2-TS.
Updating properties: all values provided in UOR-2* packets update the context, unless explicitly stated otherwise. โจ
5.7.5. Extension formats
(Heads up: the word "extension" here is about extra info inside the ROHC headers. It has nothing to do with the extension header used in IP, so don't mix them up ๐)
Fields in extensions get concatenated with the matching field in the base compressed header, if there is one. Bits in an extension are less significant than bits in the base compressed header (see section 4.5.7). ๐
The TS field is scaled in all extensions, just like in the base header. The one exception is optional: when using Extension 3, the Tsc flag can say the TS field is not scaled. value(TS_STRIDE) is the scale factor used when scaling TS ๐ฅ
Now the T-bit situation, which is a little plot twist ๐ In the next three extensions, what the fields mean depends on whether there's a T-bit in the base compressed header, and if there is one, on its value. When there's no T-bit, +T and -T both mean TS. That happens when there are no IPv4 headers in the static context, and when every IPv4 header in the static context has its RND flag set (i.e., RND = 1).
If there is a T-bit:
- T = 1 means +T is TS, and -T is IP-ID.
- T = 0 means +T is IP-ID, and -T is TS.
Extension 0:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 0 | SN | +T |
+---+---+---+---+---+---+---+---+
Extension 1:
+---+---+---+---+---+---+---+---+
| 0 1 | SN | +T |
+---+---+---+---+---+---+---+---+
| -T |
+---+---+---+---+---+---+---+---+
Extension 2:
+---+---+---+---+---+---+---+---+
| 1 0 | SN | +T |
+---+---+---+---+---+---+---+---+
| +T |
+---+---+---+---+---+---+---+---+
| -T |
+---+---+---+---+---+---+---+---+
Extension 3 is the main-character extension ๐ It can carry values for fields other than SN, TS, and IP-ID. It has three optional flag octets that signal changes to the IP header(s) and the RTP header, respectively.
Extension 3:
0 1 2 3 4 5 6 7
+-----+-----+-----+-----+-----+-----+-----+-----+
| 1 1 | S |R-TS | Tsc | I | ip | rtp | (FLAGS)
+-----+-----+-----+-----+-----+-----+-----+-----+
| Inner IP header flags | ip2 | if ip = 1
..... ..... ..... ..... ..... ..... ..... .....
| Outer IP header flags | if ip2 = 1
..... ..... ..... ..... ..... ..... ..... .....
| SN | if S = 1
..... ..... ..... ..... ..... ..... ..... .....
/ TS (encoded as in section 4.5.6) / 1-4 octets,
..... ..... ..... ..... ..... ..... ..... ..... if R-TS = 1
| |
/ Inner IP header fields / variable,
| | if ip = 1
..... ..... ..... ..... ..... ..... ..... .....
| IP-ID | 2 octets, if I = 1
..... ..... ..... ..... ..... ..... ..... .....
| |
/ Outer IP header fields / variable,
| | if ip2 = 1
..... ..... ..... ..... ..... ..... ..... .....
| |
/ RTP header flags and fields / variable,
| | if rtp = 1
..... ..... ..... ..... ..... ..... ..... .....
Here's what the Extension 3 bits do ๐ง
- S, R-TS, I, ip, rtp, ip2 just say whether each field shows up, as shown to the right of each field above.
- Tsc = 0 means TS is not scaled. Tsc = 1 means TS is scaled according to section 4.5.3, using value(TS_STRIDE). Context(Tsc) is always 1. If you don't want scaling, the compressor will set TS_STRIDE = 1, which is a neat trick ๐
- SN: see the beginning of section 5.7.
- TS: a variable number of bits of TS, encoded according to section 4.5.6. See the beginning of section 5.7.
- IP-ID: see the beginning of section 5.7.
Inner IP header flags
These correspond to the inner IP header if there are two, and to the single IP header otherwise. ๐ธ
0 1 2 3 4 5 6 7
..... ..... ..... ..... ..... ..... ..... .....
| TOS | TTL | DF | PR | IPX | NBO | RND | ip2 | if ip = 1
..... ..... ..... ..... ..... ..... ..... .....
- TOS, TTL, PR, IPX indicate whether the matching field is present, as shown to the right of the field in question below.
- DF: the Don't Fragment bit of the IP header.
- NBO: shows whether the octets of hdr(IP identifier) for this IP header are swapped before compression and after decompression. NBO = 1 means the octets need not be swapped. NBO = 0 means they are to be swapped. See section 4.5.5. ๐
- RND: shows whether hdr(IP identifier) is not compressed, and is instead sent as-is in compressed headers.
- IP2: shows whether the Outer IP header fields are present. Unless the static context contains two IP headers, IP2 is always zero.
Inner IP header fields
..... ..... ..... ..... ..... ..... ..... .....
| Type of Service/Traffic Class | if TOS = 1
..... ..... ..... ..... ..... ..... ..... .....
| Time to Live/Hop Limit | if TTL = 1
..... ..... ..... ..... ..... ..... ..... .....
| Protocol/Next Header | if PR = 1
..... ..... ..... ..... ..... ..... ..... .....
/ IP extension headers / variable,
..... ..... ..... ..... ..... ..... ..... ..... if IPX = 1
- Type of Service/Traffic Class: that field in the uncompressed IP header (absolute value).
- Time to Live/Hop Limit: that field in the uncompressed IP header.
- Protocol/Next Header: that field in the uncompressed IP header.
- IP extension header(s): handled according to section 5.8.5. ๐
Outer IP header flags
The fields in this part of the Extension 3 header refer to the outermost IP header ๐
0 1 2 3 4 5 6 7
..... ..... ..... ..... ..... ..... ..... ..... | TOS2| TTL2|
DF2 | PR2 |IPX2 |NBO2 |RND2 | I2 | if ip2 = 1
..... ..... ..... ..... ..... ..... ..... .....
These flags match the Inner IP header flags, but they refer to the outer IP header instead of the inner one. The following flag has no counterpart in the Inner IP header flags:
- I2: shows whether the IP-ID field is present.
Outer IP header fields
..... ..... ..... ..... ..... ..... ..... .....
| Type of Service/Traffic Class | if TOS2 = 1
..... ..... ..... ..... ..... ..... ..... .....
| Time to Live/Hop Limit | if TTL2 = 1
..... ..... ..... ..... ..... ..... ..... .....
| Protocol/Next Header | if PR2 = 1
..... ..... ..... ..... ..... ..... ..... .....
/ IP extension header(s) / variable,
..... ..... ..... ..... ..... ..... ..... ..... if IPX2 = 1
| IP-ID | 2 octets,
..... ..... ..... ..... ..... ..... ..... ..... if I2 = 1
The fields in this part of Extension 3 work just like the Inner IP header fields, except they refer to the outer IP header instead of the inner one. The following field has no counterpart among the Inner IP header fields:
- IP-ID: the IP Identifier field of the outer IP header, unless the inner header is an IPv6 header. In that case, I2 is always zero. ๐
RTP header flags and fields
0 1 2 3 4 5 6 7
..... ..... ..... ..... ..... ..... ..... .....
| Mode |R-PT | M | R-X |CSRC | TSS | TIS | if rtp = 1
..... ..... ..... ..... ..... ..... ..... .....
| R-P | RTP PT | if R-PT = 1
..... ..... ..... ..... ..... ..... ..... .....
/ Compressed CSRC list / if CSRC = 1
..... ..... ..... ..... ..... ..... ..... .....
/ TS_STRIDE / 1-4 oct if TSS = 1
..... ..... ..... ..... ..... ..... ..... ....
/ TIME_STRIDE (milliseconds) / 1-4 oct if TIS = 1
..... ..... ..... ..... ..... ..... ..... .....
- Mode: the compression mode. 0 = Reserved, 1 = Unidirectional, 2 = Bidirectional Optimistic, 3 = Bidirectional Reliable. ๐
- R-PT, CSRC, TSS, TIS indicate whether the matching field is present, as shown to the right of each field above.
- R-P: the RTP Padding bit, absolute value (presumed zero if absent).
- R-X: the RTP eXtension bit, absolute value.
- M: see the beginning of section 5.7.
- RTP PT: absolute value of the RTP Payload type field.
- Compressed CSRC list: see section 5.8.1.
- TS_STRIDE: the predicted increment/decrement of the RTP Timestamp field when it changes. Encoded as in section 4.5.6.
- TIME_STRIDE: the predicted time interval in milliseconds between changes in the RTP Timestamp. It also signals that the compressor wants to do timer-based compression of the RTP Timestamp field: see section 4.5.4. Encoded as in section 4.5.6. โฑ๏ธโจ
5.7.5.1. RND flags and packet types ๐
okay so the RND and RND2 flags get changed by sending UOR-2 headers with Extension 3, or IR-DYN headers, where the flag(s) have their new values ๐ The setup follows the normal rule for whatever mode you're in. In U-mode and O-mode the values get repeated several times so the decompressor literally gets at least one copy โจ In R-mode, the flags keep getting sent until an acknowledgment for a packet with the new RND flag values comes back ๐ฆ
On the decompressor side, it updates its RND and RND2 flags whenever it gets a UOR-2 with Extension 3 carrying values for RND or RND2, and the UOR-2 CRC confirms decompression worked ๐ซถ
Now the rules. When an IPv4 header whose RND flag hasn't been established to be 1 is in the static context, the packet types R-1 and UO-1 MUST NOT be used ๐
When there's no IPv4 header in the static context, or the RND flags for all IPv4 headers in the context have been established to be 1, the packet types R-1-ID, R-1-TS, UO-1-ID, and UO-1-TS MUST NOT be used ๐
While the RND flag is still being established (that transient state), the packet types R-1-ID, R-1-TS, UO-1-ID, and UO-1-TS MUST NOT be used either. This means you may have to check the RND flag(s) of Extension 3 before you can tell the exact format of a base header carrying an Extension 3, like whether a T-bit is there or not ๐ง
5.7.5.2. Flags/Fields in context ๐
Some flags and fields in Extension 3 have to be kept in the decompressor's context ๐ Their values get set using whatever mechanism fits the compression mode, unless the table below or the referred sections say otherwise ๐ธ
Flag/Field Initial value Comment
---------------------------------------------------------------------
Mode Unidirectional See section 5.6
NBO 1 See section 4.5.5
RND 0 See sections 4.5.5, 5.7.5.1
NBO2 1 As NBO, but for outer header
RND2 0 As RND, but for outer header
TS_STRIDE 1 See section 4.5.3
TIME_STRIDE 0 See section 4.5.4
Tsc 1 Tsc is always 1 in context;
can be 0 only when an Extension 3
is present. See the discussion of the
TS field in the beginning of section
5.7.
5.7.6. Feedback packets and formats ๐ฅ
when the round-trip time between compressor and decompressor is large, several packets can be in flight at once ๐ So a bunch of packets might show up at the decompressor after feedback was already sent but before the compressor has reacted to it ๐ญ And decompression can also fail because of leftover errors in the compressed header ๐
So, the rules:
a) in O-mode, the decompressor SHOULD limit the rate at which feedback on successful decompression is sent (if it's sent at all) โจ
b) when decompression fails, feedback SHOULD be sent only when decompression of several consecutive packets has failed, and when that happens, the feedback rate SHOULD be limited ๐ซ
c) when packets arrive that belong to a rejected packet stream, the feedback rate SHOULD be limited ๐ โโ๏ธ
A decompressor MAY limit the feedback rate by only sending feedback for one out of every k packets that cause the same (kind of) feedback ๐ฆ The right value of k is implementation dependent. k might be picked so feedback gets sent 1-3 times per link round-trip time ๐
See section 5.2.2 for a chat about ways to send feedback info to the compressor ๐
5.7.6.1. Feedback formats for ROHC RTP ๐ง
this section covers the feedback format for ROHC RTP. Also see 5.2.2 ๐
A few feedback formats have a field labeled SN. The SN field holds LSBs of an RTP Sequence Number โจ The sequence number to use is the one from the header that triggered the feedback. If that number can't be figured out, like when decompression fails, the sequence number to use is the last successfully decompressed header's ๐ญ If no sequence number is available at all, the feedback MUST carry a SN-NOT-VALID option ๐ When the compressor gets it, it matches valid SN LSBs with the most recent header sent with a SN that has matching LSBs ๐ง The decompressor must make sure it sends enough SN LSBs in its feedback so this matching doesn't get ambiguous. For example, if an 8-bit SN LSB field could wrap around within a round-trip time, the FEEDBACK-1 format can't be used ๐
FEEDBACK-1
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| SN |
+---+---+---+---+---+---+---+---+
A FEEDBACK-1 is an ACK ๐ To send a NACK or a STATIC-NACK, you need FEEDBACK-2. FEEDBACK-1 has no mode info, so if you need mode info, FEEDBACK-2 must be used ๐ซถ
FEEDBACK-2
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
|Acktype| Mode | SN |
+---+---+---+---+---+---+---+---+
| SN |
+---+---+---+---+---+---+---+---+
/ Feedback options /
+---+---+---+---+---+---+---+---+
Acktype: 0 = ACK
1 = NACK
2 = STATIC-NACK
3 is reserved (MUST NOT be used for parseability)
Mode: 0 is reserved
1 = Unidirectional mode
2 = Bidirectional Optimistic mode
3 = Bidirectional Reliable mode
Feedback options: a variable number of them, see section 5.7.6.2 ๐ท They can show up in any order ๐ฆ
5.7.6.2. ROHC RTP Feedback options ๐
A ROHC RTP Feedback option has variable length and this general format ๐ฆ
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| Opt Type | Opt Len |
+---+---+---+---+---+---+---+---+
/ option data / Opt Len octets
+---+---+---+---+---+---+---+---+
Sections 5.7.6.3-9 describe the feedback options that are defined right now โจ
5.7.6.3. The CRC option ๐
The CRC option holds an 8-bit CRC computed over the entire feedback payload. That means no packet type or code octet, but yes to any CID fields, using the polynomial from section 5.9.1 ๐ If the CID comes with an Add-CID octet, the Add-CID octet sits right before the FEEDBACK-1 or FEEDBACK-2 format. When computing the CRC, the CRC fields of all CRC options are zero ๐งฎ
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| Opt Type = 1 | Opt Len = 1 |
+---+---+---+---+---+---+---+---+
| CRC |
+---+---+---+---+---+---+---+---+
When feedback comes with a CRC option, the compressor MUST verify it by computing the CRC and comparing the result with the CRC in the CRC option. If the two aren't identical, the feedback information MUST be ignored ๐ โโ๏ธ
5.7.6.4. The REJECT option ๐ซ
The REJECT option tells the compressor that the decompressor doesn't have enough resources to handle the flow ๐ฌ
+---+---+---+---+---+---+---+---+
| Opt Type = 2 | Opt Len = 0 |
+---+---+---+---+---+---+---+---+
When a REJECT option comes in, the compressor stops compressing that packet stream, and should hold off on trying to increase the number of compressed packet streams for a while ๐ซฅ Any FEEDBACK packet carrying a REJECT option MUST also carry a CRC option ๐
5.7.6.5. The SN-NOT-VALID option โ
The SN-NOT-VALID option says the SN of the feedback isn't valid ๐ A compressor MUST NOT use the SN of the feedback to find the matching sent header when this option is there ๐ซ
+---+---+---+---+---+---+---+---+
| Opt Type = 3 | Opt Len = 0 |
+---+---+---+---+---+---+---+---+
5.7.6.6. The SN option ๐ข
The SN option gives you 8 extra bits of SN โจ
+---+---+---+---+---+---+---+---+
| Opt Type = 4 | Opt Len = 1 |
+---+---+---+---+---+---+---+---+
| SN |
+---+---+---+---+---+---+---+---+
5.7.6.7. The CLOCK option โฐ
The CLOCK option tells the compressor the clock resolution of the decompressor ๐ฐ๏ธ That's needed so the compressor can estimate the jitter the decompressor's clock adds when doing timer-based compression of the RTP Timestamp ๐
+---+---+---+---+---+---+---+---+
| Opt Type = 5 | Opt Len = 1 |
+---+---+---+---+---+---+---+---+
| clock resolution (ms) |
+---+---+---+---+---+---+---+---+
The smallest clock resolution you can indicate is 1 millisecond ๐ค The value zero is special: it means the decompressor can't do timer-based compression of the RTP Timestamp ๐ โโ๏ธ Any FEEDBACK packet carrying a CLOCK option SHOULD also carry a CRC option ๐ซถ
5.7.6.8. The JITTER option ๐
The JITTER option lets the decompressor report the maximum jitter it's seen lately ๐ It uses this formula, which is super similar to the formula for Max_Jitter_BC in section 4.5.4 ๐ค
Let observation window i hold the decompressor's best approximation of the compressor's sliding window (see section 4.5.4) when header i is received.
Max_Jitter_i =
max {|(T_i - T_j) - ((a_i - a_j) / TIME_STRIDE)|,
for all headers j in observation window i}
Max_Jitter =
max { Max_Jitter_i, for a large number of recent headers i }
The compressor can use this info to refine the formula it uses to pick k when doing timer-based compression of the RTP Timestamp ๐ง
+---+---+---+---+---+---+---+---+
| Opt Type = 6 | Opt Len = 1 |
+---+---+---+---+---+---+---+---+
| Max_Jitter |
+---+---+---+---+---+---+---+---+
The decompressor MAY ignore the oldest observed values of Max_Jitter_i ๐๏ธ So the reported Max_Jitter can go down. Robustness drops if the compressor uses a jitter estimate that's too small ๐ฌ So a FEEDBACK packet carrying a JITTER option SHOULD also carry a CRC option ๐ And the compressor MAY ignore decreasing Max_Jitter values ๐
5.7.6.9. The LOSS option ๐
The LOSS option lets the decompressor report the largest number of packets it's seen lost in a row ๐ซฃ The compressor MAY use this info to adjust the size of the reference window used in U- and O-mode ๐ช
+---+---+---+---+---+---+---+---+
| Opt Type = 7 | Opt Len = 1 |
+---+---+---+---+---+---+---+---+
| longest loss event (packets) |
+---+---+---+---+---+---+---+---+
The decompressor MAY choose to ignore the oldest loss events, so the value reported can go down ๐๏ธ Setting the reference window too small can hurt robustness, so a FEEDBACK packet carrying a LOSS option SHOULD also carry a CRC option ๐ก๏ธ The compressor MAY choose to ignore decreasing loss values ๐
5.7.6.10. Unknown option types ๐คทโโ๏ธ
If the compressor runs into an option type it doesn't know, it must keep parsing the rest of the FEEDBACK packet. That works because the option length is explicit ๐งฉ But it MUST otherwise ignore the unknown option ๐ ๐
5.7.6.11. RTP feedback example โจ
Okay so feedback for CID 8 saying ACK for SN 17 in Bidirectional Reliable mode can look like any of these ๐
Say we're using small CIDs:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 1 1 0 | 0 1 1 | feedback packet type, Code = 3
+---+---+---+---+---+---+---+---+
| 1 1 1 0 | 1 0 0 0 | Add-CID octet with CID = 8
+---+---+---+---+---+---+---+---+
| 0 0 | 1 1 | SN MSB = 0 | AckType = ACK, Mode = Reliable
+---+---+---+---+---+---+---+---+
| SN LSB = 17 |
+---+---+---+---+---+---+---+---+
The second, third, and fourth octets get handed to the compressor ๐
The FEEDBACK-1 format is also an option here, so you're allowed to use it too โจ Say we're using large CIDs:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 1 1 0 | 0 1 0 | feedback packet type, Code = 2
+---+---+---+---+---+---+---+---+
| 0 0 0 0 1 0 0 0 | large CID with value 8
+---+---+---+---+---+---+---+---+
| SN LSB = 17 |
+---+---+---+---+---+---+---+---+
The second and third octets get handed to the compressor ๐ฆ
Say we're using small CIDs again:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 1 1 0 | 0 1 0 | feedback packet type, Code = 2
+---+---+---+---+---+---+---+---+
| 1 1 1 0 | 1 0 0 0 | Add-CID octet with CID = 8
+---+---+---+---+---+---+---+---+
| SN LSB = 17 |
+---+---+---+---+---+---+---+---+
The second and third octets get handed to the compressor ๐ธ
And if we're using small CIDs but CID 0 instead of CID 8:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 1 1 0 | 0 0 1 | feedback packet type, Code = 1
+---+---+---+---+---+---+---+---+
| SN LSB = 17 |
+---+---+---+---+---+---+---+---+
Only the second octet gets handed to the compressor ๐ง
5.7.7. RTP IR and IR-DYN packets ๐
Okay so the subheaders that can be compressed get split into a STATIC part and a DYNAMIC part. These parts are defined in sections 5.7.7.3 through 5.7.7.7 ๐ซถ
The structure of a chain of subheaders is set by each header having a Next Header, or Protocol, field. That field tells you the type of the following header. Each Static part that's followed by another Static part has the Next Header/Protocol field, and that's what lets you parse the Static chain. The Dynamic chain, if it's there, is structured the same way ๐
IR and IR-DYN packets only get delivered to upper layers if the payload is non-empty, and that's the big deal ngl ๐ญ So an IP/UDP/RTP packet where the UDP length says the UDP payload is 12 octets can't be represented by an IR or IR-DYN packet. For those, you can use the UNCOMPRESSED profile (section 5.10) instead ๐
5.7.7.1. Basic structure of the IR packet
This packet type communicates the static part of the context, aka the values of the constant SN functions. It can optionally also communicate the dynamic part of the context, which is the parameters of the nonconstant SN functions. It can also optionally carry the payload of an original packet, if there is one ๐
0 1 2 3 4 5 6 7
--- --- --- --- --- --- --- ---
| Add-CID octet | if for small CIDs and CID != 0
+---+---+---+---+---+---+---+---+
| 1 1 1 1 1 1 0 | D |
+---+---+---+---+---+---+---+---+
| |
/ 0-2 octets of CID info / 1-2 octets if for large CIDs
| |
+---+---+---+---+---+---+---+---+
| Profile | 1 octet
+---+---+---+---+---+---+---+---+
| CRC | 1 octet
+---+---+---+---+---+---+---+---+
| |
| Static chain | variable length
| |
+---+---+---+---+---+---+---+---+
| |
| Dynamic chain | present if D = 1, variable length
| |
- - - - - - - - - - - - - - - -
| |
| Payload | variable length
| |
- - - - - - - - - - - - - - - -
D: D = 1 means the dynamic chain is there ๐Profile: The profile identifier, abbreviated as defined in section 5.2.3.CRC: An 8-bit CRC, computed according to section 5.9.1.Static chain: A chain of static subheader information.Dynamic chain: A chain of dynamic subheader information. Whatever dynamic info is there gets inferred from the Static chain.Payload: The payload of the corresponding original packet, if there is one. Whether there's a payload is inferred from the packet length.
5.7.7.2. Basic structure of the IR-DYN packet
This packet type communicates the dynamic part of the context, aka the parameters of the nonconstant SN functions ๐ฆ
0 1 2 3 4 5 6 7
--- --- --- --- --- --- --- ---
: Add-CID octet : if for small CIDs and CID != 0
+---+---+---+---+---+---+---+---+
| 1 1 1 1 1 0 0 0 | IR-DYN packet type
+---+---+---+---+---+---+---+---+
: :
/ 0-2 octets of CID info / 1-2 octets if for large CIDs
: :
+---+---+---+---+---+---+---+---+
| Profile | 1 octet
+---+---+---+---+---+---+---+---+
| CRC | 1 octet
+---+---+---+---+---+---+---+---+
| |
/ Dynamic chain / variable length
| |
+---+---+---+---+---+---+---+---+
: :
/ Payload / variable length
: :
- - - - - - - - - - - - - - - -
Profile: The profile identifier, abbreviated as defined in section 5.2.3.CRC: An 8-bit CRC, computed according to section 5.9.1.
NOTE: Okay bestie, the CRC only checks the integrity of the header itself ๐ So when someone acknowledges this header, that does not mean earlier changes to the static chain in the context got acknowledged too. Be extra careful when IR packets that update an existing context get followed by IR-DYN packets ๐ญ
Dynamic chain: A chain of dynamic subheader information. Whatever dynamic info is present gets inferred from the Static chain of the context.Payload: The payload of the corresponding original packet, if there is one. Whether there's a payload is inferred from the packet length.
Note: The static and dynamic chains of IR or IR-DYN packets for profile 0x0001 (ROHC RTP) MUST end with the static and dynamic parts of an RTP header. If not, the packet MUST be discarded and the context MUST NOT be updated. ๐
Note: The static or dynamic chains of IR or IR-DYN packets for profile 0x0002 (ROHC UDP) MUST end with the static and dynamic parts of a UDP header. If not, the packet MUST be discarded and the context MUST NOT be updated. ๐
Note: The static or dynamic chains of IR or IR-DYN packets for profile 0x0003 (ROHC ESP) MUST end with the static and dynamic parts of an ESP header. If not, the packet MUST be discarded and the context MUST NOT be updated. ๐
5.7.7.3. Initialization of IPv6 Header [IPv6]
Static part:
+---+---+---+---+---+---+---+---+
| Version = 6 |Flow Label(msb)| 1 octet
+---+---+---+---+---+---+---+---+
/ Flow Label (lsb) / 2 octets
+---+---+---+---+---+---+---+---+
| Next Header | 1 octet
+---+---+---+---+---+---+---+---+
/ Source Address / 16 octets
+---+---+---+---+---+---+---+---+
/ Destination Address / 16 octets
+---+---+---+---+---+---+---+---+
Dynamic part:
+---+---+---+---+---+---+---+---+
| Traffic Class | 1 octet
+---+---+---+---+---+---+---+---+
| Hop Limit | 1 octet
+---+---+---+---+---+---+---+---+
/ Generic extension header list / variable length
+---+---+---+---+---+---+---+---+
Eliminated:
Payload Length
Extras:
Generic extension header list: Encoded according to section 5.8.6.1, with all header items present in uncompressed form.CRC-DYNAMIC: Payload Length field (octets 5-6).CRC-STATIC: All other fields (octets 1-4, 7-40).
CRC coverage for extension headers is defined in section 5.8.7 โจ
Note: The Next Header field indicates the type of the following header in the static chain, rather than being a copy of the Next Header field of the original IPv6 header. See also section 5.7.7.8. ๐
5.7.7.4. Initialization of IPv4 Header [IPv4, section 3.1].
Static part:
Version, Protocol, Source Address, Destination Address.
+---+---+---+---+---+---+---+---+
| Version = 4 | 0 |
+---+---+---+---+---+---+---+---+
| Protocol |
+---+---+---+---+---+---+---+---+
/ Source Address / 4 octets
+---+---+---+---+---+---+---+---+
/ Destination Address / 4 octets
+---+---+---+---+---+---+---+---+
Dynamic part:
Type of Service, Time to Live, Identification, DF, RND, NBO, extension header list.
+---+---+---+---+---+---+---+---+
| Type of Service |
+---+---+---+---+---+---+---+---+
| Time to Live |
+---+---+---+---+---+---+---+---+
/ Identification / 2 octets
+---+---+---+---+---+---+---+---+
| DF|RND|NBO| 0 |
+---+---+---+---+---+---+---+---+
/ Generic extension header list / variable length
+---+---+---+---+---+---+---+---+
Eliminated:
IHL(IP Header Length, must be 5)Total Length(inferred in decompressed packets)MF flag(More Fragments flag, must be 0)Fragment Offset(must be 0)Header Checksum(inferred in decompressed packets)Options, Padding(must not be present)
Extras:
RND, NBO: See section 5.7.Generic extension header list: Encoded according to section 5.8.6.1, with all header items present in uncompressed form.CRC-DYNAMIC: Total Length, Identification, Header Checksum (octets 3-4, 5-6, 11-12).CRC-STATIC: All other fields (octets 1-2, 7-10, 13-20)
CRC coverage for extension headers is defined in section 5.8.7 ๐ฆ
Note: The Protocol field indicates the type of the following header in the static chain, rather than being a copy of the Protocol field of the original IPv4 header. See also section 5.7.7.8. ๐ซถ
5.7.7.5. Initialization of UDP Header [RFC-768].
Static part:
+---+---+---+---+---+---+---+---+
/ Source Port / 2 octets
+---+---+---+---+---+---+---+---+
/ Destination Port / 2 octets
+---+---+---+---+---+---+---+---+
Dynamic part:
+---+---+---+---+---+---+---+---+
/ Checksum / 2 octets
+---+---+---+---+---+---+---+---+
Eliminated:
Length
The Length field of the UDP header MUST match the Length field(s) of the preceding subheaders. That means there must not be any padding after the UDP payload that's covered by the IP Length. ๐
CRC-DYNAMIC: Length field, Checksum (octets 5-8).CRC-STATIC: All other fields (octets 1-4). ๐
5.7.7.6. Initialization of RTP Header [RTP] โจ
okay bestie, the RTP header is up next ๐ it's split into a static part and a dynamic part, so let's go through both ๐
Static part: just the SSRC, which is 4 octets ๐ฆ
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
/ SSRC / 4 octets
+---+---+---+---+---+---+---+---+
Dynamic part: this one is the main event ๐ it covers P, X, CC, PT, M, sequence number, timestamp, timestamp stride, and CSRC identifiers
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| V=2 | P | RX| CC | (RX is NOT the RTP X bit)
+---+---+---+---+---+---+---+---+
| M | PT |
+---+---+---+---+---+---+---+---+
/ RTP Sequence Number / 2 octets
+---+---+---+---+---+---+---+---+
/ RTP Timestamp (absolute) / 4 octets
+---+---+---+---+---+---+---+---+
/ Generic CSRC list / variable length
+---+---+---+---+---+---+---+---+
: Reserved | X | Mode |TIS|TSS: if RX = 1
+---+---+---+---+---+---+---+---+
: TS_Stride : 1-4 octets, if TSS = 1
+---+---+---+---+---+---+---+---+
: Time_Stride : 1-4 octets, if TIS = 1
+---+---+---+---+---+---+---+---+
Eliminated: nothing ๐ every field stays in the picture
Extras:
RX: controls whether the extension is present ๐งMode: the compression mode, where 0 = Reserved, 1 = Unidirectional, 2 = Bidirectional Optimistic, and 3 = Bidirectional Reliable ๐
X is a copy of the X bit from the RTP header, and it's presumed to be 0 if RX = 0 ๐ธ
Reserved is set to zero when sending and ignored when received, so no drama there โจ
The Generic CSRC list is a CSRC list encoded according to section 5.8.6.1, with all CSRC items present ๐
CRC-DYNAMIC covers the octets holding the M-bit, the sequence number field, and the timestamp (octets 2-8) ๐ฅ
CRC-STATIC covers all the other fields (octets 1, 9-12, and the original CSRC list) ๐
5.7.7.7. Initialization of ESP Header [ESP, section 2] ๐
this one is for when the NULL encryption algorithm [NULL] is NOT being used with ESP, which means the subheaders after the ESP header are encrypted (see 5.12) ๐ and for compression of the ESP header when NULL encryption is being used, check out 5.8.4.3 instead ๐ฆ
Static part: the SPI, which is 4 octets ๐
+---+---+---+---+---+---+---+---+
/ SPI / 4 octets
+---+---+---+---+---+---+---+---+
Dynamic part: the sequence number, which is also 4 octets ๐
+---+---+---+---+---+---+---+---+
/ Sequence Number / 4 octets
+---+---+---+---+---+---+---+---+
Eliminated: every other field is encrypted, so it can neither be located nor compressed ๐
CRC-DYNAMIC is the sequence number (octets 5-8) โจ
CRC-STATIC is all the other octets ๐ซถ
Note: no encrypted data counts as part of the header for computing the CRC. Octets after the eighth octet are not considered part of the header ๐
5.7.7.8. Initialization of Other Headers ๐ง
headers that aren't explicitly listed in the previous subsections can only be compressed by making them part of an extension header chain following an IPv4 or IPv6 header, see section 5.8 ๐ธ
5.8. List compression ๐
header information from the packet stream that you want to compress can be structured as an ordered list, and it's mostly constant between packets ๐ญ the generic structure looks like this:
+--------+--------+--...--+--------+
list: | item 1 | item 2 | | item n |
+--------+--------+--...--+--------+
the basic principles of list-based compression are these ๐ฆ
- while the list is constant, no information about the list is sent in compressed headers.
- small changes in the list are represented as additions (Insertion scheme), deletions (Removal scheme), or both (Remove Then Insert scheme).
- the list can also be sent in its entirety (Generic scheme).
there are two kinds of lists: CSRC lists in RTP packets, and extension header chains in IP packets (both IPv4 and IPv6) ๐
IPv6 base headers and IPv4 headers cannot be part of an extension header chain ๐ headers that can be part of extension header chains include:
- a) the AH header
- b) the null ESP header
- c) the minimal encapsulation header [RFC2004, section 3.1]
- d) the GRE header [GRE1, GRE2]
- e) IPv6 extension headers
the table-based item compression scheme (5.8.1) comes first, since it reduces the size of each item ๐ then it's defined which reference list to use in the insertion and removal schemes (5.8.2). list encoding schemes are in section 5.8.3, a few special cases are in section 5.8.4, and the exact formats are in sections 5.8.5-5.8.6 โจ
5.8.1. Table-based item compression ๐
the table-based item compression scheme is a way to compress the individual items sent in compressed lists ๐ the compressor gives each item in a list a unique identifier, called an Index ๐ conceptually, the compressor keeps a table with all the items, indexed by Index ๐๏ธ
the (Index, item) pair gets sent together in compressed lists until the compressor is confident enough that the decompressor has seen the mapping between the item and its Index ๐ฅน in R-mode, that confidence comes from receiving an acknowledgment from the decompressor. in U/O-mode, it comes from sending L (Index, item) pairs (not necessarily consecutively) ๐
once that's done, the Index alone is sent in compressed lists to point at the item ๐ฆ the compressor may reassign an existing Index to a new item, but then it has to re-establish the mapping the same way as above ๐
the decompressor conceptually keeps a table with every (Index, item) pair it knows about ๐ that table gets updated whenever an (Index, item) pair is received (and decompression is verified by a CRC) โ the decompressor pulls the item from the table whenever it gets an Index without an accompanying item ๐ซถ
5.8.1.1. Translation table in R-mode ๐ธ
on the compressor side, an entry in the Translation Table looks like this:
+-------+------+---------------+
Index i | Known | item | SN1, SN2, ... |
+-------+------+---------------+
the Known flag shows whether the mapping between Index i and item has been established, meaning whether Index i alone can be sent in compressed lists โจ Known starts at zero, and it's also set back to zero whenever Index i gets assigned to a new item ๐ Known is set to one when the matching (Index, item) pair is acknowledged ๐
acknowledgments are based on the RTP Sequence Number, so the translation table holds a list of RTP Sequence Numbers for every packet that contains the (Index, item) pair ๐ง when a packet with a sequence number in that list gets acknowledged, the Known flag is set and the sequence number list can be thrown out ๐๏ธ
each entry in the Translation Table on the decompressor side looks like this:
+-------+------+
Index i | Known | item |
+-------+------+
all the Known fields start out at zero ๐ฑ whenever the decompressor receives an (Index, item) pair, it puts the item into the table at position Index and sets that entry's Known flag to one ๐
if it gets an index without an accompanying item and the Known flag is zero, the header MUST be discarded and a NACK SHOULD be sent ๐จ
5.8.1.2. Translation table in U/O-modes ๐ฆ
on the compressor side, each entry in the Translation Table looks like this:
+-------+------+---------+
Index | Known | item | Counter |
+-------+------+---------+
the Index, Known, and item fields mean the same thing as they do in section 5.8.1.1 ๐
Known gets set when the (Index, item) pair has been sent in L compressed lists (not necessarily consecutively) ๐ the Counter field keeps track of how many times the pair has been sent ๐ข Counter is set to 0 for each new entry added to the table, and whenever Index gets assigned to a new item ๐งธ it goes up by 1 every time an (Index, item) pair is sent ๐ once the counter reaches L, the Known field is set, and after that only the Index needs to be sent in compressed lists ๐
on the decompressor side, the Translation Table is the same as the one defined in R-mode ๐ซถ
5.8.2. Reference list determination โจ
in reference based compression schemes (that's the addition or deletion based ones), compression and decompression of a list (curr_list) are based on a reference list (ref_list) that's assumed to be present in the context of both the compressor and the decompressor ๐ง the compressed list is an encoding of the differences between curr_list and ref_list ๐ซ when the decompressor gets a compressed list, it applies those differences to its reference list to get the original list back ๐ช
to identify the reference list (to be) used, each compressed list carries an identifier (ref_id) ๐ the reference list gets established in different ways in R-mode and U/O-mode ๐
5.8.2.1. Reference list in R-mode and U/O-mode โจ
okay so in R-mode, picking the reference list is all about acknowledgments ๐. The compressor uses the latest list the decompressor has acknowledged as ref_list. ref_list only gets updated when an acknowledgment comes in ๐ซถ. The least significant bits of the RTP Sequence Number of the acknowledged packet become the ref_id.
In U/O-mode it's giving generations ๐ธ. A run of identical lists counts as one generation, and they all share the same generation identifier (gen_id). gen_id goes up by 1 every time the list changes, and it rides along in compressed and uncompressed lists that could become reference lists. Normally gen_id has to show up in at least L headers before that list can be used as a ref_list. But some acknowledgments can be sent in O-mode (and in U-mode too), and once an acknowledgment for a header arrives, that header's list counts as known and doesn't need repeating โจ. The least significant bits of the gen_id are used as the ref_id in U/O-mode.
The reference list logic is kind of like the SN and TS logic ๐
. The big difference is that the decompressor keeps a sliding window of candidates for ref_list and grabs the right one using the ref_id of the compressed list.
Logic of compressor:
a) In the IR state, the compressor sends Generic lists (see 5.8.5) containing every item of the current list, in order, to set up or refresh the decompressor's context ๐ง. In R-mode, those Generic lists keep going until a header gets acknowledged. That header's list can then be used as a reference list for compressing later lists.
In U/O-mode, the compressor sends generation identifiers with the Generic lists until:
- a generation identifier has been repeated L times, or
- an acknowledgment for a header carrying a generation identifier has been received.
The repeated (1) or acknowledged (2) list can be used as a reference list to compress later lists, and it's kept together with its generation identifier.
b) When it's not in the IR state, the compressor moves to the FO state once it spots a difference between curr_list and the previous list. It sends compressed lists based on ref_list to update the decompressor's context. (But see d).) ๐
In R-mode, the compressor keeps sending compressed lists using the same reference until it gets an acknowledgment for a packet containing the newest list. Then it may move to the SO state with regard to the list.
In U/O-mode, the compressor keeps sending compressed lists with generation identifiers until:
- a generation identifier has been repeated L times, or
- an acknowledgment for a header carrying the latest generation identifier has been received.
The repeated or acknowledged list becomes the future reference list. The compressor may move to the SO state with regard to the list.
c) In R-mode, the compressor keeps a sliding window holding the lists that were sent to update the decompressor's context and haven't been acknowledged yet ๐ช. The window shrinks when an acknowledgment arrives: every list sent before the acknowledged one gets removed. The compressor may use the Index to represent items of lists in the sliding window.
In U/O-mode, the compressor needs to store:
- the reference list and its generation identifier, and
- if the current generation identifier is different from the reference generation, the current list and the sequence numbers it was sent with.
(2) is needed to figure out whether an acknowledgment is about the latest generation. It's not needed in U-mode.
d) In U/O-mode, the compressor may choose not to send a generation identifier with a compressed list. Lists without generation identifiers don't get a new generation identifier, and they must not be used as future reference lists. They don't update the context. This is handy when a new list repeats a few times and then reverts back to its old value ๐.
Logic of decompressor:
e) In R-mode, the decompressor acknowledges all received uncompressed or compressed lists that establish or update the context. (Such compressed headers contain a CRC.) ๐
In O-mode, the decompressor MAY acknowledge a list with a new generation identifier, see section 5.4.2.2.
In U-mode, the decompressor MAY acknowledge a list sent in an IR packet, see section 5.3.2.3.
f) The decompressor keeps a sliding window containing the lists that may be used as reference lists ๐ฆ.
In R-mode, the sliding window holds lists that have been acknowledged but not yet used as reference lists.
In U/O-mode, the sliding window holds at most one list per generation. It contains all generations the decompressor has seen that are newer than the last generation used as a reference.
g) When the decompressor receives a compressed list, it grabs the proper ref_list from the sliding window based on the ref_id, then decompresses the compressed list to get curr_list ๐งฉ.
In R-mode, curr_list gets inserted into the sliding window if an acknowledgment is sent for it. The sliding window is shrunk by removing all lists received before ref_list.
In U/O-mode, curr_list gets inserted into the sliding window together with its generation identifier, as long as the compressed list had a generation identifier and the sliding window doesn't already have a list with that generation identifier. All lists with generations older than ref_id are removed from the sliding window.
5.8.3. Encoding schemes for the compressed list ๐
Four encoding schemes for the compressed list are described here. The exact formats of the compressed CSRC list and the compressed IP extension header list that use these schemes are described in sections 5.8.5-5.8.6 ๐ .
Generic scheme
Unlike the later schemes, this one doesn't rely on a reference list having been established. The entire list gets sent, using table based compression for each individual item. The generic scheme is always used when establishing the decompressor's context, and it may also be used at other times, whenever the compressor sees fit ๐ง.
Insertion Only scheme
When the new list can be built from ref_list by adding items, a list of the added items is sent (using table based compression), along with the positions in ref_list where the new items go. An insertion bit mask shows the insertion positions in ref_list ๐.
When a list compressed with the Insertion Only scheme comes in, curr_list is rebuilt by scanning the insertion bit mask from left to right. When a '0' is seen, an item is copied from ref_list. When a '1' is seen, an item is copied from the list of added items. If a '1' shows up after the list of added items has run out, something went wrong, and decompression fails ๐ญ: the header MUST NOT be delivered to upper layers; it should be discarded, and it MUST NOT be acknowledged nor used as a reference.
To build the insertion bit mask and the list of added items, the compressor MAY use the following algorithm:
- Start with an empty bit list and an empty Inserted Item list.
- Start by considering the first item of
curr_listandref_list. - If
curr_listhas a different item thanref_list, append a set bit (1) to the bit list. Append the first item incurr_list(represented using table-based item compression) to the Inserted Item list, then advance to the next item ofcurr_list. Otherwise, append a zero bit (0) to the bit list, advance to the next item ofcurr_list, and advance to the next item ofref_list. - Repeat 3) until
curr_listhas been exhausted. - If the length of the bit list is less than the required bit mask length, append additional zeroes.
Removal Only scheme
This scheme can be used when curr_list can be obtained by removing some items from ref_list. The positions of the items that are in ref_list but not in curr_list are sent as a removal bit mask ๐ซง.
When the compressed list arrives, the decompressor gets curr_list by scanning the removal bit mask from left to right. When a '0' is seen, the next item of ref_list is copied into curr_list. When a '1' is seen, the next item of ref_list is skipped over without being copied. If a '0' is seen after ref_list has been exhausted, something went wrong and decompression fails ๐: the header MUST NOT be delivered to upper layers; it should be discarded, and it MUST NOT be acknowledged nor used as a reference.
To build the removal bit mask and the list of added items, the compressor MAY use the following algorithm:
- Start with an empty bit list.
- Start by considering the first item of
curr_listandref_list. - If
curr_listhas a different item thanref_list, append a set bit (1) to the bit list and advance to the next item ofref_list. Otherwise, append a zero bit (0) to the bit list, advance to the next item ofcurr_list, and advance to the next item ofref_list. - Repeat 3) until
curr_listhas been exhausted. - If the length of the bit list is less than the required bit mask length, append additional ones.
Remove Then Insert scheme
This one is a combo ๐. curr_list is obtained by first removing items from ref_list, then inserting items into the resulting list. A removal bit mask, an insertion bit mask, and a list of added items are sent.
When the compressed list arrives, the decompressor processes the removal bit mask the same way as in the Removal Only scheme. The resulting list is then used as the reference list when the insertion bit mask and the list of added items are processed, the same way as in the Insertion Only scheme โจ.
5.8.4. Special handling of IP extension headers ๐ฆ
In CSRC list compression, each CSRC gets its own index. IP extension header list compression is different ๐ง: an index is usually tied to a type of extension header. When there's more than one IP header, there's more than one list of extension headers, so an index per type per list is used.
Being tied to a type means a new index doesn't always have to be used each time a field in an IP extension header changes. But when a field in an extension header changes, the mapping between the index and the new value of the extension header still needs to be set up, except in the special handling cases defined in the following subsections ๐.
5.8.4.1. Next Header field ๐
okay so the next header field in an IP header or extension header changes whenever the type of the header right after it changes ๐ญ like when a new extension header gets slotted in after it, when the very next extension header gets removed from the list, or when the order of the extension headers gets shuffled. it's honestly not that rare that for a given header, ONLY the next header field changes while everything else stays exactly the same โจ
so if only the next header field changes, the extension header counts as unchanged, and there are special rules for handling that change down below ๐ซถ
every uncompressed extension header item that gets communicated tells you its own type in its Next Header field. ngl the rules below explain how to handle the Next Header fields by picturing the conceptual reference list as an exact recreation of the original uncompressed extension header list ๐
a) When a subsequent extension header is removed from the list, the new value of the next header field is obtained from the reference extension header list. for example, say the reference header list (ref_list) has headers A, B and C (ref_ext_hdr A, B, C), and the current extension header list (curr_list) only has extension headers A and C (curr_ext_hdr A, C). the order and value of the next header fields of these extension headers look like this ๐
ref_list:
+--------+-----+ +--------+-----+ +--------+-----+
| type B | | | type C | | | type D | |
+--------+ | +--------+ | +--------+ |
| | | | | |
+--------------+ +--------------+ +--------------+
ref_ext_hdr A ref_ext_hdr B ref_ext_hdr C
curr_list:
+--------+-----+ +--------+-----+
| type C | | | type D | |
+--------+ | +--------+ |
| | | |
+--------------+ +--------------+
curr_ext_hdr A curr_ext_hdr C
when you compare curr_ext_hdr A in curr_list with ref_ext_hdr A in ref_list, the value of the next header field went from "type B" to "type C" because extension header B got removed ๐
the new value of the next header field in curr_ext_hdr A, which is "type C", does NOT need to be sent to the decompressor. instead, it gets pulled from the next header field of the removed ref_ext_hdr B ๐ฆ
b) When a new extension header is inserted after an existing extension header, the next header field in the communicated item carries the type of itself, rather than the type of the header that follows. for example, say the reference header list (ref_list) has headers A and C (ref_ext_hdr A, C), and the current header list (curr_list) has headers A, B and C (curr_ext_hdr A, B, C). here's how the order and value of the next header fields look ๐
ref_list:
+--------+-----+ +--------+-----+
| type C | | | type D | |
+--------+ | +--------+ |
| | | |
+--------------+ +--------------+
ref_ext_hdr A ref_ext_hdr C
curr_list:
+--------+-----+ +--------+-----+ +--------+-----+
| type B | | | type C | | | type D | |
+--------+ | +--------+ | +--------+ |
| | | | | |
+--------------+ +--------------+ +--------------+
curr_ext_hdr A curr_ext_hdr B curr_ext_hdr C
comparing curr_list and ref_list, the value of the next header field in extension header A changed from "type C" to "type B" โจ
the uncompressed curr_ext_hdr B is carried in the compressed header list. BUT it carries "type B" instead of "type C" in its next header field ๐ when the decompressor inserts a new header after curr_ext_hdr A, the next header field of A gets taken from the new header, and the next header field of the new header gets taken from ref_ext_hdr A ๐
c) some headers whose compression is defined in this document don't have Next Header fields, or they don't have their Next Header field in the standard position (first octet of the header). the GRE and ESP headers are like that ๐ญ when they're sent as uncompressed items in lists, these headers get modified so that they DO have a Next Header field as their first octet (see 5.8.4.3 and 5.8.4.4). this is necessary so the decompressor can decode the item ๐ซถ
5.8.4.2. Authentication Header (AH) ๐
the sequence number field in the AH [AH] holds a counter value that only ever goes up for a security association. so when you compare curr_list with ref_list, if the sequence number in the AH changes but the SPI field doesn't, the AH is not considered changed. main character energy ๐
if the sequence number in the AH increases linearly as the RTP Sequence Number increases, AND the compressor is confident the decompressor has picked up that pattern, the sequence number in the AH doesn't need to be sent. the decompressor applies linear extrapolation to rebuild the sequence number in the AH ๐ฆ
otherwise, a compressed sequence number gets included in the IPX compression field in an Extension 3 of an UOR-2 header ๐
the authentication data field in the AH changes from packet to packet and is sent as-is ๐ if the uncompressed AH is sent, the authentication data field goes inside the uncompressed AH. otherwise, it gets sent after the compressed IP/UDP/RTP and IPv6 extension headers and before the payload. see the beginning of section 5.7 ๐ธ
Note: The payload length field of the AH uses a different notion of length than other IPv6 extension headers. bestie, read that twice, it's a sneaky one ๐
5.8.4.3. Encapsulating Security Payload Header (ESP) โจ
Okay so when the Encapsulating Security Payload Header (ESP) [ESP] is showing up and the encryption algorithm is anything other than NULL, the UDP and RTP headers are both encrypted ๐, which means they can't be compressed. The ESP header basically ends the compressible header chain, bestie ๐. The ROHC ESP profile defined in section 5.12 MAY be used for the stream in this case ๐ง
Now here's the plot twist ๐ When the NULL encryption algorithm is used, the payload isn't actually encrypted. That's the situation where ESP is only doing authentication and not encryption, so compressing the rest of the header chain is possible ๐ฆ. The rest of this section explains how to compress the ESP header when NULL encryption is being used with ESP.
Here's the tricky bit ๐ซฃ You can't tell whether NULL encryption is in use just by looking at a header in the stream, because that info only lives at the encryption endpoints. So a compressor can try compressing under the assumption that NULL encryption is in play, and then bail out later if that assumption turns out to be wrong.
The compressor can, for example, peek at the Next Header fields and at the header fields that are supposed to be static in the subsequent headers to figure out if NULL encryption is happening. If those change unpredictably, it's probably some other encryption algorithm, and compression of the subsequent headers SHOULD be aborted ๐ฌ. After that, compression of the stream is either discontinued, or a profile that only compresses up to the ESP header may be used (see 5.12). While attempting to compress the header, the compressor should use the SPI of the ESP header together with the destination IP address as the defining fields for working out which packets belong to the stream ๐
In the ESP header [ESP, section 2], the fields that can be compressed are the SPI, the sequence number, the Next Header, and the padding bytes if they're in the standard format defined in [ESP]. (As always, the decompressor puts these fields back using the info in the context. Be careful to get all of it back exactly right, because the Authentication Data has to be verified over the exact same information it was computed over ๐ง)
ESP header [ESP, section 2]:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Security Parameters Index (SPI) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload Data (variable) |
~ ~
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Padding (0-255 octets) |
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Pad Length | Next Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Authentication Data |
+ (variable length, but assumed to be 12 octets) +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SPI: Static. If it changes, it needs to be reestablished ๐คSequence Number: Not sent when the offset from the sequence number of the compressed header is constant. When the offset is not constant, the sequence number may be compressed by sending LSBs. See 5.8.4. ๐ขPayload Data: This is where the subsequent headers live. They're parsed according to the Next Header field.Padding: The padding octets are assumed to be as defined in [ESP], i.e., to take the values 1, 2, ..., k, where k = Pad Length. If the padding in the static context has this pattern, padding in compressed headers is assumed to have this pattern as well and is removed. If padding in the static context does not have this pattern, the padding is not removed. ๐Pad Length: Dynamic. Always sent. It's the 14th octet from the end of the packet.Next Header: Static. It's the 13th octet from the end of the packet.
Authentication Data: This can have variable length, but when compression of a NULL-encryption ESP header is attempted, it is assumed to have length 12 octets.
The sequence number in ESP behaves the same way as the sequence number field in AH. When it increases linearly, it can be compressed to zero bits. When it doesn't increase linearly, a compressed sequence number is included in the IPX compression field in an Extension 3 of an UOR-2 header.
The information that's part of an uncompressed item of a compressed list is the Next Header field, followed by the SPI and the Sequence Number. Padding, Pad Length, Next Header, and Authentication Data are sent as-is at the end of the packet. This means the Next Header ends up in two places ๐
Uncompressed ESP list item:
+---+---+---+---+---+---+---+---+
| Next Header ! 1 octet (see section 5.8.4.1)
+---+---+---+---+---+---+---+---+
/ SPI / 4 octets
+---+---+---+---+---+---+---+---+
/ Sequence Number / 4 octets
+---+---+---+---+---+---+---+---+
When sending Uncompressed ESP list items, all ESP fields near the end of the packet are left untouched (Padding, Pad Length, Next Header, Authentication Data) ๐งบ
A compressed item consists of a compressed sequence number. When an item is compressed, Padding (if it follows the 1, 2, ..., k pattern) and Next Header are removed near the end of the packet. Authentication Data and Pad Length stay as-is near the end of the packet ๐
5.8.4.4. GRE Header [RFC 2784, RFC 2890]
The GRE header is a set of flags, followed by a mandatory Protocol Type and optional parts depending on which flags are set ๐
The sequence number field in the GRE header holds a counter value for a GRE tunnel. So when comparing curr_list with ref_list, if the sequence number in GRE changes, the GRE is not considered changed. Not drama, just vibes ๐ซถ
If the sequence number in the GRE header increases linearly as the RTP Sequence Number increases, and the compressor is confident that the decompressor has received the pattern, the sequence number in GRE need not be sent. The decompressor then applies linear extrapolation to rebuild the sequence number in the GRE header ๐ง
Otherwise, a compressed sequence number is included in the IPX compression field in an Extension 3 of an UOR-2 header.
The checksum data field in GRE, if present, changes from packet to packet and is sent as-is. If the uncompressed GRE header is sent, the checksum data field goes inside the uncompressed GRE header. Otherwise, if it's present, it's sent after the compressed IP/UDP/RTP and IPv6 extension headers and before the payload. See the beginning of section 5.7 โจ
In order to allow simple parsing of lists of items, an uncompressed GRE header sent as an item in a list is modified from the original GRE header in the following manner ๐งฉ:
- The 16-bit Protocol Type field, which encodes the type of the subsequent header using Ether types (see the Ether types section in [ASSIGNED]), is removed.
- A one-octet Next Header field is inserted as the first octet of the header. Its value corresponds to GRE (this value is 47 according to the Assigned Internet Protocol Number section of [ASSIGNED]) when the uncompressed item is inserted in a list, and to the type of the subsequent header when the uncompressed item is in a Generic list.
Note that this implies only GRE headers with Ether types that correspond to an IP protocol number can be compressed ๐
Uncompressed GRE list item:
+---+---+---+---+---+---+---+---+
| Next Header ! 1 octet (see section 5.8.4.1)
+---+---+---+---+---+---+---+---+
/ C | | K | S | | Ver | 1 octet
+---+---+---+---+---+---+---+---+
/ Checksum / 2 octets, if C=1
+---+---+---+---+---+---+---+---+
/ Key / 4 octets, if K=1
+---+---+---+---+---+---+---+---+
/ Sequence Number / 4 octets, if S=1
+---+---+---+---+---+---+---+---+
The bits left blank in the second octet are set to zero when
sending and ignored when received.
The fields Reserved0 and Reserved1 of the GRE header [GRE2] must be all zeroes; otherwise, the packet cannot be compressed by this profile ๐ซ
5.8.5. Format of compressed lists in Extension 3 ๐
5.8.5.1. Format of IP Extension Header(s) field
In Extension 3 (section 5.7.5), there's a field called IP extension header(s). This section describes the format of that field ๐ฆ
0 1 2 3 4 5 6 7
+-----+-----+-----+-----+-----+-----+-----+-----+
| CL | ASeq| ESeq| Gseq| res | 1 octet
+-----+-----+-----+-----+-----+-----+-----+-----+
: compressed AH Seq Number, 1 or 4 octets : if ASeq = 1
----- ----- ----- ----- ----- ----- ----- -----
: compressed ESP Seq Number, 1 or 4 octets : if Eseq = 1
----- ----- ----- ----- ----- ----- ----- -----
: compressed GRE Seq Number, 1 or 4 octets : if Gseq = 1
----- ----- ----- ----- ----- ----- ----- -----
: compressed header list, variable length : if CL = 1
----- ----- ----- ----- ----- ----- ----- -----
ASeq: indicates presence of compressed AH Seq NumberESeq: indicates presence of compressed ESP Seq NumberGSeq: indicates presence of compressed GRE Seq NumberCL: indicates presence of compressed header listres: reserved; set to zero when sending, ignored when received
When Aseq, Eseq, or Gseq is set, the corresponding header item (AH, ESP, or GRE header) is compressed. When it's not set, the corresponding header item is sent uncompressed or isn't present at all ๐คทโโ๏ธ
The format of compressed AH, ESP and GRE Sequence Numbers can each be either of the following:
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
| 0 | LSB of sequence number | | 1 | |
+---+---+---+---+---+---+---+---+ +---+ +
| |
+ LSB of sequence number +
| |
+ +
| |
+---+---+---+---+---+---+---+---+
The format of the compressed header list field is described in section 5.8.6 ๐
5.8.5.2. Format of Compressed CSRC List
The Compressed CSRC List field in the RTP header part of an Extension 3 (section 5.7.5) is as in section 5.8.6 ๐ธ
5.8.6. Compressed list formats ๐
okay so this section is all about what compressed lists actually look like on the wire โจ The layout is the same for CSRC lists and header lists. The only difference is what the items are. In CSRC lists they're CSRC identifiers, and in header lists they're uncompressed or compressed headers, as described in 5.8.4.2-4 ๐
5.8.6.1. Encoding Type 0 (generic scheme)
Type 0 is the generic one, the OG of the bunch ๐ง Here's the layout:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| ET=0 |GP |PS | CC = m |
+---+---+---+---+---+---+---+---+
: gen_id : 1 octet, if GP = 1
+---+---+---+---+---+---+---+---+
| XI 1, ..., XI m | m octets, or m * 4 bits
/ --- --- --- ---/
| : Padding : if PS = 0 and m is odd
+---+---+---+---+---+---+---+---+
| |
/ item 1, ..., item n / variable
| |
+---+---+---+---+---+---+---+---+
Here's what each piece means ๐ฆ
ET: Encoding type is zero.PS: Indicates size of XI fields.PS = 0indicates 4-bit XI fields.PS = 1indicates 8-bit XI fields.GP: Indicates presence of gen_id field.CC: CSRC counter from original RTP header.gen_id: Identifier for a sequence of identical lists. It is present in U/O-mode when the compressor decides that it may use this list as a future reference list.XI 1, ..., XI m: m XI items. The format of an XI item is as follows:
+---+---+---+---+
PS = 0: | X | Index |
+---+---+---+---+
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
PS = 1: | X | Index |
+---+---+---+---+---+---+---+---+
X = 1indicates that the item corresponding to the Index is sent in the item 0, ..., item n list.X = 0indicates that the item corresponding to the Index is not sent.
When 4-bit XI items are used and m > 1, the XI items get packed into octets like this ๐ธ
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| XI k | XI k + 1 |
+---+---+---+---+---+---+---+---+
Padding: A 4-bit padding field is present whenPS = 0and m is odd. The Padding field is set to zero when sending and ignored when receiving.Item 1, ..., item n: Each item corresponds to an XI withX = 1in XI 1, ..., XI m.
5.8.6.2. Encoding Type 1 (insertion only scheme)
Type 1 only does insertions, so it's giving very focused energy ๐
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| ET=1 |GP |PS | XI 1 |
+---+---+---+---+---+---+---+---+
: gen_id : 1 octet, if GP = 1
+---+---+---+---+---+---+---+---+
| ref_id |
+---+---+---+---+---+---+---+---+
/ insertion bit mask / 1-2 octets
+---+---+---+---+---+---+---+---+
| XI list | k octets, or (k - 1) * 4 bits
/ --- --- --- ---/
| : Padding : if PS = 0 and k is even
+---+---+---+---+---+---+---+---+
| |
/ item 1, ..., item n / variable
| |
+---+---+---+---+---+---+---+---+
Unless explicitly stated otherwise, fields have the same meaning and values as for encoding type 0 ๐
ET: Encoding type is one (1).XI 1: WhenPS = 0, the first 4-bit XI item is placed here. WhenPS = 1, the field is set to zero when sending, and ignored when receiving.ref_id: The identifier of the reference CSRC list used when the list was compressed. It is the 8 least significant bits of the RTP Sequence Number in R-mode and gen_id (see section 5.8.2) in U/O-mode.insertion bit mask: Bit mask indicating the positions where new items are to be inserted. See Insertion Only scheme in section 5.8.3. The bit mask can have either of the following two formats:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 7-bit mask | bit 1 is the first bit
+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+
| 1 | | bit 1 is the first bit
+---+ 15-bit mask +
| | bit 7 is the last bit
+---+---+---+---+---+---+---+---+
XI list: XI fields for items to be inserted. When the insertion bit mask has k ones, the total number of XI fields is k. WhenPS = 1, all XI fields are in the XI list. WhenPS = 0, the first XI field is in the XI 1 field, and the remaining k - 1 XI fields are in the XI list.Padding: Present whenPS = 0and k is even.item 1, ..., item n: One item for each XI field with the X bit set.
5.8.6.3. Encoding Type 2 (removal only scheme)
Type 2 is the opposite vibe: removal only, no drama ๐งน
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| ET=2 |GP |res| Count |
+---+---+---+---+---+---+---+---+
: gen_id : 1 octet, if GP = 1
+---+---+---+---+---+---+---+---+
| ref_id |
+---+---+---+---+---+---+---+---+
/ removal bit mask / 1-2 octets
+---+---+---+---+---+---+---+---+
Unless explicitly stated otherwise, fields have the same meaning and values as in section 5.8.5.2 ๐ซถ
ET: Encoding type is 2.res: Reserved. Set to zero when sending, ignored when received.Count: Number of elements in ref_list.removal bit mask: Indicates the elements in ref_list to be removed in order to obtain the current list. See section 5.8.3. The removal bit mask has the same format as the insertion bit mask of section 5.8.6.3.
5.8.6.4. Encoding Type 3 (remove then insert scheme)
Type 3 does both, removal first and then insertion, so it's basically the full glow-up โจ For a description of the Remove then insert scheme, see section 5.8.3.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| ET=3 |GP |PS | XI 1 |
+---+---+---+---+---+---+---+---+
: gen_id : 1 octet, if GP = 1
+---+---+---+---+---+---+---+---+
| ref_id |
+---+---+---+---+---+---+---+---+
/ removal bit mask / 1-2 octets
+---+---+---+---+---+---+---+---+
/ insertion bit mask / 1-2 octets
+---+---+---+---+---+---+---+---+
| XI list | k octets, or (k - 1) * 4 bits
/ --- --- --- ---/
| : Padding : if PS = 0 and k is even
+---+---+---+---+---+---+---+---+
| |
/ item 1, ..., item n / variable
| |
+---+---+---+---+---+---+---+---+
The fields in this header have the same meaning and formats as in section 5.8.5.2, except when explicitly stated otherwise below ๐
ET: Encoding type is 3.removal bit mask: See section 5.8.6.3.
5.8.7. CRC coverage for extension headers ๐
All fields of extension headers are CRC-STATIC, with the following exceptions which are CRC-DYNAMIC. The exceptions are:
- Entire AH header.
- Entire ESP header.
- Sequence number in GRE, Checksum in GRE
5.9. Header compression CRCs, coverage and polynomials ๐งฎ
This chapter is where we get into how the CRCs used in packet headers in this document actually get calculated ๐ (Quick side note: another type of CRC is defined for reconstructed units in section 5.2.5, so don't mix them up!)
5.9.1. IR and IR-DYN packet CRCs
The CRC in the IR and IR-DYN packet is calculated over the entire IR or IR-DYN packet, excluding Payload and including CID or any Add-CID octet. For purposes of computing the CRC, the CRC field in the header is set to zero.
The initial content of the CRC register is to be preset to all 1's.
The CRC polynomial to be used for the 8-bit CRC is:
C(x) = 1 + x + x^2 + x^8
5.9.2. CRCs in compressed headers
The CRC in compressed headers is calculated over all octets of the entire original header, before compression, in the following manner ๐ซง
The octets of the header are classified as either CRC-STATIC or CRC-DYNAMIC, and the CRC is calculated over:
- the concatenated CRC-STATIC octets of the original header, placed in the same order as they appear in the original header, followed by
- the concatenated CRC-DYNAMIC octets of the original header, placed in the same order as they appear in the original header.
The intention is that the state of the CRC computation after 1) will be saved. As long as the CRC-STATIC octets do not change, the CRC calculation will then only need to process the CRC-DYNAMIC octets. Smart, right? ๐ง
In a typical RTP/UDP/IPv4 header, 25 octets are CRC-STATIC and 15 are CRC-DYNAMIC. In a typical RTP/UDP/IPv6 header, 49 octets are CRC-STATIC and 11 are CRC-DYNAMIC. This technique will thus reduce the computational complexity of the CRC calculation by roughly 60% for RTP/UDP/IPv4 and by roughly 80% for RTP/UDP/IPv6. Iconic โจ
Note: Whenever the CRC-STATIC fields change, the new saved CRC state after 1) is compared with the old state. If the states are identical, the CRC cannot catch the error consisting in the decompressor not having updated the static context. In U/O-mode the compressor SHOULD then for a while use packet types with another CRC length, for which there is a difference in CRC state, to ensure error detection. This is the part where you really don't want to get it wrong ๐
The initial content of the CRC register is preset to all 1's.
The polynomial to be used for the 3 bit CRC is:
C(x) = 1 + x + x^3
The polynomial to be used for the 7 bit CRC is:
C(x) = 1 + x + x^2 + x^3 + x^6 + x^7
The CRC in compressed headers is calculated over the entire original header, before compression.
5.10. ROHC UNCOMPRESSED -- no compression (Profile 0x0000) ๐ฆ
In ROHC, compression has not been defined for all kinds of IP headers. Profile 0x0000 gives you a way to send IP packets without compressing them ๐ฆ This can be used for IP fragments, RTCP packets, and in general for any packet for which compression of the header has not been defined, is not possible due to resource constraints, or is not desirable for some other reason.
After initialization, the only overhead for sending packets using Profile 0x0000 is the size of the CID. When uncompressed packets are frequent, Profile 0x0000 should be associated with a CID with size zero or one octet. There is no need to associate Profile 0x0000 with more than one CID ๐
5.10.1. IR packet ๐ธ
okay bestie, the IR packet is the initialization packet for Profile 0x0000, and it's the one that sets everything up โจ here's the layout:
0 1 2 3 4 5 6 7
--- --- --- --- --- --- --- ---
: Add-CID octet : if for small CIDs and (CID != 0)
+---+---+---+---+---+---+---+---+
| 1 1 1 1 1 1 0 |res|
+---+---+---+---+---+---+---+---+
: :
/ 0-2 octets of CID info / 1-2 octets if for large CIDs
: :
+---+---+---+---+---+---+---+---+
| Profile = 0 | 1 octet
+---+---+---+---+---+---+---+---+
| CRC | 1 octet
+---+---+---+---+---+---+---+---+
: : (optional)
/ IP packet / variable length
: :
--- --- --- --- --- --- --- ---
Here's the tea on each piece ๐ซถ
res: always zero ๐Profile: 0 ๐CRC: 8-bit CRC, computed using the polynomial of section 5.9.1 ๐ฅ It covers the first octet of the IR packet through the Profile octet of the IR packet, so it does not cover the CRC itself or the IP packet. Not me being picky about it ๐IP packet: an uncompressed IP packet may be included in the IR packet ๐ฆ The decompressor figures out whether it's there by looking at the length of the IR packet โจ
5.10.2. Normal packet ๐
A Normal packet is basically a normal IP packet with some CID info tucked in ๐ง When the channel uses small CIDs and profile 0x0000 is tied to a CID > 0, an Add-CID octet gets prepended to the IP packet. When the channel uses large CIDs, the CID starts at the second octet of the Normal packet, which is giving organized energy ๐
0 1 2 3 4 5 6 7
--- --- --- --- --- --- --- ---
: Add-CID octet : if for small CIDs and (CID != 0)
+---+---+---+---+---+---+---+---+
| first octet of IP packet |
+---+---+---+---+---+---+---+---+
: :
/ 0-2 octets of CID info / 1-2 octets if for large CIDs
: :
+---+---+---+---+---+---+---+---+
| |
/ rest of IP packet / variable length
| |
+---+---+---+---+---+---+---+---+
Now the iconic part ๐ The first octet of the IP packet starts with the bit pattern 0100 (IPv4) or 0110 (IPv6). That doesn't clash with any reserved packet types, so no extra bits are needed beyond the CID. Honestly the profile is super future-proof too, since problems don't show up until IP version 14 ๐
5.10.3. States and modes ๐ฆ
There are two modes in Profile 0x0000: Unidirectional mode and Bidirectional mode. In Unidirectional mode, the compressor repeats the IR packet periodically. In Bidirectional mode, the compressor never repeats the IR packet. The compressor and decompressor always start in Unidirectional mode, and whenever feedback comes in, the compressor switches over to Bidirectional mode โจ
The compressor can be in either of two states: the IR state or the Normal state. It starts in the IR state ๐ธ
a) IR state: only IR packets can be sent. After sending a small number of IR packets (only one when refreshing), the compressor switches to the Normal state ๐ง
b) Normal state: only Normal packets can be sent. When in Unidirectional mode, the compressor periodically transits back to the IR state. The length of the period is implementation dependent, but should be fairly long. Exponential backoff may be used ๐
c) Feedback in any state: when feedback is received in any state, the compressor switches to Bidirectional mode ๐ซถ
The decompressor can be in either of two states: NO_CONTEXT or FULL_CONTEXT. It starts in NO_CONTEXT ๐
d) IR in NO_CONTEXT: when an IR packet is received in the NO_CONTEXT state, the decompressor first verifies the packet using the CRC. If the packet is OK, the decompressor 1) moves to the FULL_CONTEXT state, 2) delivers the IP packet to upper layers if present, and 3) MAY send an ACK ๐ฅ If the packet is not OK, it gets discarded without any further action ๐
e) Anything else in NO_CONTEXT: when any other packet is received in the NO_CONTEXT state, it gets discarded without further action ๐
f) IR in FULL_CONTEXT: when an IR packet is received in the FULL_CONTEXT state, it's first verified using the CRC. If it's OK, the decompressor 1) delivers the IP packet to upper layers if present, and 2) MAY send an ACK. If the packet is not OK, no action is taken ๐
g) Normal in FULL_CONTEXT: when a Normal packet is received in the FULL_CONTEXT state, the CID information is removed and the IP packet is delivered to upper layers โญ
5.10.4. Feedback ๐
The only kind of feedback in Profile 0x0000 is ACKs โจ Profile 0x0000 MUST NOT be rejected. Profile 0x0000 SHOULD be associated with at most one CID. ACKs use the FEEDBACK-1 format of section 5.2, and the value of the profile-specific octet in the FEEDBACK-1 ACK is 0 (zero) ๐ซถ
5.11. ROHC UDP -- non-RTP UDP/IP compression (Profile 0x0002) ๐ฆ
okay so UDP/IP headers don't have a sequence number that's as well-behaved as the RTP Sequence Number ๐ญ For UDP/IPv4 there's an IP-ID field that may be echoed in feedback information, but when no IPv4 header is present, that feedback identification gets problematic ๐
So in the ROHC UDP profile, the compressor generates a 16-bit sequence number SN that goes up by one for each packet received in the packet stream ๐ธ This sequence number is relatively well-behaved and can serve as the basis for most mechanisms described for ROHC RTP. It's called SN or UDP SN below. Unless stated otherwise, the mechanisms of ROHC RTP are used for ROHC UDP too, with the UDP SN taking the role of the RTP Sequence Number ๐
The ROHC UDP profile always uses p = -1 when interpreting the SN, since there will be no repetitions or reordering of the compressor-generated SN. So the interpretation interval always starts with (ref_SN + 1) ๐ง
5.11.1. Initialization โจ
The static context for ROHC UDP streams can be initialized in either of two ways:
- By using an IR packet as in section 5.7.7.1, where the profile is two (2) and the static chain ends with the static part of an UDP packet. At the compressor, UDP SN is initialized to a random value when the IR packet is sent.
- By reusing an existing context where the existing static chain contains the static part of a UDP packet, e.g., the context of a stream compressed using ROHC RTP (profile 0x0001). This is done with an IR-DYN packet (section 5.7.7.2) identifying profile 0x0002, where the dynamic chain corresponds to the prefix of the existing static chain that ends with the UDP header. UDP SN is initialized to the RTP Sequence Number if the earlier profile was profile 0x0001, and to a random number otherwise.
For ROHC UDP, the dynamic part of a UDP packet is different from section 5.7.7.5: a two-octet field containing the UDP SN is added after the Checksum field ๐ This affects the format of dynamic chains in IR and IR-DYN packets.
Note: 2) can be used for packet streams that were initially assumed to be RTP streams, so compression started with profile 0x0001, but were later found evidently not to be RTP streams ๐
5.11.2. States and modes ๐
ROHC UDP uses the same states and modes as ROHC RTP. Mode transitions and state logic are the same, except when explicitly stated otherwise. Mechanisms dealing with fields in the RTP header (except the RTP SN) are not used. The decompressed UDP SN is never included in any header delivered to upper layers, and the UDP SN is used in place of the RTP SN in feedback ๐
5.11.3. Packet types ๐
The general format of a ROHC UDP packet is the same as for ROHC RTP (see beginning of section 5.7). Padding and CIDs are the same, as is the feedback packet type (5.7.6.1) and the feedback. IR and IR-DYN packets (5.7.7) are changed as described in 5.11.1 โจ
The general format of compressed packets is also the same, but there are differences in specific formats and extensions, as detailed below. The differences come from removing all RTP specific information except the RTP SN, which gets replaced by the UDP SN ๐ฆ
Unless explicitly stated below, the packet formats are as in sections 5.7.1-6 ๐ซถ
R-1 ๐ธ
The TS field is replaced by an IP-ID field. The M flag has become part of IP-ID. The X bit has moved. The formats R-1-ID and R-1-TS are not used.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 0 | SN |
+===+===+===+===+===+===+===+===+
| X | IP-ID |
+---+---+---+---+---+---+---+---+
UO-1 ๐ง
The TS field is replaced by an IP-ID field. The M flag has become part of SN. Formats UO-1-ID and UO-1-TS are not used.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 0 | IP-ID |
+===+===+===+===+===+===+===+===+
| SN | CRC |
+---+---+---+---+---+---+---+---+
UOR-2 ๐
New format:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 1 0 | SN |
+===+===+===+===+===+===+===+===+
| X | CRC |
+---+---+---+---+---+---+---+---+
5.11.4. Extensions ๐
Extensions are as in 5.7.5, with the following exceptions ๐ฅ
Extension 0:
+---+---+---+---+---+---+---+---+
| 0 0 | SN | IP-ID |
+---+---+---+---+---+---+---+---+
Extension 1:
+---+---+---+---+---+---+---+---+
| 0 1 | SN | IP-ID |
+---+---+---+---+---+---+---+---+
| IP-ID |
+---+---+---+---+---+---+---+---+
Extension 2:
+---+---+---+---+---+---+---+---+
| 1 0 | SN | IP-ID2 |
+---+---+---+---+---+---+---+---+
| IP-ID2 |
+---+---+---+---+---+---+---+---+
| IP-ID |
+---+---+---+---+---+---+---+---+
IP-ID2: for outer IP-ID field.
Extension 3 is the same as Extension 3 in section 5.7.5, with the following exceptions ๐
- The initial flag octet has the following format:
0 1 2 3 4 5 6 7
+-----+-----+-----+-----+-----+-----+-----+-----+
| 1 1 | S | Mode | I | ip | ip2 |
+-----+-----+-----+-----+-----+-----+-----+-----+
Mode: replaces R-TS and Tsc of 5.7.5. Provides mode information as was earlier done in RTP header flags and fields โจ
ip2: replaces rtp bit of 5.7.5. Moved here from the Inner IP header flags octet ๐
- The bit which was the ip2 flag in the Inner IP header flags in 5.7.5 is reserved. It is set to zero when sending and ignored when receiving ๐
5.11.5. IP-ID ๐ฆ
Treated as in ROHC RTP, but the offset is from UDP SN ๐
5.11.6. Feedback ๐ซถ
Feedback is as for ROHC RTP with the following exceptions ๐
- UDP SN replaces RTP SN in feedback.
- The CLOCK option (5.7.6.6) is not used.
- The JITTER option (5.7.6.7) is not used.
5.12. ROHC ESP -- ESP/IP compression (Profile 0x0003) ๐
When the ESP header is being used with an encryption algorithm other than NULL, the subheaders after the ESP header are encrypted and cannot be compressed. ๐ Profile 0x0003 exists for compressing the chain of headers up to and including the ESP header in that case. When the NULL encryption algorithm is being used, other profiles can be used and could give higher compression rates. โจ See section 5.8.4.3.
This profile is basically a cousin of ROHC UDP ๐ฆ It uses the ESP sequence number as the basis for compression instead of a generated number, but otherwise it's very similar to ROHC UDP. The interpretation interval (the value of p) for the ESP-based SN works the same way as in ROHC RTP (profile 0x0001). Apart from this, unless stated explicitly below, the mechanisms and formats are the same as for ROHC UDP. ๐
5.12.1. Initialization
The static context for ROHC ESP streams can be initialized in either of two ways: ๐ธ
- By using an IR packet as in section 5.7.7.1, where the profile is three (3) and the static chain ends with the static part of an ESP header.
- By reusing an existing context, where the existing static chain contains the static part of an ESP header. This is done with an IR-DYN packet (section 5.7.7.2) identifying profile
0x0003, where the dynamic chain corresponds to the prefix of the existing static chain that ends with the ESP header.
In contrast to ROHC UDP, no extra sequence number is added to the dynamic part of the ESP header. The ESP sequence number is the only element. ๐
Note: option 2 can be used for streams where compression was started under the assumption that NULL encryption was being used with ESP. ๐ When it becomes obvious that an encryption algorithm other than NULL is being used, the compressor may send an IR-DYN according to option 2 to switch to profile 0x0003 without having to send an IR packet. ๐ง
5.12.2. Packet types
The packet types for ROHC ESP are the same as for ROHC UDP, except that the ESP sequence number is used instead of the generated sequence number of ROHC UDP. The ESP header is not part of any compressed list in ROHC ESP. ๐
6. Implementation issues โจ
This document specifies mechanisms for the protocol and leaves many details of using those mechanisms to the implementers. ๐ซถ This chapter gives guidelines, ideas and suggestions for implementing the scheme.
6.1. Reverse decompression
This section describes an OPTIONAL decompressor operation to reduce the number of packets discarded due to an invalid context. ๐
Once a context becomes invalid (for example, when more consecutive packet losses than expected have occurred), subsequent compressed packets cannot immediately be decompressed correctly. ๐ญ Reverse decompression tries to decompress those packets later instead of throwing them away. It does this by storing them until the context has been updated and validated, and then attempting decompression.
Let the sequence of stored packets be i, i + 1, ..., i + k, where i is the first packet and i + k is the last packet before the context was updated. The decompressor will attempt to recover the stored packets in reverse order, i.e., starting with i + k and working back toward i. ๐ When a stored packet has been reconstructed, its correctness is verified using its CRC. Packets not carrying a CRC must not be delivered to upper layers. Packets where the CRC succeeds are delivered to upper layers in their original order, i.e., i, i + 1, ..., i + k. ๐
Reverse decompression adds buffering while waiting for the context to be validated, and that means extra delay. โณ So it should be used only when some amount of delay is acceptable. For example, for video packets belonging to the same video frame, the delay in packet arrivals does not cause presentation time delay. Delay-insensitive streaming applications can also be tolerant of such delay. If the decompressor cannot determine whether the application can tolerate delay, it should not perform reverse decompression.
Here's the decompression procedure in some detail: ๐
- The decompressor stores compressed packets that cannot be decompressed correctly due to an invalid context.
- When the decompressor has received a context updating packet and the context has been validated, it proceeds to recover the last packet stored. After decompression, the decompressor checks the correctness of the reconstructed header using the CRC.
- If the CRC indicates successful decompression, the decompressor stores the complete packet and attempts to decompress the preceding packet. In this way, the stored packets are recovered in reverse order until no compressed packets are left. For each packet, the decompressor checks the correctness of the decompressed headers using the header compression CRC.
- If the CRC indicates an incorrectly decompressed packet, the reverse decompression attempt MUST be terminated and all remaining uncompressed packets MUST be discarded. ๐ซ
- Finally, the decompressor forwards all the correctly decompressed packets to upper layers in their original order. ๐
6.2. RTCP
RTCP is the RTP Control Protocol [RTP]. ๐ RTCP is based on periodic transmission of control packets to all participants in a session, using the same distribution mechanism as for data packets. Its main job is to give feedback from the data receivers on the quality of the data distribution. ๐ That feedback may be used for congestion control functions, and it's directly useful for controlling adaptive encodings.
In an RTP session there are two types of packet streams: one with the RTP header and application data, and one with the RTCP control information. ๐ฆ At the transport level, the difference is in the UDP port numbers: the RTP port number is always even, and the RTCP port number is that number plus one, so it's always odd [RTP, section 10]. The ROHC header compressor implementation has several ways to handle the RTCP stream: ๐ธ
- One compressor/decompressor entity carries both types of streams on the same channel, using CIDs to tell them apart. For sending a single RTP stream together with its RTCP packets on one channel, it is most efficient to set
LARGE_CIDSto false, send the RTP packets with the implied CID 0, and use the Add-CID mechanism to send the RTCP packets. ๐ง - Two compressor/decompressor entities, one for RTP and another one for RTCP, carry the two types of streams on separate channels. This means they will not share the same CID number space. ๐
RTCP headers may simply be sent uncompressed using profile 0x0000. More efficiently, ROHC UDP compression (profile 0x0002) can be used. ๐
6.3. Implementation parameters and signals
A ROHC implementation may have two kinds of parameters: configuration parameters that are mandatory and must be negotiated between compressor and decompressor peers, and implementation parameters that are optional and, when used, stipulate how a ROHC implementation is to operate. โ๏ธ
Configuration parameters are mandatory and must be negotiated between compressor and decompressor, so that they have the same values at both compressor and decompressor. See section 5.1.1. ๐ซถ
Implementation parameters make it possible for an external entity to stipulate how an implementation of a ROHC compressor or decompressor should operate. Implementation parameters have local significance, are optional to use, and are thus not necessary to negotiate between compressor and decompressor. Note that this does not preclude signaling or negotiating implementation parameters using lower layer functionality in order to set the way a ROHC implementation should operate. Some implementation parameters are valid only at either the compressor or the decompressor. Implementation parameters may further be divided into parameters that allow an external entity to describe the way the implementation should operate, and parameters that allow an external entity to trigger a specific event, i.e., signals. ๐ฅ
6.3.1. ROHC implementation parameters at compressor โจ
okay bestie, the compressor gets its own set of knobs ๐
CONTEXT_REINITIALIZATION -- signal
This one is the big red button ๐ฅ It triggers a full reinitialization of the whole context at the decompressor, both the static and the dynamic part. So the compressor MUST, when CONTEXT_REINITIALIZATION gets triggered, back off to the IR state ๐ and rebuild everything from scratch by sending IR packets, with both the static and dynamic chains covering the entire uncompressed headers ๐ฆ. It keeps doing that until it's reasonably confident the decompressor contexts are reinitialized โ
. And the context reinitialization MUST be done for all contexts at the compressor, no exceptions ๐
This can be used for context relocation at, e.g., a cellular handover that results in a change of compression point in the radio access network ๐ฑ
NO_OF_PACKET_SIZES_ALLOWED -- value: positive integer
An external entity can set this to limit how many packet sizes the ROHC implementation may use ๐ธ The catch is that it only works if no external entity is using PACKET_SIZES. With this set, the ROHC implementation at the compressor MUST NOT use more different packet sizes than this value says. The implementation must itself be able to figure out which packet sizes it'll use and report them back to the external entity using PACKET_SIZES_USED. Also, it should be noted that one packet size might be used for several header formats, and the number of packet sizes can be reduced by using padding and segmentation ๐งฉ
NO_OF_PACKET_SIZES_USED -- value: positive integer
This is set by the ROHC implementation to show how many packet sizes it'll actually use ๐ It can be set to a large value to show that no real effort was made to keep that number small ๐
PACKET_SIZES_ALLOWED -- value: list of positive integers (bytes)
If this one is set, it decides which packet sizes in bytes the ROHC implementation may use ๐ So packet sizes that aren't in this set MUST NOT be used. This is how an external entity can make the implementation produce packet sizes that fit pre-configured lower layers better ๐ฏ If this parameter is used to pick the allowed sizes, these rules apply:
- A packet large enough to hold the entire IR header (both static and dynamic chain) MUST be part of the set of sizes, unless
MRRUis set to a large enough value to allow segmentation. - The packet size likely to be used most frequently in the SO state SHOULD be part of the set.
- The packet size likely to be used most frequently in the FO state SHOULD be part of the set.
PACKET_SIZES_USED -- values: set of positive integers (bytes)
This one describes which packet sizes the ROHC implementation actually uses ๐ It kicks in when NO_OF_PACKET_SIZES_ALLOWED or PACKET_SIZES_ALLOWED is used by an external entity to say how many packet sizes the implementation should use. The info in here can then be used to configure lower layers ๐ง
PAYLOAD_SIZES -- values: set of positive integer values (bytes)
An external entity that wants to use PACKET_SIZES_USED sets this one to say which payload sizes it can expect ๐ฆ
Now, when the ROHC implementation has a limited set of allowed packet sizes and its most preferable header format isn't in that set, it's got a few options ๐ค
- Choose the next larger header format from the allowed set. This is probably the most efficient choice.
- Use the most preferable header format as if there were no size restrictions, then add padding octets to complete a packet of the next larger size in the allowed set.
- Use segmentation to fragment the packet into pieces that would make up packets of permissible sizes (possibly after adding padding to the last segment).
Just a heads up though: even if the last two parameters let you restrict how many packet sizes get used, those restrictions will have a negative impact on compression performance ๐ so it's a trade-off, babe โ๏ธ
6.3.2. ROHC implementation parameters at decompressor ๐
MODE -- values: [U-mode, O-mode, R-mode]
This one triggers a mode transition using the mechanism described in chapter 5 whenever its value changes ๐ฆ The options are U-mode (Unidirectional mode), O-mode (Bidirectional Optimistic mode), or R-mode (Bidirectional Reliable mode). The transition goes from the current mode to whatever new mode the implementation parameter signals. For example, if the current mode is Bidirectional Optimistic mode, MODE should have the value O-mode. If MODE is changed to R-mode, a mode transition MUST be made from Bidirectional Optimistic mode to Bidirectional Reliable mode. And MODE should not only be a trigger for transitions, it also makes it visible which mode ROHC is operating in ๐
CLOCK_RESOLUTION -- value: nonnegative integer
This tells you the system clock resolution in milliseconds โฑ๏ธ A zero (0) value means there's no clock available. If it's nonzero, the decompressor can use timer-based TS compression (section 4.5.4) and SN wraparound detection (section 5.3.2.2.4). And when it's nonzero, its specific value actually matters for the algorithms to be correct ๐ง
REVERSE_DECOMPRESSION_DEPTH -- value: nonnegative integer
This decides whether reverse decompression, as described in section 6.1, gets used at all, and if so how far it goes ๐ The value is the maximum number of packets that can be buffered and possibly reverse decompressed by the decompressor. A zero (0) value means reverse decompression MUST NOT be used ๐ โโ๏ธ
6.4. Handling of resource limitations at the decompressor ๐ง
Okay so on a point-to-point link, the two nodes can agree on how many compressed sessions they're ready to support ๐ค But here's the tea: the decompressor can't always predict exactly when it'll run out of resources ๐ฌ So ROHC lets the negotiated number of contexts be larger than could be handled in the worst case. As context resources get used up, an attempt to set up a new context might get rejected by the decompressor, using the REJECT option of the feedback payload ๐
โโ๏ธ
When a REJECT option comes in, the compressor SHOULD wait for a while before trying to compress more streams headed for the node that rejected it โณ Patience is a virtue, bestie ๐ซถ
6.5. Implementation structures ๐๏ธ
This section is some explanatory material on the data structures a ROHC implementation will have to maintain in one form or another ๐งฑ It's not meant to constrain the implementations, so don't stress about matching it exactly ๐
6.5.1. Compressor context ๐๏ธ
The compressor context has a static part and a dynamic part ๐ The static part has the same content as the static chain defined in section 5.7.7. The dynamic part is made of multiple elements, which fall into four types:
- a) Sliding Window (SW)
- b) Translation Table (TT)
- c) Flag
- d) Field
These elements may be common to all modes or mode specific. Here's the table that summarizes all of them ๐
+--------+---------------------------+-------------+----------------+
| | Common to | Specific to | Specific to |
| | all modes | R-mode | U/O-mode |
+--------+---------------------------+-------------+----------------+
| SWs | GSW | R_CSW | UO_CSW |
| | | R_IESW | UO_IESW |
+--------+---------------------------+-------------+----------------+
| TTs | | R_CTT | UO_CTT |
| | | R_IETT | UO_IETT |
+--------+---------------------------+-------------+----------------+
| Flags | UDP Chksum | | ACKED |
| | TSS, TIS | | |
| | RND, RND2 | | |
| | NBO, NBO2 | | |
+--------+---------------------------+-------------+----------------+
| Fields | Profile | | CSRC_REF_ID |
| | C_MODE | | CSRC_GEN_ID |
| | C_STATE | | CSRC_GEN_COUNT |
| | C_TRANS | | IPEH_REF_ID |
| | TS_STRIDE (if TSS = 1) | | IPEH_GEN_ID |
| | TS_OFFSET (if TSS = 1) | | IPEH_GEN_COUNT |
| | TIME_STRIDE (if TIS = 1) | | |
| | CURR_TIME (if TIS = 1) | | |
| | MAX_JITTER_CD (if TIS = 1)| | |
| | LONGEST_LOSS_EVENT(O) | | |
| | CLOCK_RESOLUTION(O) | | |
| | MAX_JITTER(O) | | |
+--------+---------------------------+-------------+----------------+
Let's go through the elements one by one ๐
- 1) GSW: Generic
W_LSBSliding Window- Each element in GSW consists of all the dynamic fields in the dynamic chain (defined in section 5.7.7) plus the fields specified in a) but excluding the fields specified in b).
- a) Packet Arrival Time (if TIS = 1)
- a) Scaled RTP Time Stamp (if TSS = 1) (optional)
- a) Offset_i (if RND = 0) (optional)
- b) UDP Checksum, TS Stride, CSRC list, IPv6 Extension Headers
- 2) R_CSW: CSRC Sliding Window in R-mode
- R_IESW: IPv6 Extension Header Sliding Window in R-mode
- UO_CSW: CSRC Sliding Window in U/O-mode
- UO_IESW: IPv6 Extension Header Sliding Window in U/O-mode
- Each element in R_CSW, R_IESW, UO_CSW and UO_IESW is defined in section 6.5.3.
- 3) R_CTT: CSRC Translation Table in R-mode
- R_IETT: IPv6 Extension Header Translation Table in U/O-mode
- UO_CTT: CSRC Translation Table in U/O-mode
- UO_IETT: IPv6 Extension Header Translation Table in U/O-mode
- Each element in R_CTT and R_IETT is defined in section 5.8.1.1. Each element in UO_CTT and UO_IETT is defined in section 5.8.1.2.
- 4) ACKED: Indicates whether or not the decompressor has ever acked
- 5) CURR_TIME: The current time value (used for context relocation when timer-based timestamp compression is used) โฐ
- 6) All the other flags and fields are defined elsewhere in the ROHC document ๐
6.5.2. Decompressor context ๐
Okay so the decompressor context is split into two bits: a static part and a dynamic part ๐ง. The static part holds the same stuff as the static chain from section 5.7.7. The dynamic part is made of a bunch of elements, and the star of the show is the nonstatic reference header, which holds all the nonstatic fields. Those nonstatic fields are the fields in the dynamic chain from section 5.7.7, minus UDP Checksum and TS_Stride (they're not invited to this party ๐ ). Everything else sorts into four types:
- a) Sliding Window (SW)
- b) Translation Table (TT)
- d) Flag
- e) Field
These elements can be mode specific or common to all modes. Here's the lowdown in table form ๐:
+--------+---------------------------+-------------+----------------+
| | Common to | Specific to | Specific to |
| | all modes | R-mode | U/O-mode |
+--------+---------------------------+-------------+----------------+
| SWs | | R_CSW | UO_CSW |
| | | R_IESW | UO_IESW |
+--------+---------------------------+-------------+----------------+
| TTs | | R_CTT | UO_CTT |
| | | R_IETT | UO_IETT |
+--------+---------------------------+-------------+----------------+
| Flags | UDP Checksum | | ACKED |
| | TSS, TIS | | |
| | RND, RND2 | | |
| | NBO, NBO2 | | |
+--------+---------------------------+-------------+----------------+
| Fields | Profile | | CSRC_GEN_ID |
| | D_MODE | | IPEH_GEN_ID |
| | D_STATE | | PRE_SN_V_REF |
| | D_TRANS | | |
| | TS_STRIDE (if TSS = 1) | | |
| | TS_OFFSET (if TSS = 1) | | |
| | TIME_STRIDE (if TIS = 1) | | |
| | PKT_ARR_TIME (if TIS = 1) | | |
| | LONGEST_LOSS_EVENT(O) | | |
| | CLOCK_RESOLUTION(O) | | |
| | MAX_JITTER(O) | | |
+--------+---------------------------+-------------+----------------+
- ACKED: Indicates whether or not ACK has ever been sent. โ
- PKT_ARR_TIME: The arrival time of the packet that most recently decompressed and verified using CRC. โฐ
PRE_SN_V_REF: The sequence number of the packet verified before the most recently verified packet.
CSRC_GEN_ID: The CSRC gen_id of the most recently received packet.
IPEH_GEN_ID: The IPv6 Extension Header gen_id of the most recently received packet.
- The remaining elements are as defined in the compressor context. ๐ซถ
6.5.3. List compression: Sliding windows in R-mode and U/O-mode ๐ธ
In R-mode list compression (see section 5.8.2.1), each entry in the sliding window, on both the compressor side and the decompressor side, has this structure:
+---------------------+--------+------------+
| RTP Sequence Number | icount | index list |
+---------------------+--------+------------+
The table index list holds a list of index. Each of these index points to the item in the original list carried in the packet identified by the RTP Sequence Number. The mapping between the index and the item is identified in the translation table. The icount field carries the number of index in the following index list. Iconic ๐ง
In U/O-mode list compression, each entry in the sliding window at both the compressor side and the decompressor side has this structure:
+--------+--------+------------+
| Gen_id | icount | index list |
+--------+--------+------------+
The icount and index list fields are the same as in R-mode. The only difference is that instead of using the RTP Sequence Number to identify each entry, U/O-mode includes the Gen_id in the sliding window. โจ
7. Security Considerations ๐ฅ
Because encryption eliminates the redundancy that header compression schemes try to exploit, there is some inducement to forego encryption of headers in order to enable operation over low-bandwidth links. However, for those cases where encryption of data (and not headers) is sufficient, RTP does specify an alternative encryption method in which only the RTP payload is encrypted and the headers are left in the clear. That would still allow header compression to be applied. ๐
ROHC compression is transparent with regard to the RTP Sequence Number and RTP Timestamp fields, so the values of those fields can be used as the basis of payload encryption schemes (e.g., for computation of an initialization vector). ๐
๐ญ Okay this part is SERIOUS bestie. A malfunctioning or malicious header compressor could cause the header decompressor to reconstitute packets that do not match the original packets but still have valid IP, UDP and RTP headers and possibly also valid UDP checksums. Such corruption may be detected with end-to-end authentication and integrity mechanisms, which will not be affected by the compression. Moreover, this header compression scheme uses an internal checksum for verification of reconstructed headers. This reduces the probability of producing decompressed headers that don't match the original ones without this being noticed.
Denial-of-service attacks are possible if an intruder can introduce (for example) bogus STATIC, DYNAMIC or FEEDBACK packets onto the link and thereby cause compression efficiency to be reduced. However, an intruder who can inject arbitrary packets at the link layer in this manner raises additional security issues that dwarf those related to the use of header compression. ๐
8. IANA Considerations ๐
The ROHC profile identifier is a non-negative integer. In many negotiation protocols, it will be represented as a 16-bit value. Due to the way the profile identifier is abbreviated in ROHC packets, the 8 least significant bits of the profile identifier have a special significance: two profile identifiers with identical 8 LSBs should be assigned only if the higher-numbered one is intended to supersede the lower-numbered one. To highlight this relationship, profile identifiers should be given in hexadecimal (as in 0x1234, which would for example supersede 0x0A34). ๐ฆ
Following the policies outlined in [IANA-CONSIDERATIONS], the IANA policy for assigning new values for the profile identifier shall be Specification Required: values and their meanings must be documented in an RFC or in some other permanent and readily available reference, in sufficient detail that interoperability between independent implementations is possible. In the 8 LSBs, the range 0 to 127 is reserved for IETF standard-track specifications, and the range 128 to 254 is available for other specifications that meet this requirement (such as Informational RFCs). The LSB value 255 is reserved for future extensibility of the present specification. ๐
The following profile identifiers are already allocated:
Profile Document Usage
identifier
0x0000 RFCthis ROHC uncompressed
0x0001 RFCthis ROHC RTP
0x0002 RFCthis ROHC UDP
0x0003 RFCthis ROHC ESP
9. Acknowledgments ๐ซถ
Earlier header compression schemes described in [CJHC], [IPHC], and [CRTP] have been important sources of ideas and knowledge. ๐ง
The editor would like to extend his warmest thanks to Mikael Degermark, who actually did a lot of the editing work, and Peter Eriksson, who made a copy editing pass through the document, significantly increasing its editorial consistency. Of course, all remaining editorial problems have then been inserted by the editor. ๐
Thanks to Andreas Jonsson (Lulea University), who supported this work by his study of header field change patterns. ๐
Finally, this work would not have succeeded without the continual advice in navigating the IETF standards track, garnished with both editorial and technical comments, from the IETF transport area directors, Allison Mankin and Scott Bradner. ๐
10. Intellectual Property Right Claim Considerations โญ
The IETF has been notified of intellectual property rights claimed in regard to some or all of the specification contained in this document. For more information, consult the online list of claimed rights. ๐
The IETF takes no position regarding the validity or scope of any intellectual property or other rights that might be claimed to pertain to the implementation or use of the technology described in this document, or the extent to which any license under such rights might or might not be available. Neither does it represent that it has made any effort to identify any such rights. Information on the IETF's procedures with respect to rights in standards-track and standards-related documentation can be found in BCP-11. Copies of claims of rights made available for publication, and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementors or users of this specification, can be obtained from the IETF Secretariat. ๐งพ
The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights which may cover technology that may be required to practice this standard. Please address the information to the IETF Executive Director. ๐
11. References ๐
11.1. Normative References ๐โจ
Okay so these are the references the spec needs you to have read, bestie ๐ค
[UDP] Postel, J., "User Datagram Protocol", STD 6, RFC 768, August 1980.
[IPv4] Postel, J., "Internet Protocol", STD 5, RFC 791, September 1981.
[IPv6] Deering, S. and R. Hinden, "Internet Protocol, Version 6 (IPv6) Specification", RFC 2460, December 1998.
[RTP] Schulzrinne, H., Casner, S., Frederick, R. and V. Jacobson, "RTP: A Transport Protocol for Real-Time Applications", RFC 1889, January 1996.
[HDLC] Simpson, W., "PPP in HDLC-like framing", STD 51, RFC 1662, July 1994.
[ESP] Kent, S. and R. Atkinson, "IP Encapsulating Security Payload", RFC 2406, November 1998.
[NULL] Glenn, R. and S. Kent, "The NULL Encryption Algorithm and Its Use With Ipsec", RFC 2410, November 1998.
[AH] Kent, S. and R. Atkinson, "IP Authentication Header", RFC 2402, November 1998.
[MINE] Perkins, C., "Minimal Encapsulation within IP", RFC 2004, October 1996.
[GRE1] Farinacci, D., Li, T., Hanks, S., Meyer, D. and P. Traina, "Generic Routing Encapsulation (GRE)", RFC 2784, March 2000.
[GRE2] Dommety, G., "Key and Sequence Number Extensions to GRE", RFC 2890, August 2000.
[ASSIGNED] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC 1700, October 1994.
11.2. Informative References ๐
These ones are the extra reading, the "ooh, go deeper" list ๐
[VJHC] Jacobson, V., "Compressing TCP/IP Headers for Low-Speed Serial Links", RFC 1144, February 1990.
[IPHC] Degermark, M., Nordgren, B. and S. Pink, "IP Header Compression", RFC 2507, February 1999.
[CRTP] Casner, S. and V. Jacobson, "Compressing IP/UDP/RTP Headers for Low-Speed Serial Links", RFC 2508, February 1999.
[CRTPC] Degermark, M., Hannu, H., Jonsson, L.E., Svanbro, K., "Evaluation of CRTP Performance over Cellular Radio Networks", IEEE Personal Communication Magazine, Volume 7, number 4, pp. 20-25, August 2000.
[REQ] Degermark, M., "Requirements for robust IP/UDP/RTP header compression", RFC 3096, June 2001.
[LLG] Svanbro, K., "Lower Layer Guidelines for Robust RTP/UDP/IP Header Compression", Work in Progress.
[IANA-CONSIDERATIONS] Alvestrand, H. and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 2434, October 1998.
12. Authors' Addresses ๐
Okay bestie, here's everyone who worked on this one, with their contact details ๐ซถ
Carsten Bormann, Editor
Universitaet Bremen TZI
Postfach 330440
D-28334 Bremen, Germany
Phone: +49 421 218 7024
Fax: +49 421 218 7000
EMail: cabo@tzi.org
Carsten Burmeister
Panasonic European Laboratories GmbH
Monzastr. 4c
63225 Langen, Germany
Phone: +49-6103-766-263
Fax: +49-6103-766-166
EMail: burmeister@panasonic.de
Mikael Degermark
The University of Arizona
Dept of Computer Science
P.O. Box 210077
Tucson, AZ 85721-0077, USA
Phone: +1 520 621-3498
Fax: +1 520 621-4642
EMail: micke@cs.arizona.edu
Hideaki Fukushima
Matsushita Electric Industrial Co.,
Ltd006, Kadoma, Kadoma City,
Osaka, Japan
Phone: +81-6-6900-9192
Fax: +81-6-6900-9193
EMail: fukusima@isl.mei.co.jp
Hans Hannu
Box 920
Ericsson Erisoft AB
SE-971 28 Lulea, Sweden
Phone: +46 920 20 21 84
Fax: +46 920 20 20 99
EMail: hans.hannu@ericsson.com
Lars-Erik Jonsson
Box 920
Ericsson Erisoft AB
SE-971 28 Lulea, Sweden
Phone: +46 920 20 21 07
Fax: +46 920 20 20 99
EMail: lars-erik.jonsson@ericsson.com
Rolf Hakenberg
Panasonic European Laboratories GmbH
Monzastr. 4c
63225 Langen, Germany
Phone: +49-6103-766-162
Fax: +49-6103-766-166
EMail: hakenberg@panasonic.de
Tmima Koren
Cisco Systems, Inc.
170 West Tasman Drive
San Jose, CA 95134, USA
Phone: +1 408-527-6169
EMail: tmima@cisco.com
Khiem Le
2-700
Mobile Networks Laboratory
Nokia Research Center
6000 Connection Drive
Irving, TX 75039, USA
Phone: +1-972-894-4882
Fax: +1 972 894-4589
EMail: khiem.le@nokia.com
Zhigang Liu
2-700
Mobile Networks Laboratory
Nokia Research Center
6000 Connection Drive
Irving, TX 75039, USA
Phone: +1 972 894-5935
Fax: +1 972 894-4589
EMail: zhigang.liu@nokia.com
Anton Martensson
Ericsson Radio Systems AB
Torshamnsgatan 23
SE-164 80 Stockholm, Sweden
Phone: +46 8 404 3881
Fax: +46 8 757 5550
EMail: anton.martensson@era.ericsson.se
Akihiro Miyazaki
Matsushita Electric Industrial Co., Ltd
1006, Kadoma, Kadoma City, Osaka, Japan
Phone: +81-6-6900-9192
Fax: +81-6-6900-9193
EMail: akihiro@isl.mei.co.jp
Krister Svanbro
Box 920
Ericsson Erisoft AB
SE-971 28 Lulea, Sweden
Phone: +46 920 20 20 77
Fax: +46 920 20 20 99
EMail: krister.svanbro@ericsson.com
Thomas Wiebke
Panasonic European Laboratories GmbH
Monzastr. 4c
63225 Langen, Germany
Phone: +49-6103-766-161
Fax: +49-6103-766-166
EMail: wiebke@panasonic.de
Takeshi Yoshimura
NTT DoCoMo, Inc.
3-5, Hikarinooka
Yokosuka, Kanagawa, 239-8536, Japan
Phone: +81-468-40-3515
Fax: +81-468-40-3788
EMail: yoshi@spg.yrp.nttdocomo.co.jp
Haihong Zheng
2-700
Mobile Networks Laboratory
Nokia Research Center
6000 Connection Drive
Irving, TX 75039, USA
Phone: +1 972 894-4232
Fax: +1 972 894-4589
EMail: haihong.zheng@nokia.com
Appendix A. Detailed classification of header fields ๐ง
Okay so header compression is possible because most header fields don't change randomly from packet to packet ๐ฆ Lots of them are static or change in a pretty predictable way ๐ That means when you design a scheme, you really need to understand how each field behaves, in detail ๐ค
Appendix A works in two steps. First, A.1 gives a general classification, where fields are sorted based on stable knowledge and assumptions. That step doesn't account for how the changing fields behave, since that depends a lot on the implementation and the application. A less stable but more detailed look at those change characteristics comes next, in A.2. Finally, A.3 wraps everything up with conclusions on how each header field should be handled by the header compression scheme, so compression works well and the functionality still works ๐
A.1. General classification โจ
At a general level, header fields are split into 5 classes ๐
INFERRED โ these fields hold values that can be inferred from other values, like the size of the frame carrying the packet. So the compression scheme doesn't have to handle them at all.
STATIC โ these fields are expected to stay constant for the whole lifetime of the packet stream. Static info must in some way be communicated once.
STATIC-DEF โ STATIC fields whose values define a packet stream. They're generally handled as STATIC ๐ธ
STATIC-KNOWN โ these STATIC fields are expected to have well-known values, so they don't need to be communicated at all ๐
CHANGING โ these fields are expected to vary in some way: randomly, within a limited value set or range, or in some other manner ๐
In this section, every IP, UDP and RTP header field gets assigned to one of these classes. For all the fields except the CHANGING ones, we also explain the motives behind the classification. Then in A.2, CHANGING fields get a closer look and are sorted by their expected change behavior ๐
A.1.1. IPv6 header fields ๐ธ
Here's the IPv6 table, bestie ๐
+---------------------+-------------+----------------+
| Field | Size (bits) | Class |
+---------------------+-------------+----------------+
| Version | 4 | STATIC |
| Traffic Class | 8 | CHANGING |
| Flow Label | 20 | STATIC-DEF |
| Payload Length | 16 | INFERRED |
| Next Header | 8 | STATIC |
| Hop Limit | 8 | CHANGING |
| Source Address | 128 | STATIC-DEF |
| Destination Address | 128 | STATIC-DEF |
+---------------------+-------------+----------------+
Version
The version field says which IP version is in use. Packets with different values in this field must be handled by different IP stacks, so every packet in a stream must be the same IP version. That's why it's classified as STATIC ๐ฆ
Flow Label
This field may be used to identify packets belonging to a specific packet stream. If it's not used, the value should be set to zero. Otherwise, all packets in the same stream must have the same value in this field, since it's one of the things that defines the stream. So it's STATIC-DEF โจ
Payload Length
Info about packet length (and so payload length) is expected to come from the link layer, so this one is INFERRED ๐
Next Header
This usually has the same value in every packet of a stream, and it encodes the type of the subsequent header. It only changes during the stream's lifetime when extension headers are sometimes present and sometimes not. So it's STATIC ๐
Source and Destination addresses
These fields are part of the definition of a stream, so they must be constant for all packets in it. They're classified as STATIC-DEF ๐ซถ
Here's the total size of the fields in each class, bestie ๐
+--------------+--------------+
| Class | Size (octets)|
+--------------+--------------+
| INFERRED | 2 |
| STATIC | 1.5 |
| STATIC-DEF | 34.5 |
| CHANGING | 2 |
+--------------+--------------+
A.1.2. IPv4 header fields โจ
Okay bestie, the IPv4 header is up next ๐ธ Here's how every field gets classified:
+---------------------+-------------+----------------+
| Field | Size (bits) | Class |
+---------------------+-------------+----------------+
| Version | 4 | STATIC |
| Header Length | 4 | STATIC-KNOWN |
| Type Of Service | 8 | CHANGING |
| Packet Length | 16 | INFERRED |
| Identification | 16 | CHANGING |
| Reserved flag | 1 | STATIC-KNOWN |
| Don't Fragment flag | 1 | STATIC |
| More Fragments flag | 1 | STATIC-KNOWN |
| Fragment Offset | 13 | STATIC-KNOWN |
| Time To Live | 8 | CHANGING |
| Protocol | 8 | STATIC |
| Header Checksum | 16 | INFERRED |
| Source Address | 32 | STATIC-DEF |
| Destination Address | 32 | STATIC-DEF |
+---------------------+-------------+----------------+
Version ๐
The version field tells you which IP version is in use. Packets with different values here must be handled by different IP stacks, so every packet in a stream has to be the same IP version. That makes it STATIC ๐
Header Length ๐
As long as no options are present in the IP header, the header length is constant and well known. If options did show up, the fields would be STATIC, but here it's assumed there are no options, so the field is STATIC-KNOWN โจ
Packet Length ๐ง
Info about packet length is expected to come from the link layer, so this one is INFERRED ๐ฆ
Flags ๐
The Reserved flag must be set to zero, so it's STATIC-KNOWN ๐ซถ The Don't Fragment (DF) flag stays the same for every packet in a stream, so it's STATIC ๐
Then there's the More Fragments (MF) flag. It's expected to be zero because not much fragmentation is expected, thanks to the small packet size. So it's STATIC-KNOWN too ๐
Fragment Offset ๐ธ
If no fragmentation happens, the fragment offset is always zero, which makes it STATIC-KNOWN ๐
Protocol ๐
This field usually has the same value across all packets in a stream. It encodes the type of the header that comes next. Only when extension headers are sometimes there and sometimes not does the value change during a stream's lifetime. So it's STATIC โจ
Header Checksum ๐
The header checksum protects each hop from processing a corrupted header. But when almost all of the IP header info has been compressed away, keeping this checksum is pointless. It can be regenerated on the decompressor side instead, so it's INFERRED ๐ง
Source and Destination addresses ๐
These fields are part of what defines a stream, so they must stay constant for every packet in it. That's a clear STATIC-DEF situation ๐ฆ
Total size of the fields in each class:
+--------------+----------------+
| Class | Size (octets) |
+--------------+----------------+
| INFERRED | 4 |
| STATIC | 1 oct + 5 bits |
| STATIC-DEF | 8 |
| STATIC-KNOWN | 2 oct + 3 bits |
| CHANGING | 4 |
+--------------+----------------+
A.1.3. UDP header fields ๐ฆ
UDP is a much simpler vibe ๐ Here's the breakdown:
+------------------+-------------+-------------+
| Field | Size (bits) | Class |
+------------------+-------------+-------------+
| Source Port | 16 | STATIC-DEF |
| Destination Port | 16 | STATIC-DEF |
| Length | 16 | INFERRED |
| Checksum | 16 | CHANGING |
+------------------+-------------+-------------+
Source and Destination ports ๐ธ
These fields are part of the stream definition, so they must stay constant for every packet in the stream. That makes them STATIC-DEF ๐
Length ๐ง
This field is redundant, so it's INFERRED ๐
Total size of the fields in each class:
+------------+---------------+
| Class | Size (octets) |
+------------+---------------+
| INFERRED | 2 |
| STATIC-DEF | 4 |
| CHANGING | 2 |
+------------+---------------+
A.1.4. RTP header fields ๐
RTP is next, and it has some fun little quirks ๐
+-----------------+-------------+----------------+
| Field | Size (bits) | Class |
+-----------------+-------------+----------------+
| Version | 2 | STATIC-KNOWN |
| Padding | 1 | STATIC |
| Extension | 1 | STATIC |
| CSRC Counter | 4 | CHANGING |
| Marker | 1 | CHANGING |
| Payload Type | 7 | CHANGING |
| Sequence Number | 16 | CHANGING |
| Timestamp | 32 | CHANGING |
| SSRC | 32 | STATIC-DEF |
| CSRC | 0(-480) | CHANGING |
+-----------------+-------------+----------------+
Version ๐
Only one working RTP version exists, and that's version 2. So it's STATIC-KNOWN โจ
Padding ๐ซถ
Using this field depends on the application. But when payload padding is used, it's likely to be present in every packet, so it's STATIC ๐
Extension ๐
If the application uses RTP extensions, they're likely to show up in every packet. Extensions are very uncommon, though. For safety's sake, this field is classified as STATIC and not STATIC-KNOWN ๐ก๏ธ
SSRC ๐ฆ
This field is part of the stream definition, so it must stay constant for every packet in the stream. It's STATIC-DEF ๐
Total size of the fields in each class:
+--------------+---------------+
| Class | Size (octets) |
+--------------+---------------+
| STATIC | 2 bits |
| STATIC-DEF | 4 |
| STATIC-KNOWN | 2 bits |
| CHANGING | 7.5(-67.5) |
+--------------+---------------+
A.1.5. Summary for IP/UDP/RTP ๐
Okay, the grand finale ๐ Here's the full IP/UDP/RTP picture:
+----------------+----------------+----------------+
| Class \ IP ver | IPv6 (octets) | IPv4 (octets) |
+----------------+----------------+----------------+
| INFERRED | 4 | 6 |
| STATIC | 1 oct + 6 bits | 1 oct + 7 bits |
| STATIC-DEF | 42.5 | 16 |
| STATIC-KNOWN | 2 bits | 2 oct + 5 bits |
| CHANGING | 11.5(-71.5) | 13.5(-73.5) |
+----------------+----------------+----------------+
| Total | 60(-120) | 40(-100) |
+----------------+----------------+----------------+
Main character energy, fr fr ๐๐โจ
A.2. Analysis of change patterns of header fields ๐ โจ
okay so if we want to compress every header field well, we have to know how each one changes ๐ง That's why we do an extended classification. It builds on the general one in A.1 and zooms in on the fields that got labeled CHANGING there. Different apps use fields in different ways, and that can change how they act ๐ For the variable ones, we'll talk about what usually happens with conversational audio and video ๐ฆ
The CHANGING fields get sorted into five subclasses:
STATIC: fields that were CHANGING in the general pass but are STATIC here because of some extra assumptions ๐ธSEMISTATIC: STATIC most of the time, but now and then the value changes and then goes back to the original after a known number of packets ๐RARELY-CHANGING (RC): they change every so often and then keep the new value ๐ALTERNATING: they bounce between a small number of different values ๐IRREGULAR: the leftovers, where no useful change pattern can be found ๐ญ
To get more classification options without making things messier, you can classify by the field's values, by the deltas of the field's values, or by both ๐งฉ
Once it's classified, we also note what extra knowledge we might have about the field values and/or deltas. For STATIC or SEMISTATIC fields, the value might not just be STATIC but also well KNOWN a priori (two states for SEMISTATIC fields) โจ For fields that don't behave irregularly, changes are usually within a LIMITED range compared to the maximum possible change for that field ๐ For everything else, the values are completely UNKNOWN ๐คทโโ๏ธ
Table A.1 sorts all the CHANGING fields by their expected change patterns, especially for conversational audio and video ๐
+------------------------+-------------+-------------+-------------+
| Field | Value/Delta | Class | Knowledge |
+========================+=============+=============+=============+
| Sequential | Delta | STATIC | KNOWN |
| -----------+-------------+-------------+-------------+
| IPv4 Id: Seq. jump | Delta | RC | LIMITED |
| -----------+-------------+-------------+-------------+
| Random | Value | IRREGULAR | UNKNOWN |
+------------------------+-------------+-------------+-------------+
| IP TOS / Tr. Class | Value | RC | UNKNOWN |
+------------------------+-------------+-------------+-------------+
| IP TTL / Hop Limit | Value | ALTERNATING | LIMITED |
+------------------------+-------------+-------------+-------------+
| Disabled | Value | STATIC | KNOWN |
| UDP Checksum: ---------+-------------+-------------+-------------+
| Enabled | Value | IRREGULAR | UNKNOWN |
+------------------------+-------------+-------------+-------------+
| No mix | Value | STATIC | KNOWN |
| RTP CSRC Count: -------+-------------+-------------+-------------+
| Mixed | Value | RC | LIMITED |
+------------------------+-------------+-------------+-------------+
| RTP Marker | Value | SEMISTATIC | KNOWN/KNOWN |
+------------------------+-------------+-------------+-------------+
| RTP Payload Type | Value | RC | UNKNOWN |
+------------------------+-------------+-------------+-------------+
| RTP Sequence Number | Delta | STATIC | KNOWN |
+------------------------+-------------+-------------+-------------+
| RTP Timestamp | Delta | RC | LIMITED |
+------------------------+-------------+-------------+-------------+
| No mix | - | - | - |
| RTP CSRC List: -------+-------------+-------------+-------------+
| Mixed | Value | RC | UNKNOWN |
+------------------------+-------------+-------------+-------------+
Table A.1 : Classification of CHANGING header fields
Heads up: the table and the discussions below don't account for changes caused by loss or reordering before the compression point โ ๏ธ
A.2.1. IPv4 Identification ๐
The Identification field (IP ID) in the IPv4 header is there to tell which fragments belong together when fragmented datagrams get reassembled ๐งฉ The IPv4 spec doesn't say exactly how this field gets its values. It only says each packet should get an IP ID that's unique for the source-destination pair and protocol for as long as the datagram (or any of its fragments) could be alive in the network โณ So there are lots of ways to assign IP ID values, and we've split them into three classes ๐ฐ
Sequential jump
This is the most common policy in today's IP stacks ๐ A single IP ID counter gets used for all packet streams. When the sender runs more than one packet stream at once, the IP ID can jump up by more than one between packets in a stream. The values are way more predictable and need fewer bits to send than random ones do, and the packet-to-packet increment (set by how many outgoing streams are active and how often they send) is usually limited ๐
Random
Some IP stacks use a pseudo-random number generator to assign IP ID values ๐ฒ There's no correlation between the ID values of later datagrams, so there's no way to predict the IP ID for the next datagram ๐ For header compression, that means the IP ID field has to be sent uncompressed with every datagram, which adds two extra octets of header ๐ฌ IP stacks in cellular terminals SHOULD NOT use this IP ID assignment policy ๐ต
Sequential
This policy keeps a separate counter for each outgoing packet stream, so the IP ID goes up by one for each packet in the stream, except when it wraps around ๐ That means the delta of the field is constant and well known a priori ๐ When RTP runs on top of UDP and IP, the IP ID value follows the RTP Sequence Number ๐ซถ This is the most desirable policy for header compression, but it's not as common as it perhaps should be ๐ฅบ It might be because it only works when UDP and IP are implemented together. That way UDP, which tells packet streams apart with the Port identification fields, can make IP use a separate ID counter for each packet stream ๐ง
To avoid violating [IPv4], packets sharing the same IP address pair and IP protocol number can't use the same IP ID values ๐ซ So implementations of sequential policies have to keep the ID number spaces disjoint for packet streams of the same IP protocol going between the same pair of nodes ๐งฉ There are several ways to do that, and all of them introduce occasional jumps, which makes the policy less than perfectly sequential ๐ For header compression, fewer jumps are better โจ
It's worth noting that the ID is an IPv4 mechanism, so it's not a problem for IPv6 ๐โโ๏ธ For IPv4, the ID could be handled in three different ways ๐ฅ
First, there's the inefficient but reliable solution, where the ID field is sent as-is in every packet. That increases the compressed headers by two octets. This is the best way to handle the ID field if the sender uses random assignment ๐ฒ
Second, there can be more flexible mechanisms that need fewer bits for ID handling, as long as sequential jump assignment is used. Those solutions will probably need even more bits if the sender uses random assignment ๐ So knowing the sender's assignment policy can help you pick between the two solutions above ๐
Finally, even for IPv4, header compression could be designed without any extra information for the ID field in compressed headers. To use those schemes, you have to know which ID assignment policy the sender is using. That might not be possible to know, so the applicability of such solutions is very uncertain ๐ซ๏ธ Still, designers of IPv4 stacks for cellular terminals SHOULD use an assignment policy close to sequential ๐ฑ๐
A.2.2. IP Traffic-Class / Type-Of-Service ๐ฆ
The Traffic-Class (IPv6) or Type-Of-Service (IPv4) field is expected to stay constant for the lifetime of a packet stream, or to change pretty seldom ๐ข
A.2.3. IP Hop-Limit / Time-To-Live ๐ฃ๏ธ
The Hop-Limit (IPv6) or Time-To-Live (IPv4) field is expected to stay constant for the lifetime of a packet stream, or to alternate between a limited number of values because of route changes ๐
A.2.4. UDP Checksum ๐งฎ
The UDP checksum is optional ๐ค If it's disabled, its value is constantly zero and could be compressed away ๐๏ธ If it's enabled, its value depends on the payload. For compression purposes, that's the same as it changing randomly with every packet ๐ฒ
A.2.5. RTP CSRC Counter ๐ข
This is a counter for how many CSRC items are in the CSRC list ๐ The number is expected to be almost constant from packet to packet and to change by small amounts ๐ As long as no RTP mixer is used, the value of this field is zero 0๏ธโฃ
A.2.6. RTP Marker ๐
For audio, the marker bit should only be set in the first packet of a talkspurt ๐ฃ๏ธ For video, it should be set in the last packet of every picture ๐ฌ So in both cases, the RTP marker is classified as SEMISTATIC with well-known values for both states ๐
A.2.7. RTP Payload Type ๐ต
Changes to the RTP payload type within a packet stream are expected to be rare ๐ฃ Applications could adapt to congestion by changing the payload type and/or frame sizes, but that's not expected to happen often ๐ง๏ธ
A.2.8. RTP Sequence Number ๐ข
The RTP Sequence Number gets incremented by one for each packet sent ๐ฅฐ
A.2.9. RTP Timestamp โฐ
okay so in the audio case ๐ค, as long as there are no pauses in the stream, the RTP Timestamp goes up by a constant delta, which is just the number of samples in one speech frame ๐ถ. It basically follows the RTP Sequence Number around, literally right alongside it. When there's a silent period and a new talkspurt starts, the timestamp jumps in proportion to how long the silence lasted ๐คซ. But ngl, the increment will probably stay within a pretty limited range, so it's not wild ๐.
In the video case ๐ฌ, between two consecutive packets the timestamp will either stay the same or go up by a multiple of a fixed value tied to the picture clock frequency โจ. It can also go down by a multiple of that fixed value if B-pictures are used ๐ (plot twist). The delta interval, written as a multiple of the picture clock frequency, is in most cases super limited ๐.
A.2.10. RTP Contributing Sources (CSRC) ๐ซ
the participants in a session, which are identified by the CSRC fields, are expected to be almost the same from packet to packet, with only a few people joining or leaving ๐ฏโโ๏ธ. As long as RTP mixers aren't being used, there are no CSRC fields present at all ๐ง.
A.3. Header compression strategies ๐๏ธ
this section builds on everything we've already covered ๐ง . Using those classifications, it gives recommendations for how to handle each field during header compression. There are seven different actions ๐ , and each one is listed below together with the fields it applies to.
A.3.1. Do not send at all ๐ โโ๏ธ
fields with well-known values a priori don't have to be sent at all, because the other side already knows them ๐. These are:
- IPv6 Payload Length
- IPv4 Header Length
- IPv4 Reserved Flag
- IPv4 Last Fragment Flag
- IPv4 Fragment Offset
- UDP Checksum (if disabled)
- RTP Version
A.3.2. Transmit only initially ๐ฎ
fields that stay constant for the whole life of the packet stream only have to be sent, and correctly delivered to the decompressor, once ๐. These are:
- IP Version
- IP Source Address
- IP Destination Address
- IPv6 Flow Label
- IPv4 May Fragment Flag
- UDP Source Port
- UDP Destination Port
- RTP Padding Flag
- RTP Extension Flag
- RTP SSRC
A.3.3. Transmit initially, but be prepared to update ๐
fields that only change every so often must be transmitted initially, but there must also be a way to update them with new values if they change ๐ฅน. These fields are:
- IPv6 Next Header
- IPv6 Traffic Class
- IPv6 Hop Limit
- IPv4 Protocol
- IPv4 Type Of Service (TOS)
- IPv4 Time To Live (TTL)
- RTP CSRC Counter
- RTP Payload Type
- RTP CSRC List
ok but this one is a big deal ๐: since the values of the IPv4 Protocol and IPv6 Next Header fields are basically linked to the type of the subsequent header, they deserve special treatment when subheaders get inserted or removed ๐ญ.
A.3.4. Be prepared to update or send as-is frequently ๐
for fields that are normally either constant or deducible from some other field, but that often diverge from that behavior, there must be an efficient way to update the value or to send it as-is in some packets ๐ซ. These fields are:
- IPv4 Identification (if not sequentially assigned)
- RTP Marker
- RTP Timestamp
A.3.5. Guarantee continuous robustness ๐ก๏ธ
for fields that behave like a counter with a fixed delta for ALL packets, the only requirement on the transmission encoding is that packet losses between compressor and decompressor must be tolerable ๐ช. If several of these fields exist, they can all be communicated together ๐ค. Such fields can also be used to interpret the values of the fields in the previous section ๐. Fields with this counter behavior are:
- IPv4 Identification (if sequentially assigned)
- RTP Sequence Number
A.3.6. Transmit as-is in all packets ๐ฆ
fields with completely random values for each packet must be included as-is in every compressed header ๐ฒ. Those fields are:
- IPv4 Identification (if randomly assigned)
- UDP Checksum (if enabled)
A.3.7. Establish and be prepared to update delta ๐
finally, there's one field that usually increases by a fixed delta and is correlated with another field ๐ซถ. For this field, it makes sense to make that delta part of the context state โจ. The delta must then be initiated and updated in the same way as the fields listed in A.3.3 ๐. The field this applies to is:
- RTP Timestamp
Full Copyright Statement โ๏ธ
Copyright (C) The Internet Society (2001). All Rights Reserved.
this document and translations of it may be copied and furnished to others ๐. Derivative works that comment on or otherwise explain it, or that assist in its implementation, may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, as long as the above copyright notice and this paragraph are included on all such copies and derivative works ๐. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations ๐ซ. The exceptions are when it's needed for developing Internet standards, in which case the procedures for copyrights defined in the Internet Standards process must be followed, or when it's required to translate it into languages other than English ๐.
The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns โพ๏ธ.
This document and the information contained herein is provided on an "AS IS" basis, and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ๐.
Acknowledgement ๐ซถ
Funding for the RFC Editor function is currently provided by the Internet Society ๐.
๐ฆ where this page comes from
- ietf: rfc3095 2001-07
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms