RFC 4077

A Negative Acknowledgement Mechanism for Signaling Compression

πŸ“… published May 2005, from ietf

RFC 4077 πŸ’Œ (part 1 of 3)

okay bestie, this is RFC 4077, and it's giving A Negative Acknowledgement Mechanism for Signaling Compression πŸ¦‹ Here's the title-block tea πŸ«–

  • Number: RFC 4077
  • Title: A Negative Acknowledgement Mechanism for Signaling Compression
  • Author: A.B. Roach, Estacado Systems
  • Stream: Network Working Group
  • Category: Standards Track πŸ‘‘
  • Date: May 2005
  • Obsoletes / Updates: the title block doesn't list any, so this one isn't replacing or patching another RFC ✨

Table of Contents ✨

   1. Introduction ....................................................2
      1.1. The Problem ................................................2
           1.1.1. Compartment Disposal ................................3
           1.1.2. Client Restart ......................................3
           1.1.3. Server Failover .....................................3
      1.2. The Solution ...............................................4
   2. Node Behavior ...................................................4
      2.1. Normal SigComp Message Transmission ........................4
      2.2. Receiving a "Bad" SigComp Message ..........................5
      2.3. Receiving a SigComp NACK ...................................6
           2.3.1. Unreliable Transport ................................6
           2.3.2. Reliable Transport ..................................6
      2.4. Detecting Support for NACK .................................7
   3. Message Format ..................................................7
      3.1. Message Fields .............................................8
      3.2. Reason Codes ...............................................9
   4. Security Considerations ........................................13
      4.1. Reflector Attacks .........................................13
      4.2. NACK Spoofing .............................................13
   5. IANA Considerations ............................................14
   6. Acknowledgements ...............................................14
   7. References .....................................................14
      7.1. Normative References ......................................14
      7.2. Informative References ....................................14

Status of This Memo πŸ“œ

this one is an Internet standards track protocol for the Internet community 🌐 and it's literally asking for discussion and suggestions for improvements, so bring your ideas πŸ’…

for the official standardization state and status, check the current edition of the "Internet Official Protocol Standards" (STD 1). and the distribution of this memo is unlimited, so it's open to everyone πŸ¦‹

Copyright (C) The Internet Society (2005).

Abstract 🌸

this doc describes a mechanism that lets Signaling Compression (SigComp) implementations report precise error information when they get a message they can't decompress πŸ™ˆ the recipient can use that negative feedback to make fine-grained tweaks to the compressed message before resending it, so recovery from error situations is fast and efficient πŸ”₯


1. Introduction ✨

okay so Signaling Compression [1], which everyone calls "SigComp," is a protocol for moving compressed messages between two network elements πŸ’œ one of its biggest features is that the sending node can ask the receiving node to store state objects so they can be pulled back up later 🧚

1.1. The Problem 😭

the "SigComp - Extended Operations" document [2] already gives a way to confirm that state was created, but real-world experience has shown that a sender's view of the shared state can still differ from the receiver's view in several situations πŸ™ˆ the list below isn't exhaustive, it's just the main ones ✨

1.1.1. Compartment Disposal πŸ—‘οΈ

in SigComp, stored states belong to compartments πŸŽ€ conceptually, a compartment stands for one instance of a remote application, and it exists to limit how much state each remote application is allowed to store πŸ’… a compartment gets created when a valid SigComp message arrives from a remote application. the way applications are supposed to clean up is by signaling when they're done with a compartment, so it can be deleted (using the S-bit in requested feedback data) 🌈

the catch is that expecting apps to behave isn't enough to stop state from piling up πŸ’€ unexpected client failures, reboots, and loss of connectivity can leave compartments "stuck" and never removed 😭 so there has to be a scheme where compartments that look unused can eventually be thrown out 🧹

but here's the twist: tossing compartments without explicit signaling can have the unfortunate side effect of discarding active compartments too πŸ™ƒ and that leads to a different view of state between the server and the client 🫠

1.1.2. Client Restart πŸ“±

SigComp was mainly motivated by compressing messages sent over a radio interface, so most deployments have a mobile unit as one of the endpoints πŸ¦‹ mobile terminals aren't guaranteed to stay available for long stretches of time 😩 when a node restarts (say, because the battery ran out), the network-based server can end up believing the client still has several states that are no longer actually available πŸ’”

1.1.3. Server Failover πŸ”

a lot of apps that use SigComp (SIP [3] is one example) look up servers with DNS SRV records 🌸 the cool thing about SRV records is that you can list multiple servers, and clients pick one at random, with probabilities set by the q-value weighting 🎲 this exists so load gets spread across a set of equivalent servers, and so clients keep working even if the server they're talking to fails πŸ‘‘

when that happens, the client typically sends its traffic to an equivalent server that can do the same job 🫢 from the application's point of view, this new server often looks like the same endpoint as the failed one, so it ends up resolving to the same compartment 🧚

replicating SigComp state across a cluster of servers is an option, but keeping that state consistent needs a two-phase commit process πŸ˜΅β€πŸ’« that adds a ton of complexity to the server and can seriously hurt performance πŸ’€

1.2. The Solution ✨

SigComp does let returned SigComp parameters signal that all states are gone (by setting "state_memory_size" to 0 for one message in the reverse direction) πŸ™ƒ but that's only a partial fix, not a full one πŸ™ˆ it wipes out an entire compartment even when only one state is corrupt or missing, and it needs a reverse-direction message that the remote application will authorize 😀 unless a lower-layer security mechanism is in use (TLS, for example), that usually means a compressed application-level message has to go the reverse direction before recovery can happen πŸ’… in lots of cases (like SIP-based mobile terminals) those messages won't be sent often, and in pure client/server deployments they won't ever be sent at all πŸ’€

the proposed fix is a simple Negative Acknowledgement (NACK) mechanism πŸŽ€ it lets the recipient tell the sender that a failure happened πŸ“£ the NACK carries a reason code describing the nature of the failure, and for certain kinds of failures it also includes extra details that might help with recovery 🫢


2. Node Behavior 🧠

the next sections spell out what nodes do when they send and receive SigComp NACKs πŸ’œ the actual format and values live in Section 3 πŸ¦‹

2.1. Normal SigComp Message Transmission πŸ“€

even though everything else stays normal, SigComp implementations that use the NACK mechanism need to calculate and store a SHA-1 hash for each SigComp message they send πŸ” that hash has to be stored in a way that, given the SHA-1 hash, the implementation can find the compartment the sent message was associated with πŸ—ΊοΈ

put another way: if someone hands the SHA-1 hash back to the compressor, it needs to be able to find the compartment it was working with when it sent the message with that hash ✨ this only requires that the compressor knows which compartment it's working with when it sends a message (which is always the case), and that the stored SHA-1 hash points to that compartment somehow 🌈

2.2. Receiving a "Bad" SigComp Message 🚨

when a received SigComp message causes a decompression failure, the recipient forms and sends a SigComp NACK message πŸ“¨ this NACK contains the SHA-1 hash of the received SigComp message that couldn't be decompressed πŸ” it also includes the exact reason decompression failed, plus any extra details that might help the NACK recipient fix the problem πŸ› οΈ check Section 3 for more on how the NACK message and its fields are formatted πŸ’…

for a connection-oriented transport, like TCP, the NACK message goes back to the originator of the failed message over that same connection πŸ”—

for a stream-based transport, like TCP, the standard SigComp delimiter of 0xFFFF is used to end the NACK message πŸͺ„

for a connectionless transport, like UDP, the NACK message goes back to the originator of the failed message at the port and IP address the message came from πŸ“¬ heads up: that may or may not be the same port the application would normally receive messages on πŸ‘€ to play nice with implementations that use connect() or similar constructs, the NACK gets sent from the IP address and port the uninterpretable message was sent to πŸ’– practically speaking, the easiest way to figure that out is to bind listening sockets to a specific interface, but other mechanisms are also allowed 🧚

the behavior described above is strictly necessary for any generally useful form of NACK mechanism πŸ’― in the most general case, when an implementation gets a message it can't decompress, it has exactly three useful pieces of information: (1) the contents of the message, (2) an indication of why the message can't be decoded, and (3) the IP address and port the message came from 🎁 none of these tells you where the remote application is listening for messages, if that's different from the sending port πŸ™ˆ

2.3. Receiving a SigComp NACK πŸ“₯

the very first thing a node does when it gets a NACK is try to find the message the NACK refers to πŸ”Ž that search uses the 20-byte SHA-1 hash contained in the NACK πŸ” once the matching message is found, further operations are based on the compartment that was associated with the sent message πŸ—‚οΈ

what a node does next after receiving a SigComp NACK depends on whether a reliable or an unreliable transport is in use 🌸

2.3.1. Unreliable Transport πŸ¦‹

Okay so when SigComp runs over an unreliable transport, the app literally can't count on the transport layer to deliver any particular message πŸ’€. That makes it the application layer's job to retransmit stuff when needed. The NACK mechanism is built around that assumption, so it never retransmits anything on the app's behalf 🫢.

When a NACK comes in for a message that was sent over an unreliable transport, the NACK recipient uses the info inside it to tweak the compressor tied to the proper compartment ✨. What those tweaks look like depends on the compression scheme and will vary from implementation to implementation, so there's no one-size-fits-all answer here πŸ’…. The only requirement on these adjustments is that they must have the effect of compensating for the error that was indicated (e.g., by removing the state that the remote node says it can't retrieve) 😭.

In particular, when an unreliable transport is used, the original message must not be retransmitted by the SigComp layer upon receipt of a NACK.

Instead, the next application-initiated transmission of a message will take advantage of the adjustments made as a result of processing the NACK 🌈.

2.3.2. Reliable Transport πŸ”’

When a reliable transport is in use, the app assumes that any message it passes down the stack will get retransmitted as needed so the remote node receives it, unless the transport layer says something failed 🧚. SigComp acts as a shim between the transport layer and the app, so the SigComp implementation is responsible for making sure any failure to transmit a message gets communicated up to the app πŸ“¬.

When a NACK is received for a message sent over a reliable transport, the SigComp layer must indicate to the app that an error has occurred. In general, the app should react the same way it does to any other transport layer error, like a TCP connection reset πŸ‘‘. For most apps, that first reaction will be an attempt to reset and re-establish the connection and re-initiate the failed transaction. The SigComp layer should also use the info in the NACK to make appropriate adjustments to the compressor tied to the proper compartment (similar to the adjustments made for unreliable transport). So if the compartment isn't reset by resetting the TCP connection, the next message will take advantage of those adjustments 🌸.

2.4. Detecting Support for NACK πŸ”

Detecting NACK support can be genuinely useful in certain situations. For example, with the current definition of SigComp, a sender has to wait for acknowledgment of state receipt before it can reference that state. When multiple messages go out before a response comes back, all that waiting can seriously tank message compression efficiency πŸ“‰. If it's known that the receiver supports NACK, the sender can optimistically assume the state created by a sent message has been created and is allowed to be referenced πŸš€. If that assumption turns out to be false (due to, for example, packet loss or packet reordering), the sender can recover upon receipt of a NACK πŸ’ͺ.

To make that detection possible, any implementation that will send NACK messages upon decompression failure indicates a SigComp version number of 0x02 in its Universal Decompressor Virtual Machine (UDVM) πŸŽ€. The bytecodes sent to such an endpoint can check the version number and send the appropriate indication back to their compressor as requested feedback. Except for the NACK mechanism described in this document, implementations advertising a version of 0x02 behave exactly like those advertising a version number of 0x01 ✨.

3. Message Format πŸ“¦

SigComp NACK packets are syntactically valid SigComp messages that have been specifically designed to be safely ignored by implementations that don't support the NACK mechanism πŸ™ˆ.

In particular, NACK messages are formatted as the second variant of a SigComp message (typically used for code upload) with a "code_len" field of zero. The NACK information (message identifier, reason for failure, and error details) is encoded in the "remaining SigComp message" area, which is typically used for input data. Further, the "destination" field is used as a version identifier to indicate which version of NACK is being employed πŸ’….

3.1. Message Fields 🧾

The format of the NACK message and the use of the fields within it are shown in Figure 1.

                  0   1   2   3   4   5   6   7
                +---+---+---+---+---+---+---+---+
                | 1   1   1   1   1 | T |   0   |
                +---+---+---+---+---+---+---+---+
                |                               |
                :    returned feedback item     :
                |                               |
                +---+---+---+---+---+---+---+---+
                |         code_len = 0          |
                +---+---+---+---+---+---+---+---+
                | code_len = 0  |  version = 1  |
                +---+---+---+---+---+---+---+---+
                |          Reason Code          |
                +---+---+---+---+---+---+---+---+
                |  OPCODE of failed instruction |
                +---+---+---+---+---+---+---+---+
                |   PC of failed instruction    |
                |                               |
                +---+---+---+---+---+---+---+---+
                |                               |
                : SHA-1 Hash of failed message  :
                |                               |
                +---+---+---+---+---+---+---+---+
                |                               |
                :         Error Details         :
                |                               |
                +---+---+---+---+---+---+---+---+

Figure 1: SigComp NACK Message Format

  • "Reason Code" is a one-byte value that indicates the nature of the decompression failure. The specific codes are given in Section 3.2.
  • "OPCODE of failed instruction" is a one-byte field that includes the opcode to which the PC was pointing when the failure occurred. If failure occurred before the UDVM began executing any code, this field is set to 0.
  • "PC of failed instruction" is a two-byte field containing the value of the program counter when failure occurred (i.e., the memory address of the failed UDVM instruction). The field is encoded with the most significant byte of the PC first (i.e., in network or big endian order). If failure occurred before the UDVM began executing any code, this field is set to 0.
  • "SHA-1 Hash of failed message" contains the full 20-byte SHA-1 hash of the SigComp message that could not be decompressed. This information allows the NACK recipient to locate the message that failed to decompress so that adjustments to the correct compartment can be performed. When performing this hash, the entire SigComp message is used, from the header byte (binary 11111xxx) to the end of the input. Any lower-level protocol headers (such as UDP or IP) and message delimiters (the 0xFFFF that marks message boundaries in stream protocols) are not included in the hash. When used over a stream based protocol, any 0xFFxx escape sequences are un-escaped before performing the hash operation.
  • "Error Details" provides additional information that might be useful in correcting the problem that caused decompression failure. Its meaning is specific to the "Reason Code". See Section 3.2 for specific information on what appears in this field.
  • "Code_len" is the "code_len" field from a standard SigComp message. It is always set to "0" for NACK messages.
  • "Version" gives the version of the NACK mechanism being employed. This document defines version 1.

3.2. Reason Codes πŸ’Œ

Okay so, lots of these status codes are way more useful for debugging interop problems than for fixing errors on the fly, ngl. πŸ” The STATE_NOT_FOUND error is the big exception ✨: it'll generally make the NACK recipient encode future messages so they don't use the indicated state. πŸ’…

When any of the other status messages show up, an implementation would typically be expected to either use a different set of bytecodes or, if that's not an option, send that specific message uncompressed. 🫢

    Error                      Code Details
    -------------------------- ---- ---------------------------
    STATE_NOT_FOUND              1  State ID (6 - 20 bytes)
    CYCLES_EXHAUSTED             2  Cycles Per Bit (1 byte)
    USER_REQUESTED               3
    SEGFAULT                     4
    TOO_MANY_STATE_REQUESTS      5
    INVALID_STATE_ID_LENGTH      6
    INVALID_STATE_PRIORITY       7
    OUTPUT_OVERFLOW              8
    STACK_UNDERFLOW              9
    BAD_INPUT_BITORDER          10
    DIV_BY_ZERO                 11
    SWITCH_VALUE_TOO_HIGH       12
    TOO_MANY_BITS_REQUESTED     13
    INVALID_OPERAND             14
    HUFFMAN_NO_MATCH            15
    MESSAGE_TOO_SHORT           16
    INVALID_CODE_LOCATION       17
    BYTECODES_TOO_LARGE         18  Memory size (2 bytes)
    INVALID_OPCODE              19
    INVALID_STATE_PROBE         20
    ID_NOT_UNIQUE               21  State ID (6 - 20 bytes)
    MULTILOAD_OVERWRITTEN       22
    STATE_TOO_SHORT             23  State ID (6 - 20 bytes)
    INTERNAL_ERROR              24
    FRAMING_ERROR               25

Only the five errors STATE_NOT_FOUND, CYCLES_EXHAUSTED, BYTECODES_TOO_LARGE, ID_NOT_UNIQUE, and STATE_TOO_SHORT carry details πŸ“¦. For all other error codes, the "Error Details" field has zero length.

                 Figure 2: SigComp NACK Reason Codes
  1. STATE_NOT_FOUND: A state that was referenced cannot be found. The state may have been referenced by the UDVM executing a STATE-ACCESS instruction; it also may have been referenced by the "partial state identifier" field in a SigComp message. The "details" field contains the state identifier for the state that could not be found. This is also the proper error to return in the case that a unique state item was matched but fewer bytes of state ID were sent than required by the minimum_access_length.
  2. CYCLES_EXHAUSTED: Decompression of the message has taken more cycles than were allocated to it. The "details" field contains a one-byte value that communicates the number of cycles per bit. The cycles per bit is represented as an unsigned 8-bit integer (i.e., not encoded).
  3. USER_REQUESTED: The DECOMPRESSION-FAILURE opcode has been executed.
  4. SEGFAULT: An attempt to read from or write to memory that is outside of the UDVM's memory space has been attempted.
  5. TOO_MANY_STATE_REQUESTS: More than four requests to store or delete state objects have been requested.
  6. INVALID_STATE_ID_LENGTH: A state id length less than 6 or greater than 20 has been specified.
  7. INVALID_STATE_PRIORITY: A state priority of 65535 has been specified when attempting to store a state.
  8. OUTPUT_OVERFLOW: The decompressed message is too large to be decoded by the receiving node.
  9. STACK_UNDERFLOW: An attempt to pop a value off the UDVM stack was made with a stack_fill value of 0.
  10. BAD_INPUT_BITORDER: An INPUT-BITS or INPUT-HUFFMAN instruction was encountered with the "input_bit_order" register set to an invalid value (i.e., one of the upper 13 bits is set).
  11. DIV_BY_ZERO: A DIVIDE or REMAINDER opcode was encountered with a divisor of 0.
  12. SWITCH_VALUE_TOO_HIGH: The input to a SWITCH opcode exceeds the number of branches defined.
  13. TOO_MANY_BITS_REQUESTED: An INPUT-BITS or INPUT-HUFFMAN instruction was encountered that attempted to input more than 16 bits.
  14. INVALID_OPERAND: An operand for an instruction could not be resolved to an integer value (e.g., a literal or reference operand beginning with 11111111).
  15. HUFFMAN_NO_MATCH: The input string does not match any of the bitcodes in the INPUT-HUFFMAN opcode.
  16. MESSAGE_TOO_SHORT: When attempting to decode a SigComp message, the recipient determined that there were not enough bytes in the message for it to be valid.
  17. INVALID_CODE_LOCATION: The "code location" field in the SigComp message was set to the invalid value of 0.
  18. BYTECODES_TOO_LARGE: The bytecodes that a SigComp message attempted to upload exceed the amount of memory available in the receiving UDVM. The details field is a two-byte expression of the DECOMPRESSION_MEMORY_SIZE of the receiving UDVM. This value is communicated most-significant-byte first.
  19. INVALID_OPCODE: The UDVM attempted to identify an undefined byte value as an instruction.
  20. INVALID_STATE_PROBE: When attempting to retrieve state, the state_length operand is set to 0 but the state_begin operand is non-zero.
  21. ID_NOT_UNIQUE: A partial state identifier that was used to access state matched more than one state item. Note that this error might be returned as the result of executing a STATE-ACCESS instruction or attempting to locate a unique piece of state as identified by the "partial state identifier" in a SigComp message. The "details" field contains the partial state identifier that was requested.
  22. MULTILOAD_OVERWRITTEN: A MULTILOAD instruction attempted to overwrite itself.
  23. STATE_TOO_SHORT: A STATE-ACCESS instruction has attempted to copy more bytes from a state item than the state item actually contains. The "details" field contains the partial state identifier that was requested. Implementors are cautioned to return only the partial state identifier that was requested; if the NACK contains any state identifier in addition to what was requested, attackers may be able to use that additional information to access the state. 🚨
  24. INTERNAL_ERROR: The UDVM encountered an unexpected condition that prevented it from decompressing the message.
  25. FRAMING_ERROR: The UDVM encountered a framing error (unquoted 0xFF 80 .. 0xFF FE in an input stream.) This error is applicable only to messages received on a stream transport. In the case of a framing error, a SHA-1 hash for a unique message cannot be determined. Consequently, when a FRAMING_ERROR NACK is sent, the "SHA-1 Hash of failed message" field should be set to all zeros.

4. Security Considerations πŸ”

4.1. Reflector Attacks πŸͺž

Because SigComp NACK messages are by necessity sent in response to other messages, it's possible to trigger them by intentionally sending malformed messages to a SigComp implementation with a spoofed IP address. However, because such actions can only generate one message for each message sent, they don't serve as amplifier attacks. Further, due to the reasonably small size of NACK packets, there cannot be a significant increase in the size of the packet generated.

It is worth noting that nearly all deployed protocols exhibit this same behavior.

4.2. NACK Spoofing πŸ•΅οΈ

Although it is possible to forge NACK messages as if they were generated by a different node, the damage that can be caused is minimal. Reporting a loss of state will typically result in nothing more than the re-transmission of that state in a subsequent message. Other failure codes would result in the next message being sent using an alternate compression mechanism, or possibly uncompressed.

Although all of the above consequences result in slightly larger messages, none of them have particularly catastrophic implications for security. πŸ›‘οΈ


5. IANA Considerations πŸ“‹

This document defines a new value for the IANA registered attribute SigComp_version.

Value (in hex): 02
Description: SigComp version 2 (NACK support)
Reference: [RFC4077]

6. Acknowledgements 🫢

Thanks to Carsten Bormann, Zhigang Liu, Pekka Pessi, and Robert Sugar for their comments and suggestions. Special thanks to Abigail Surtees and Richard Price for several very detailed reviews and suggestions. πŸ™Œ


7. References πŸ“š

7.1. Normative References

  • [1] Price, R., Bormann, C., Christoffersson, J., Hannu, H., Liu, Z., and J. Rosenberg, "Signaling Compression (SigComp)", RFC 3320, January 2003.
  • [2] Hannu, H., Christoffersson, J., Forsgren, S., Leung, K.-C., Liu, Z., and R. Price, "Signaling Compression (SigComp) - Extended Operations", RFC 3321, January 2003.

7.2. Informative References

  • [3] Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston, A., Peterson, J., Sparks, R., Handley, M., and E. Schooler, "SIP: Session Initiation Protocol", RFC 3261, June 2002.

Author's Address πŸ–ŠοΈ

Adam Roach
Estacado Systems
17210 Campbell Road
Suite 250
Dallas, TX 75252
US

EMail: adam@estacado.net

Copyright (C) The Internet Society (2005).

This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.

This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM 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.


Intellectual Property βš–οΈ

The IETF takes no position regarding the validity or scope of any Intellectual Property Rights 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; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79.

Copies of IPR disclosures made to the IETF Secretariat 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 implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.

The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org.


Acknowledgement πŸ’œ

Funding for the RFC Editor function is currently provided by the Internet Society.

πŸ“¦ where this page comes from