RFC 9260

Stream Control Transmission Protocol

๐Ÿ“… published June 2022, from ietf

Stream Control Transmission Protocol โœจ

Okay bestie, this is RFC 9260 ๐ŸŽ€, and it's titled Stream Control Transmission Protocol (SCTP). It was published in June 2022 by the IETF. The authors are R. Stewart from Netflix, Inc., M. Tรผxen from Mรผnster Univ. of Appl. Sciences, and K. Nielsen from Kamstrup A/S. It's an Internet Standards Track document, with ISSN 2070-1721 ๐Ÿ’….

It obsoletes RFC 4460, RFC 4960, RFC 6096, RFC 7053, and RFC 8540. There's no Updates line in the header, so nothing gets updated ๐Ÿฆ‹.


Abstract โœจ

This doc describes SCTP and replaces RFC 4960 ๐Ÿ“œ. It also brings in the chunk flags registry spec from RFC 6096 and the I bit spec for DATA chunks from RFC 7053, so those two are obsoleted too ๐Ÿ’–. And since RFCs 4460 and 8540 are errata for SCTP, this document obsoletes them as well ๐Ÿ™ˆ.

SCTP was originally built to carry PSTN signaling messages over IP networks, which is very old-school phone-network energy ๐Ÿ“ž. It's also a good fit for other stuff, like WebRTC ๐ŸŒธ.

SCTP is a reliable transport protocol that runs on top of a connectionless packet network, like IP ๐Ÿš€. It gives its users these services:

  • acknowledged, error-free, non-duplicated transfer of user data ๐Ÿ’…
  • data fragmentation so it fits the discovered Path Maximum Transmission Unit (PMTU) size ๐Ÿงฉ
  • sequenced delivery of user messages within multiple streams, with an option for order-of-arrival delivery of individual user messages โœจ
  • optional bundling of multiple user messages into a single SCTP packet ๐ŸŽ€
  • network-level fault tolerance through support for multi-homing at either or both ends of an association ๐Ÿ‘‘

The design also has solid congestion avoidance behavior and resists flooding and masquerade attacks. Iconic, honestly ๐Ÿ”ฅ.


Status of This Memo โœจ

This is an Internet Standards Track document ๐Ÿ’œ.

This document is a product of the Internet Engineering Task Force (IETF) ๐ŸŒˆ. It reflects the consensus of the IETF community, got public review, and was approved for publication by the Internet Engineering Steering Group (IESG) ๐Ÿ‘‘. For more on Internet Standards, check Section 2 of RFC 7841 ๐Ÿ“š.

For the current status of this document, any errata, and how to send feedback, go to https://www.rfc-editor.org/info/rfc9260 ๐Ÿฆ‹.


Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved ๐Ÿ’–.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents, which are at https://trustee.ietf.org/license-info and apply on the date this document was published. Please read these carefully, because they spell out your rights and restrictions for this document ๐Ÿ“œ. Code Components pulled out of this document must include Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions. Those components come without warranty, as described in the Revised BSD License ๐Ÿ”.

This document may contain material from IETF Documents or IETF Contributions that were published or made publicly available before November 10, 2008 ๐Ÿ•ฐ๏ธ. The people who control the copyright in some of that material may not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. Unless you get an adequate license from those copyright holders, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside that process. The only exceptions are formatting it for publication as an RFC or translating it into languages other than English ๐Ÿ™…โ€โ™€๏ธ.


Table of Contents โœจ

   1.  Introduction
     1.1.  Motivation
     1.2.  Architectural View of SCTP
     1.3.  Key Terms
     1.4.  Abbreviations
     1.5.  Functional View of SCTP
       1.5.1.  Association Startup and Takedown
       1.5.2.  Sequenced Delivery within Streams
       1.5.3.  User Data Fragmentation
       1.5.4.  Acknowledgement and Congestion Avoidance
       1.5.5.  Chunk Bundling
       1.5.6.  Packet Validation
       1.5.7.  Path Management
     1.6.  Serial Number Arithmetic
     1.7.  Changes from RFC 4960
   2.  Conventions
   3.  SCTP Packet Format
     3.1.  SCTP Common Header Field Descriptions
     3.2.  Chunk Field Descriptions
       3.2.1.  Optional/Variable-Length Parameter Format
       3.2.2.  Reporting of Unrecognized Parameters
     3.3.  SCTP Chunk Definitions
       3.3.1.  Payload Data (DATA) (0)
       3.3.2.  Initiation (INIT) (1)
         3.3.2.1.  Optional or Variable-Length Parameters in INIT
                 chunks
       3.3.3.  Initiation Acknowledgement (INIT ACK) (2)
         3.3.3.1.  Optional or Variable-Length Parameters in INIT ACK
                 Chunks
       3.3.4.  Selective Acknowledgement (SACK) (3)
       3.3.5.  Heartbeat Request (HEARTBEAT) (4)
       3.3.6.  Heartbeat Acknowledgement (HEARTBEAT ACK) (5)
       3.3.7.  Abort Association (ABORT) (6)
       3.3.8.  Shutdown Association (SHUTDOWN) (7)
       3.3.9.  Shutdown Acknowledgement (SHUTDOWN ACK) (8)
       3.3.10. Operation Error (ERROR) (9)
         3.3.10.1.  Invalid Stream Identifier (1)
         3.3.10.2.  Missing Mandatory Parameter (2)
         3.3.10.3.  Stale Cookie (3)
         3.3.10.4.  Out of Resource (4)
         3.3.10.5.  Unresolvable Address (5)
         3.3.10.6.  Unrecognized Chunk Type (6)
         3.3.10.7.  Invalid Mandatory Parameter (7)
         3.3.10.8.  Unrecognized Parameters (8)
         3.3.10.9.  No User Data (9)
         3.3.10.10. Cookie Received While Shutting Down (10)
         3.3.10.11. Restart of an Association with New Addresses (11)
         3.3.10.12. User-Initiated Abort (12)
         3.3.10.13. Protocol Violation (13)
       3.3.11. Cookie Echo (COOKIE ECHO) (10)
       3.3.12. Cookie Acknowledgement (COOKIE ACK) (11)
       3.3.13. Shutdown Complete (SHUTDOWN COMPLETE) (14)
   4.  SCTP Association State Diagram
   5.  Association Initialization
     5.1.  Normal Establishment of an Association
       5.1.1.  Handle Stream Parameters
       5.1.2.  Handle Address Parameters
       5.1.3.  Generating State Cookie
       5.1.4.  State Cookie Processing
       5.1.5.  State Cookie Authentication
       5.1.6.  An Example of Normal Association Establishment
     5.2.  Handle Duplicate or Unexpected INIT, INIT ACK, COOKIE ECHO,
           and COOKIE ACK Chunks
       5.2.1.  INIT Chunk Received in COOKIE-WAIT or COOKIE-ECHOED
               State (Item B)
       5.2.2.  Unexpected INIT Chunk in States Other than CLOSED,
               COOKIE-ECHOED, COOKIE-WAIT, and SHUTDOWN-ACK-SENT
       5.2.3.  Unexpected INIT ACK Chunk
       5.2.4.  Handle a COOKIE ECHO Chunk When a TCB Exists
         5.2.4.1.  An Example of an Association Restart
       5.2.5.  Handle Duplicate COOKIE ACK Chunk
       5.2.6.  Handle Stale Cookie Error
     5.3.  Other Initialization Issues
       5.3.1.  Selection of Tag Value
     5.4.  Path Verification
   6.  User Data Transfer
     6.1.  Transmission of DATA Chunks
     6.2.  Acknowledgement on Reception of DATA Chunks
       6.2.1.  Processing a Received SACK Chunk
     6.3.  Management of Retransmission Timer
       6.3.1.  RTO Calculation
       6.3.2.  Retransmission Timer Rules
       6.3.3.  Handle T3-rtx Expiration
     6.4.  Multi-Homed SCTP Endpoints
       6.4.1.  Failover from an Inactive Destination Address
     6.5.  Stream Identifier and Stream Sequence Number
     6.6.  Ordered and Unordered Delivery
     6.7.  Report Gaps in Received DATA TSNs
     6.8.  CRC32c Checksum Calculation
     6.9.  Fragmentation and Reassembly
     6.10. Bundling
   7.  Congestion Control
     7.1.  SCTP Differences from TCP Congestion Control
     7.2.  SCTP Slow-Start and Congestion Avoidance
       7.2.1.  Slow-Start
       7.2.2.  Congestion Avoidance
       7.2.3.  Congestion Control
       7.2.4.  Fast Retransmit on Gap Reports
       7.2.5.  Reinitialization
         7.2.5.1.  Change of Differentiated Services Code Points
         7.2.5.2.  Change of Routes
     7.3.  PMTU Discovery
   8.  Fault Management
     8.1.  Endpoint Failure Detection
     8.2.  Path Failure Detection
     8.3.  Path Heartbeat
     8.4.  Handle "Out of the Blue" Packets
     8.5.  Verification Tag
       8.5.1.  Exceptions in Verification Tag Rules
   9.  Termination of Association
     9.1.  Abort of an Association
     9.2.  Shutdown of an Association
   10. ICMP Handling
   11. Interface with Upper Layer
     11.1.  ULP-to-SCTP
       11.1.1.  Initialize
       11.1.2.  Associate
       11.1.3.  Shutdown
       11.1.4.  Abort
       11.1.5.  Send
       11.1.6.  Set Primary
       11.1.7.  Receive
       11.1.8.  Status
       11.1.9.  Change Heartbeat
       11.1.10. Request Heartbeat
       11.1.11. Get SRTT Report
       11.1.12. Set Failure Threshold
       11.1.13. Set Protocol Parameters
       11.1.14. Receive Unsent Message
       11.1.15. Receive Unacknowledged Message
       11.1.16. Destroy SCTP Instance
     11.2.  SCTP-to-ULP
       11.2.1.  DATA ARRIVE Notification
       11.2.2.  SEND FAILURE Notification
       11.2.3.  NETWORK STATUS CHANGE Notification
       11.2.4.  COMMUNICATION UP Notification
       11.2.5.  COMMUNICATION LOST Notification
       11.2.6.  COMMUNICATION ERROR Notification
       11.2.7.  RESTART Notification
       11.2.8.  SHUTDOWN COMPLETE Notification
   12. Security Considerations
     12.1.  Security Objectives
     12.2.  SCTP Responses to Potential Threats
       12.2.1.  Countering Insider Attacks
       12.2.2.  Protecting against Data Corruption in the Network
       12.2.3.  Protecting Confidentiality
       12.2.4.  Protecting against Blind Denial-of-Service Attacks
         12.2.4.1.  Flooding
         12.2.4.2.  Blind Masquerade
         12.2.4.3.  Improper Monopolization of Services
     12.3.  SCTP Interactions with Firewalls
     12.4.  Protection of Non-SCTP-capable Hosts
   13. Network Management Considerations
   14. Recommended Transmission Control Block (TCB) Parameters
     14.1.  Parameters Necessary for the SCTP Instance
     14.2.  Parameters Necessary per Association (i.e., the TCB)
     14.3.  Per Transport Address Data
     14.4.  General Parameters Needed
   15. IANA Considerations
     15.1.  IETF-Defined Chunk Extension
     15.2.  IETF-Defined Chunk Flags Registration
     15.3.  IETF-Defined Chunk Parameter Extension
     15.4.  IETF-Defined Additional Error Causes
     15.5.  Payload Protocol Identifiers
     15.6.  Port Numbers Registry
   16. Suggested SCTP Protocol Parameter Values
   17. References
     17.1.  Normative References
     17.2.  Informative References
   Appendix A.  CRC32c Checksum Calculation
   Acknowledgements
   Authors' Addresses

1. Introduction โœจ

This section explains where SCTP came from, what services it offers, and the basic concepts you need before the detailed protocol description makes sense ๐Ÿง .

This document obsoletes [RFC4960] ๐Ÿ’…. On top of that, it brings in the chunk flags registry spec from [RFC6096] and the I bit spec for DATA chunks from [RFC7053] ๐ŸŽ€. So [RFC6096] and [RFC7053] are obsoleted by this document too โœจ.

To be continued in part 2 ๐Ÿฆ‹

1.1. Motivation โœจ

okay so, TCP [RFC0793] has been the main queen of reliable data transfer on IP networks for ages, and honestly it has earned that crown ๐Ÿ‘‘. But more and more apps were like "ugh, TCP is lowkey too limiting" and just built their own reliable transfer on top of UDP [RFC0768] ๐Ÿ’…. Here's what they wanted to get away from:

  • TCP gives you reliable transfer AND strict in-order delivery, bundled together. Some apps only need reliability and don't care about sequence, and others would be happy with partial ordering. Either way, TCP's head-of-line blocking adds delay nobody asked for ๐Ÿ˜ญ.
  • TCP is a stream, and that's often a vibe-killer ๐Ÿ™„. Apps end up adding their own record marking to show where each message starts and ends, and they use the push facility on purpose so a full message gets sent in a reasonable amount of time ๐Ÿซถ.
  • TCP sockets have a limited scope, which makes it harder to get highly available data transfer when you're using multi-homed hosts ๐ŸŒ.
  • TCP is pretty vulnerable to denial-of-service attacks, like SYN attacks. Not it ๐Ÿ’€.

Transporting PSTN signaling over the IP network is an app where every single one of these TCP limitations shows up ๐Ÿšจ. That's what directly motivated SCTP, but other apps might find SCTP a really good match for what they need too โœจ. A great example is the use of data channels in the WebRTC infrastructure ๐ŸŽ€.

1.2. Architectural View of SCTP ๐Ÿฆ‹

okay, picture this. SCTP sits as a layer between the SCTP user application (the "SCTP user" for short) and a connectionless packet network service, like IP ๐Ÿงš. The rest of this document assumes SCTP runs on top of IP. The main thing SCTP does is reliably transfer user messages between peer SCTP users, and it does that inside an association between two SCTP endpoints ๐Ÿ’œ. Section 11 of this document sketches the API at the boundary between SCTP and the SCTP upper layers, so check that out too ๐ŸŒธ.

SCTP is connection oriented, but an SCTP association is a much bigger concept than a TCP connection, and that's the main thing to get ๐Ÿ™ˆ. During association startup, each SCTP endpoint (see Section 1.3) gives the other endpoint a list of transport addresses (multiple IP addresses paired with an SCTP port) that it can be reached at and that it will send SCTP packets from ๐Ÿ“ฌ. The association covers transfers over all of the possible source/destination combinations you can make from each endpoint's lists. Main character energy, honestly ๐Ÿ‘‘โœจ.

 _____________                                      _____________
|  SCTP User  |                                    |  SCTP User  |
| Application |                                    | Application |
|-------------|                                    |-------------|
|    SCTP     |                                    |    SCTP     |
|  Transport  |                                    |  Transport  |
|   Service   |                                    |   Service   |
|-------------|                                    |-------------|
|             |One or more    ----      One or more|             |
| IP Network  |IP address      \/        IP address| IP Network  |
|   Service   |appearances     /\       appearances|   Service   |
|_____________|               ----                 |_____________|

  SCTP Node A |<-------- Network transport ------->| SCTP Node B

Figure 1: An SCTP Association ๐ŸŽ€

Okay, and in addition to wrapping SCTP packets in IPv4 or IPv6, you can also encapsulate SCTP packets in UDP as specified in [RFC6951], or encapsulate them in DTLS as specified in [RFC8261] ๐Ÿ’…๐ŸŒˆ. So many options, bestie, I'm obsessed ๐Ÿ˜ญ๐Ÿซถ.

1.3. Key Terms ๐Ÿ’…

Okay bestie, some of these words already popped up in the earlier sections, so this is the glossary, all in one place. Read it once and we'll all be speaking the same language ๐Ÿซถ

  • Active Destination Transport Address: A transport address on a peer endpoint that a transmitting endpoint considers available for receiving user messages. โœจ
  • Association Maximum DATA Chunk Size (AMDCS): The smallest Path Maximum DATA Chunk Size (PMDCS) of all destination addresses. ๐Ÿ“
  • Bundling of Chunks: An optional multiplexing operation, whereby more than one chunk can be carried in the same SCTP packet. ๐ŸŽ€
  • Bundling of User Messages: An optional multiplexing operation, whereby more than one user message can be carried in the same SCTP packet. Each user message occupies its own DATA chunk. ๐Ÿงš
  • Chunk: A unit of information within an SCTP packet, consisting of a chunk header and chunk-specific content. ๐Ÿ“
  • Congestion Window (cwnd): An SCTP variable that limits outstanding data, in number of bytes, that a sender can send to a particular destination transport address before receiving an acknowledgement. ๐Ÿšฆ
  • Control Chunk: A chunk not being used for transmitting user data, i.e., every chunk that is not a DATA chunk. ๐Ÿ™…โ€โ™€๏ธ
  • Cumulative TSN Ack Point: The Transmission Sequence Number (TSN) of the last DATA chunk acknowledged via the Cumulative TSN Ack field of a SACK chunk. ๐Ÿ“
  • Flightsize: The number of bytes of outstanding data to a particular destination transport address at any given time. โœˆ๏ธ
  • Idle Destination Address: An address that has not had user messages sent to it within some length of time, normally the 'HB.interval' or greater. ๐Ÿ˜ด
  • Inactive Destination Transport Address: An address that is considered inactive due to errors and unavailable to transport user messages. ๐Ÿ’ค
  • Message (or User Message): Data submitted to SCTP by the Upper-Layer Protocol (ULP). ๐Ÿ’Œ
  • Network Byte Order: Most significant byte first, a.k.a., big endian. ๐Ÿ”ข
  • Ordered Message: A user message that is delivered in order with respect to all previous user messages sent within the stream on which the message was sent. ๐Ÿ“š
  • Outstanding Data (or Data Outstanding or Data In Flight): The total size of the DATA chunks associated with outstanding TSNs. A retransmitted DATA chunk is counted once in outstanding data. A DATA chunk that is classified as lost but that has not yet been retransmitted is not in outstanding data. ๐Ÿ›ซ
  • Outstanding TSN (at an SCTP Endpoint): A TSN (and the associated DATA chunk) that has been sent by the endpoint but for which it has not yet received an acknowledgement. โณ
  • "Out of the Blue" (OOTB) Packet: A correctly formed packet, for which the receiver cannot identify the association it belongs to. See Section 8.4. ๐Ÿ›ธ
  • Path: The route taken by the SCTP packets sent by one SCTP endpoint to a specific destination transport address of its peer SCTP endpoint. Sending to different destination transport addresses does not necessarily guarantee getting separate paths. Within this specification, a path is identified by the destination transport address, since the routing is assumed to be stable. This includes, in particular, the source address being selected when sending packets to the destination address. ๐Ÿ›ค๏ธ
  • Path Maximum DATA Chunk Size (PMDCS): The maximum size (including the DATA chunk header) of a DATA chunk that fits into an SCTP packet not exceeding the PMTU of a particular destination address. ๐Ÿงฉ
  • Path Maximum Transmission Unit (PMTU): The maximum size (including the SCTP common header and all chunks including their paddings) of an SCTP packet that can be sent to a particular destination address without using IP-level fragmentation. ๐Ÿ“ฆ
  • Primary Path: The destination and source address that will be put into a packet outbound to the peer endpoint by default. The definition includes the source address since an implementation MAY wish to specify both destination and source address to better control the return path taken by reply chunks and on which interface the packet is transmitted when the data sender is multi-homed. ๐Ÿ 
  • Receiver Window (rwnd): An SCTP variable a data sender uses to store the most recently calculated receiver window of its peer, in number of bytes. This gives the sender an indication of the space available in the receiver's inbound buffer. ๐ŸชŸ
  • SCTP Association: A protocol relationship between SCTP endpoints, composed of the two SCTP endpoints and protocol state information, including Verification Tags and the currently active set of Transmission Sequence Numbers (TSNs), etc. An association can be uniquely identified by the transport addresses used by the endpoints in the association. Two SCTP endpoints MUST NOT have more than one SCTP association between them at any given time. ๐Ÿค
  • SCTP Endpoint: The logical sender/receiver of SCTP packets. On a multi-homed host, an SCTP endpoint is represented to its peers as a combination of a set of eligible destination transport addresses to which SCTP packets can be sent and a set of eligible source transport addresses from which SCTP packets can be received. All transport addresses used by an SCTP endpoint MUST use the same port number but can use multiple IP addresses. A transport address used by an SCTP endpoint MUST NOT be used by another SCTP endpoint. In other words, a transport address is unique to an SCTP endpoint. ๐Ÿ™‹โ€โ™€๏ธ
  • SCTP Packet (or Packet): The unit of data delivery across the interface between SCTP and the connectionless packet network (e.g., IP). An SCTP packet includes the common SCTP header, possible SCTP control chunks, and user data encapsulated within SCTP DATA chunks. ๐ŸŽ
  • SCTP User Application (or SCTP User): The logical higher-layer application entity that uses the services of SCTP, also called the Upper-Layer Protocol (ULP). ๐Ÿ‘ฉโ€๐Ÿ’ป
  • Slow-Start Threshold (ssthresh): An SCTP variable. This is the threshold that the endpoint will use to determine whether to perform slow-start or congestion avoidance on a particular destination transport address. Ssthresh is in number of bytes. ๐ŸŒฑ
  • State Cookie: A container of all information needed to establish an association. ๐Ÿช
  • Stream: A unidirectional logical channel established from one to another associated SCTP endpoint, within which all user messages are delivered in sequence, except for those submitted to the unordered delivery service. ๐Ÿ›ฃ๏ธ
    • Note: The relationship between stream numbers in opposite directions is strictly a matter of how the applications use them. It is the responsibility of the SCTP user to create and manage these correlations if they are so desired. ๐Ÿ—๏ธ
  • Stream Sequence Number: A 16-bit sequence number used internally by SCTP to ensure sequenced delivery of the user messages within a given stream. One Stream Sequence Number is attached to each ordered user message. ๐Ÿ”
  • Tie-Tags: Two 32-bit random numbers that together make a 64-bit nonce. These tags are used within a State Cookie and TCB so that a newly restarting association can be linked to the original association within the endpoint that did not restart and yet not reveal the true Verification Tags of an existing association. ๐ŸŽŸ๏ธ
  • Transmission Control Block (TCB): An internal data structure created by an SCTP endpoint for each of its existing SCTP associations to other SCTP endpoints. TCB contains all the status and operational information for the endpoint to maintain and manage the corresponding association. ๐Ÿ—‚๏ธ
  • Transmission Sequence Number (TSN): A 32-bit sequence number used internally by SCTP. One TSN is attached to each chunk containing user data to permit the receiving SCTP endpoint to acknowledge its receipt and detect duplicate deliveries. ๐Ÿ”ข
  • Transport Address: A transport address is typically defined by a network-layer address, a transport-layer protocol, and a transport-layer port number. In the case of SCTP running over IP, a transport address is defined by the combination of an IP address and an SCTP port number (where SCTP is the transport protocol). ๐Ÿ“ฌ
  • Unordered Message: Unordered messages are "unordered" with respect to any other message; this includes both other unordered messages as well as other ordered messages. An unordered message might be delivered prior to or later than ordered messages sent on the same stream. ๐ŸŽฒ
  • User Message: The unit of data delivery across the interface between SCTP and its user. ๐Ÿ’ฌ
  • Verification Tag: A 32-bit unsigned integer that is randomly generated. The Verification Tag provides a key that allows a receiver to verify that the SCTP packet belongs to the current association and is not an old or stale packet from a previous association. ๐Ÿ”

1.4. Abbreviations ๐Ÿ”ค

Here's the cheat sheet for all the acronyms we've been throwing around, so nobody gets lost in the alphabet soup ๐Ÿฒ

   MAC     Message Authentication Code [RFC2104]

   RTO     Retransmission Timeout

   RTT     Round-Trip Time

   RTTVAR  Round-Trip Time Variation

   SCTP    Stream Control Transmission Protocol

   SRTT    Smoothed RTT

   TCB     Transmission Control Block

   TLV     Type-Length-Value coding format

   TSN     Transmission Sequence Number

   ULP     Upper-Layer Protocol

1.5. Functional View of SCTP ๐Ÿฆ‹

Okay so the SCTP transport service is lowkey a big multitasker, and it can be broken down into a bunch of functions. The diagram below shows them all, and we'll walk through each one in the rest of this section ๐Ÿ’–

                       SCTP User Application

       -----------------------------------------------------
        _____________                  ____________________
       |             |                | Sequenced Delivery |
       | Association |                |   within Streams   |
       |             |                |____________________|
       |   Startup   |
       |             |         ____________________________
       |     and     |        |  User Data Fragmentation   |
       |             |        |____________________________|
       |  Takedown   |
       |             |         ____________________________
       |             |        |      Acknowledgement       |
       |             |        |            and             |
       |             |        |    Congestion Avoidance    |
       |             |        |____________________________|
       |             |
       |             |         ____________________________
       |             |        |       Chunk Bundling       |
       |             |        |____________________________|
       |             |
       |             |     ________________________________
       |             |    |       Packet Validation        |
       |             |    |________________________________|
       |             |
       |             |     ________________________________
       |             |    |        Path Management         |
       |_____________|    |________________________________|

      Figure 2: Functional View of the SCTP Transport Service

1.5.1. Association Startup and Takedown ๐Ÿš€

okay so an association gets started when the SCTP user asks for it (peep the ASSOCIATE (or SEND) primitive in Section 11 ๐ŸŽ€).

a cookie mechanism, similar to the one Karn and Simpson described in [RFC2522], gets used during initialization to protect against synchronization attacks ๐Ÿ›ก๏ธ. it's a four-way handshake, and the last two legs are allowed to carry user data so setup is extra quick โšก. the startup sequence is in Section 5 of this document.

SCTP also does graceful close (aka shutdown) of an active association when the SCTP user requests it. check out the SHUTDOWN primitive in Section 11 ๐Ÿ’…. it can also do an ungraceful close (aka abort), either because the user hit the ABORT primitive or because the SCTP layer spotted an error ๐Ÿ’€. Section 9 covers both the graceful and the ungraceful close procedures.

SCTP does NOT do a half-open state like TCP, where one side keeps sending while the other end is closed ๐Ÿ™…โ€โ™€๏ธ. the word NOT here is just me being loud. wait, that's wrong, it isn't in caps in the original, so let me say it properly: SCTP does not support a half-open state (like TCP) where one side keeps sending data while the other end is closed. when either endpoint does a shutdown, the association on each peer stops accepting new data from its user and only delivers the data already queued at the time of the graceful close (see Section 9) ๐ŸŒธ.

1.5.2. Sequenced Delivery within Streams ๐ŸŒˆ

the word "stream" in SCTP means a sequence of user messages that have to be delivered to the upper-layer protocol in order, compared to the other messages in that same stream. this is different from TCP, where a stream is a sequence of bytes (in this document, a byte is assumed to be 8 bits) ๐Ÿงš.

at association startup, the SCTP user can say how many streams the association should support. this number gets negotiated with the remote end (see Section 5.1.1) ๐Ÿค. user messages are tied to stream numbers (SEND and RECEIVE primitives; Section 11). internally, SCTP gives each message the SCTP user passes in a Stream Sequence Number. on the receiving side, SCTP makes sure messages reach the SCTP user in sequence within a given stream โœจ. the cute part is that while one stream is stuck waiting for the next in-sequence message, delivery from the other streams can keep going ๐Ÿฆ‹.

SCTP also has a way to skip the sequenced delivery service. user messages sent this way get delivered to the SCTP user the moment they arrive ๐Ÿƒโ€โ™€๏ธ.

1.5.3. User Data Fragmentation ๐Ÿงฉ

when it's needed, SCTP fragments user messages so the SCTP packet handed to the lower layer doesn't go over the PMTU ๐Ÿ“. once a user message has been fragmented, that fragmentation can't be changed anymore ๐Ÿ”’. on receipt, fragments get reassembled into complete messages before they're passed to the SCTP user ๐Ÿ’–.

1.5.4. Acknowledgement and Congestion Avoidance ๐ŸŒŠ

SCTP gives each user data fragment or unfragmented message a Transmission Sequence Number (TSN). the TSN is independent of any Stream Sequence Number assigned at the stream level ๐Ÿ™Œ. the receiving end acknowledges every TSN it receives, even if there are gaps in the sequence. if a user data fragment or unfragmented message needs a retransmit, the TSN it was given is reused. that's how reliable delivery stays functionally separate from sequenced stream delivery ๐Ÿ‘‘.

the acknowledgement and congestion avoidance function handles packet retransmission when a timely acknowledgement hasn't come back. retransmission is governed by congestion avoidance procedures that are similar to the ones used for TCP. see Sections 6 and 7 for the detailed protocol procedures for this function ๐Ÿฆข.

1.5.5. Chunk Bundling ๐Ÿ“ฆ

as Section 3 describes, the SCTP packet handed to the lower layer is a common header followed by one or more chunks. each chunk holds either user data or SCTP control information ๐Ÿ’…. an SCTP implementation that supports bundling on the sender side might hold back sending user messages so the matching DATA chunks can be bundled together ๐Ÿ“.

the SCTP user can choose to ask that an SCTP implementation does not delay sending a user message just for this purpose. even when the user picks that option, the implementation might still delay the sending for other reasons (for example, congestion control or flow control), and it might also bundle multiple DATA chunks if it can ๐Ÿ™ˆ.

1.5.6. Packet Validation ๐Ÿ”

the SCTP common header includes a mandatory Verification Tag field and a 32-bit checksum field (see Appendix A for a description of the 32-bit Cyclic Redundancy Check (CRC32c) checksum) ๐ŸŽ€. each end of the association picks the Verification Tag value during association startup. packets that show up without the expected Verification Tag value get discarded, which protects against blind masquerade attacks and stale SCTP packets from a previous association ๐Ÿ›ก๏ธ. the sender of each SCTP packet sets the CRC32c checksum to add more protection against data corruption in the network. the receiver of an SCTP packet with an invalid CRC32c checksum silently discards that packet ๐Ÿคซ.

1.5.7. Path Management ๐Ÿ›ฃ๏ธ

the sending SCTP user can change the set of transport addresses used as destinations for SCTP packets, using the primitives in Section 11 ๐Ÿ—บ๏ธ. the SCTP path management function monitors reachability with heartbeats when other packet traffic isn't enough to tell, and it tells the SCTP user whenever the reachability of any transport address of the peer endpoint changes ๐Ÿ“ก. for each outgoing SCTP packet, the path management function picks the destination transport address based on the SCTP user's instructions and the currently perceived reachability status of the eligible destination set ๐ŸŽฏ. it's also in charge of reporting the eligible set of local transport addresses to the peer endpoint during association startup, and of passing the transport addresses returned from the peer endpoint on to the SCTP user ๐Ÿ’Œ.

at association startup, a primary path is defined for each SCTP endpoint, and it's used to send SCTP packets normally ๐ŸŒŸ.

on the receiving end, path management is responsible for checking that a valid SCTP association exists for an inbound SCTP packet before the packet gets passed on for further processing ๐Ÿ•ต๏ธโ€โ™€๏ธ.

Note: Path Management and Packet Validation are done at the same time; although described separately above, in reality, they cannot be performed as separate items. ๐Ÿซถ

1.6. Serial Number Arithmetic ๐Ÿ”ข

it's super important to remember that the Transmission Sequence Number space is finite, even though it's very large. this space ranges from 0 to 2^32 - 1. because the space is finite, all arithmetic dealing with Transmission Sequence Numbers MUST be performed modulo 2^32. this unsigned arithmetic keeps the relationship between sequence numbers intact as they cycle from 2^32 - 1 back to 0. computer modulo arithmetic has some subtleties, so you have to be really careful when you program comparisons of these values ๐Ÿ˜ญ. when referring to TSNs, the symbol <= means "less than or equal" (modulo 2^32).

comparisons and arithmetic on TSNs in this document SHOULD use Serial Number Arithmetic, as defined in [RFC1982], where SERIAL_BITS = 32.

an endpoint SHOULD NOT transmit a DATA chunk with a TSN that is more than 2^31 - 1 above the beginning TSN of its current send window. doing that will cause problems when comparing TSNs ๐Ÿ™ƒ.

Transmission Sequence Numbers wrap around when they reach 2^32 - 1. that means the next TSN a DATA chunk MUST use after transmitting TSN = 2^32 - 1 is TSN = 0 ๐Ÿ”„.

any arithmetic done on Stream Sequence Numbers SHOULD use Serial Number Arithmetic, as defined in [RFC1982], where SERIAL_BITS = 16. all other arithmetic and comparisons in this document use normal arithmetic ๐Ÿงฎ.

1.7. Changes from RFC 4960 ๐Ÿ“

SCTP was originally defined in [RFC4960], which this document obsoletes. if you're curious about the details of the various changes this document includes, go check out [RFC8540] ๐Ÿ‘€.

on top of these and some further editorial changes, this document also includes the following changes:

  • Update references. ๐Ÿ“š
  • Improve the language related to requirements levels. ๐Ÿ’ฌ
  • Allow the ASSOCIATE primitive to take multiple remote addresses; also refer to the socket API specification. ๐Ÿ”Œ
  • Refer to the Packetization Layer Path MTU Discovery (PLPMTUD) specification for path MTU discovery. ๐Ÿ›ค๏ธ
  • Move the description of ICMP handling from the Appendix to the main text. ๐Ÿšš
  • Remove the Appendix describing Explicit Congestion Notification (ECN) handling from the document. ๐Ÿ‘‹
  • Describe the packet size handling more precisely by introducing PMTU, PMDCS, and AMDCS. ๐Ÿ“
  • Add the definition of control chunk. ๐Ÿงธ
  • Improve the description of the handling of INIT and INIT ACK chunks with invalid mandatory parameters. ๐Ÿซง
  • Allow using L > 1 for Appropriate Byte Counting (ABC) during slow start. ๐Ÿข
  • Explicitly describe the reinitialization of the congestion controller on route changes. ๐Ÿ—บ๏ธ
  • Improve the terminology to make it clear that this specification does not describe a full mesh architecture. ๐Ÿ•ธ๏ธ
  • Improve the description of sequence number generation (Transmission Sequence Number and Stream Sequence Number). ๐Ÿ”ข
  • Improve the description of reneging. ๐Ÿ™…โ€โ™€๏ธ
  • Don't require the change of the Cumulative TSN Ack anymore for increasing the congestion window. This improves the consistency with the handling in congestion avoidance. ๐Ÿ“ˆ
  • Improve the description of the State Cookie. ๐Ÿช
  • Fix the API for retrieving messages in case of association failures. ๐Ÿ› ๏ธ

2. Conventions ๐Ÿ’œ

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here โœจ.

3. SCTP Packet Format โœจ

okay so an SCTP packet is basically a little package ๐ŸŽ€ with a common header up front and then a bunch of chunks after it. Each chunk holds either control info or user data, so the chunks are the actual juicy stuff and the header is the wrapper that says who it's from and where it's headed ๐Ÿ’…

Here's the layout (it's giving boxes ๐Ÿ“ฆ):

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                         Common Header                         |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                           Chunk #1                            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                              ...                              |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                           Chunk #n                            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Okay, the big bundling rule ๐Ÿšจ: INIT, INIT ACK, and SHUTDOWN COMPLETE chunks MUST NOT be bundled with any other chunk into an SCTP packet. They're solo-only, no exceptions ๐Ÿ™…โ€โ™€๏ธ Everything else MAY be bundled together to form an SCTP packet, as long as that packet does not exceed the PMTU. For the full bundling tea, check Section 6.10 ๐Ÿงš

If a user data message is too big to fit into one SCTP packet, no panic ๐Ÿ˜ญ it can be fragmented into multiple chunks using the procedure defined in Section 6.9.

And one more thing that's non-negotiable: all integer fields in an SCTP packet MUST be transmitted in network byte order, unless otherwise stated ๐Ÿ”ขโœจ

3.1. SCTP Common Header Field Descriptions

Let's meet the header fields, bestie ๐Ÿซถ here's the picture first:

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |      Source Port Number       |    Destination Port Number    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                       Verification Tag                        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                           Checksum                            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Source Port Number: 16 bits (unsigned integer) ๐ŸŒธ This is the SCTP sender's port number. The receiver can use it together with the source IP address, the SCTP Destination Port Number, and possibly the destination IP address to figure out which association this packet belongs to. The Source Port Number 0 MUST NOT be used ๐Ÿ™…โ€โ™€๏ธ

Destination Port Number: 16 bits (unsigned integer) ๐Ÿฆ‹ This is the SCTP port number the packet is destined for. The receiving host uses this port number to de-multiplex the SCTP packet to the correct receiving endpoint/application, so it's basically the address label on the envelope ๐Ÿ’Œ The Destination Port Number 0 MUST NOT be used.

Verification Tag: 32 bits (unsigned integer) ๐Ÿ” The receiver of an SCTP packet uses the Verification Tag to validate the sender of the packet. It's the secret handshake ๐Ÿคซ On transmit, the value of the Verification Tag MUST be set to the value of the Initiate Tag received from the peer endpoint during association initialization, with the following exceptions:

  • A packet containing an INIT chunk MUST have a zero Verification Tag.
  • A packet containing a SHUTDOWN COMPLETE chunk with the T bit set MUST have the Verification Tag copied from the packet with the SHUTDOWN ACK chunk.
  • A packet containing an ABORT chunk MAY have the Verification Tag copied from the packet that caused the ABORT chunk to be sent. For details, see Sections 8.4 and 8.5.

Checksum: 32 bits (unsigned integer) ๐Ÿงฎ This field contains the checksum of the SCTP packet. Its calculation is discussed in Section 6.8. SCTP uses the CRC32c algorithm, as described in Appendix A, to calculate the checksum. Math never looked so iconic ๐Ÿ‘‘

3.2. Chunk Field Descriptions โœจ

okay bestie, every chunk in an SCTP packet gets the same four-part outfit: a Chunk Type, Chunk Flags, a Chunk Length, and a Chunk Value ๐ŸŽ€ Here's how it's laid out:

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |  Chunk Type   |  Chunk Flags  |         Chunk Length          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  \                                                               \
  /                          Chunk Value                          /
  \                                                               \
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Type: 8 bits (unsigned integer) ๐Ÿฆ‹ This tells you what kind of info is inside the Chunk Value. It runs from 0 to 254. The value 255 is saved for future use as an extension field, so it's giving "reserved, don't touch" ๐Ÿ’…

The chunk types defined in this document are:

     +==========+===========================================+
     | ID Value | Chunk Type                                |
     +==========+===========================================+
     | 0        | Payload Data (DATA)                       |
     +----------+-------------------------------------------+
     | 1        | Initiation (INIT)                         |
     +----------+-------------------------------------------+
     | 2        | Initiation Acknowledgement (INIT ACK)     |
     +----------+-------------------------------------------+
     | 3        | Selective Acknowledgement (SACK)          |
     +----------+-------------------------------------------+
     | 4        | Heartbeat Request (HEARTBEAT)             |
     +----------+-------------------------------------------+
     | 5        | Heartbeat Acknowledgement (HEARTBEAT ACK) |
     +----------+-------------------------------------------+
     | 6        | Abort (ABORT)                             |
     +----------+-------------------------------------------+
     | 7        | Shutdown (SHUTDOWN)                       |
     +----------+-------------------------------------------+
     | 8        | Shutdown Acknowledgement (SHUTDOWN ACK)   |
     +----------+-------------------------------------------+
     | 9        | Operation Error (ERROR)                   |
     +----------+-------------------------------------------+
     | 10       | State Cookie (COOKIE ECHO)                |
     +----------+-------------------------------------------+
     | 11       | Cookie Acknowledgement (COOKIE ACK)       |
     +----------+-------------------------------------------+
     | 12       | Reserved for Explicit Congestion          |
     |          | Notification Echo (ECNE)                  |
     +----------+-------------------------------------------+
     | 13       | Reserved for Congestion Window Reduced    |
     |          | (CWR)                                     |
     +----------+-------------------------------------------+
     | 14       | Shutdown Complete (SHUTDOWN COMPLETE)     |
     +----------+-------------------------------------------+
     | 15 to 62 | Unassigned                                |
     +----------+-------------------------------------------+
     | 63       | Reserved for IETF-defined Chunk           |
     |          | Extensions                                |
     +----------+-------------------------------------------+
     | 64 to    | Unassigned                                |
     | 126      |                                           |
     +----------+-------------------------------------------+
     | 127      | Reserved for IETF-defined Chunk           |
     |          | Extensions                                |
     +----------+-------------------------------------------+
     | 128 to   | Unassigned                                |
     | 190      |                                           |
     +----------+-------------------------------------------+
     | 191      | Reserved for IETF-defined Chunk           |
     |          | Extensions                                |
     +----------+-------------------------------------------+
     | 192 to   | Unassigned                                |
     | 254      |                                           |
     +----------+-------------------------------------------+
     | 255      | Reserved for IETF-defined Chunk           |
     |          | Extensions                                |
     +----------+-------------------------------------------+

                      Table 1: Chunk Types
โš ๏ธ Heads up: the ECNE and CWR chunk types are reserved for future use of Explicit Congestion Notification (ECN). Not in use yet, so don't go looking for them in your packets ๐Ÿ™ˆ

Chunk Types are encoded so that the top 2 bits tell the processing endpoint what to do if it doesn't recognize the Chunk Type. It's like a built-in instruction manual, honestly iconic ๐Ÿ‘‘

    +----+--------------------------------------------------+
    | 00 | Stop processing this SCTP packet and discard the |
    |    | unrecognized chunk and all further chunks.       |
    +----+--------------------------------------------------+
    | 01 | Stop processing this SCTP packet, discard the    |
    |    | unrecognized chunk and all further chunks, and   |
    |    | report the unrecognized chunk in an ERROR chunk  |
    |    | using the 'Unrecognized Chunk Type' error cause. |
    +----+--------------------------------------------------+
    | 10 | Skip this chunk and continue processing.         |
    +----+--------------------------------------------------+
    | 11 | Skip this chunk and continue processing, but     |
    |    | report it in an ERROR chunk using the            |
    |    | 'Unrecognized Chunk Type' error cause.           |
    +----+--------------------------------------------------+

                 Table 2: Processing of Unknown Chunks

Chunk Flags: 8 bits ๐ŸŒธ What these bits mean depends on the Chunk Type. Unless a chunk says otherwise, they get set to 0 when you send and get ignored when you receive. Chill vibes ๐Ÿงš

Chunk Length: 16 bits (unsigned integer) ๐Ÿ“ This is the chunk's total size in bytes, counting the Chunk Type, Chunk Flags, Chunk Length, and Chunk Value fields. So if the Chunk Value is empty, the Length is 4. Chunk padding doesn't count toward this number, but padding on variable-length parameters, except the last parameter in the chunk, does get included ๐Ÿ“

๐Ÿ“ Note: a robust implementation is expected to accept the chunk whether or not the final padding was included in the Chunk Length. Be generous, bestie ๐Ÿซถ

Chunk Value: variable length ๐Ÿ’œ This is the actual info being delivered in the chunk. Its usage and format depend on the Chunk Type, so it changes like an outfit for every occasion โœจ

The total length of a chunk (Type, Length, and Value fields included) MUST be a multiple of 4 bytes. If it isn't, the sender MUST pad the chunk with all-zero bytes, and that padding is NOT counted in the Chunk Length field. The sender MUST NOT pad with more than 3 bytes. The receiver MUST ignore the padding bytes. Padding rules, locked in ๐Ÿ”’

SCTP-defined chunks get the full tea in Section 3.3, and the guidelines for IETF-defined chunk extensions live in Section 15.1 of this document ๐Ÿฆ‹

3.2.1. Optional/Variable-Length Parameter Format ๐Ÿงš

Okay so every SCTP control chunk's value starts with a chunk-type-specific header full of required fields, and then zero or more parameters come after it ๐Ÿ’–. The optional and variable-length parameters all use a Type-Length-Value format, and it looks like this:

  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
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |        Parameter Type         |       Parameter Length        |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 \                                                               \
 /                        Parameter Value                        /
 \                                                               \
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Parameter Type: 16 bits (unsigned integer) ๐ŸŽ€ The Type field is a 16-bit ID for what kind of parameter it is. It goes from 0 to 65534.

The value 65535 is reserved for IETF-defined extensions. Anything not defined in a specific SCTP chunk's description is reserved for use by IETF, so hands off ๐Ÿ™…โ€โ™€๏ธ

Parameter Length: 16 bits (unsigned integer) ๐ŸŒธ The Parameter Length field holds the size of the whole parameter in bytes. That counts the Parameter Type, Parameter Length, and Parameter Value fields. So a parameter with a zero-length Parameter Value would have a Parameter Length of 4. The Parameter Length does not include any padding bytes.

Parameter Value: variable length ๐Ÿฆ‹ This is where the actual info being carried in the parameter lives.

The total length of a parameter (counting the Parameter Type, Parameter Length, and Parameter Value fields) MUST be a multiple of 4 bytes. If it isn't, the sender pads the end of the parameter (after the Parameter Value field) with all zero bytes. That padding isn't counted in the Parameter Length field. A sender MUST NOT pad with more than 3 bytes, and the receiver MUST ignore the padding bytes. Padding rules are a lowkey big deal ๐Ÿซถ

The Parameter Types are encoded so that the highest-order 2 bits tell you what to do if the processing endpoint doesn't recognize the Parameter Type. Here's the breakdown:

+----+--------------------------------------------------------+
| 00 | Stop processing this parameter and do not process any  |
|    | further parameters within this chunk.                  |
+----+--------------------------------------------------------+
| 01 | Stop processing this parameter, do not process any     |
|    | further parameters within this chunk, and report the   |
|    | unrecognized parameter, as described in Section 3.2.2. |
+----+--------------------------------------------------------+
| 10 | Skip this parameter and continue processing.           |
+----+--------------------------------------------------------+
| 11 | Skip this parameter and continue processing, but       |
|    | report the unrecognized parameter, as described in     |
|    | Section 3.2.2.                                         |
+----+--------------------------------------------------------+

             Table 3: Processing of Unknown Parameters

Please note ๐Ÿ“ that when an INIT or INIT ACK chunk is received, in all four cases an INIT ACK or COOKIE ECHO chunk gets sent back in response, respectively. In the 00 or 01 case, the processing of the parameters after the unknown one gets canceled, but nothing that was already processed gets rolled back. Main character processing energy ๐Ÿ’…

The actual SCTP parameters are defined in the specific SCTP chunk sections. The rules for IETF-defined parameter extensions are in Section 15.3. Parameter types MUST be unique across all chunks. For example, the parameter type '5' is used to represent an IPv4 address (see Section 3.3.2.1.1). That means '5' is reserved across all chunks to mean an IPv4 address, and it MUST NOT be reused with a different meaning in any other chunk. Don't be that person who recycles type numbers ๐Ÿ™ˆ

3.2.2. Reporting of Unrecognized Parameters โœจ

Okay so if the receiver of an INIT chunk spots unrecognized parameters and has to report them under Section 3.2.1, it MUST put the "Unrecognized Parameter" parameter(s) in the INIT ACK chunk sent back in response to the INIT chunk. Also, if the receiver of the INIT chunk isn't going to establish an association (for example, because it's out of resources), an "Unrecognized Parameters" error cause would NOT be included with any ABORT chunk sent to the INIT chunk's sender. Wait, let me restate that properly: it would not be included, full stop ๐Ÿ’€

Now, if the receiver of any other chunk (like INIT ACK) detects unrecognized parameters and has to report them under Section 3.2.1, it SHOULD bundle the ERROR chunk with the "Unrecognized Parameters" error cause together with the chunk sent in response (like COOKIE ECHO). If the receiver of an INIT ACK chunk can't bundle the COOKIE ECHO chunk with the ERROR chunk, the ERROR chunk MAY be sent separately, but not before the COOKIE ACK chunk has been received. Bundling is giving organized ๐ŸŽ€

Any time a COOKIE ECHO chunk is sent in a packet, it MUST be the first chunk. No exceptions, bestie ๐Ÿ‘‘

3.3. SCTP Chunk Definitions ๐Ÿฆ‹

This section defines the format of the different SCTP chunk types. Let's get into the good stuff โœจ

3.3.1. Payload Data (DATA) (0) ๐Ÿ’–

The following format MUST be used for the DATA chunk:

  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
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |   Type = 0    |  Res  |I|U|B|E|            Length             |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                              TSN                              |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |      Stream Identifier S      |   Stream Sequence Number n    |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                  Payload Protocol Identifier                  |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 \                                                               \
 /                 User Data (seq n of Stream S)                 /
 \                                                               \
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Res: 4 bits ๐ŸŒˆ All set to 0 on transmit and ignored on receipt.

I bit: 1 bit ๐Ÿ”ฅ The (I)mmediate bit MAY be set by the sender whenever the sender of a DATA chunk can benefit from the corresponding SACK chunk being sent back without delay. See Section 4 of [RFC7053] for a discussion of the benefits.

U bit: 1 bit ๐Ÿงš The (U)nordered bit, if set to 1, means this is an unordered DATA chunk, and there's no Stream Sequence Number assigned to it. So the receiver MUST ignore the Stream Sequence Number field.

After reassembly (if it's needed), unordered DATA chunks MUST be dispatched to the upper layer by the receiver without any attempt to reorder them. Chaotic good energy ๐ŸŒช๏ธ

If an unordered user message is fragmented, each fragment of the message MUST have its U bit set to 1. Every piece has to match, no exceptions ๐Ÿ’…

B bit: 1 bit ๐Ÿ“ The (B)eginning fragment bit, if set, marks the first fragment of a user message.

E bit: 1 bit ๐ŸŒธ The (E)nding fragment bit, if set, marks the last fragment of a user message.

Length: 16 bits (unsigned integer) ๐ŸŽ€ This field is the length of the DATA chunk in bytes, from the start of the type field to the end of the User Data field, not counting any padding. A DATA chunk with one byte of user data will have the Length field set to 17 (meaning 17 bytes).

A DATA chunk with a User Data field of length L will have the Length field set to (16 + L), meaning 16 + L bytes, where L MUST be greater than 0. Zero-length user data is not it ๐Ÿ™…โ€โ™€๏ธ

TSN: 32 bits (unsigned integer) ๐Ÿฆ‹ This value is the TSN for this DATA chunk. The valid range is from 0 to 4294967295 (2^32 - 1). TSN wraps back to 0 after reaching 4294967295, which is very plot twist of it ๐ŸŒ€

Stream Identifier S: 16 bits (unsigned integer) ๐ŸŒท Identifies the stream that the following user data belongs to.

Stream Sequence Number n: 16 bits (unsigned integer) ๐Ÿ’œ This value is the Stream Sequence Number of the following user data within stream S. The valid range is 0 to 65535.

When a user message gets fragmented by SCTP for transport, the same Stream Sequence Number MUST be carried in each of the fragments of the message. Same number, every fragment, always ๐Ÿซถ

Payload Protocol Identifier: 32 bits (unsigned integer) ๐Ÿง This value is an application (or upper layer) specified protocol identifier. The upper layer hands it to SCTP, and SCTP sends it along to its peer. SCTP doesn't use this identifier itself, but certain network entities, as well as the peer application, can use it to figure out what kind of information is in this DATA chunk. This field MUST be sent even in fragmented DATA chunks (so it's still available for agents sitting in the middle of the network). Also, this field isn't touched by an SCTP implementation. The upper layer is responsible for the host to network byte order conversion of this field ๐Ÿง 

The value 0 means that the upper layer didn't specify any application identifier for this payload data. Zero is the "no label" vibe โœจ

User Data: variable length ๐ŸŒˆ This is the payload user data. The implementation MUST pad the end of the data to a 4-byte boundary with all zero bytes. Any padding MUST NOT be included in the Length field. A sender MUST never add more than 3 bytes of padding. Padding is literally the unsung hero ๐Ÿ‘‘

An unfragmented user message MUST have both the B and E bits set to 1. Setting both the B and E bits to 0 means it's a middle fragment of a multi-fragment user message. Here's the summary:

+===+===+===========================================+
| B | E |                Description                |
+===+===+===========================================+
| 1 | 0 |  First piece of a fragmented user message |
+---+---+-------------------------------------------+
| 0 | 0 | Middle piece of a fragmented user message |
+---+---+-------------------------------------------+
| 0 | 1 |  Last piece of a fragmented user message  |
+---+---+-------------------------------------------+
| 1 | 1 |            Unfragmented message           |
+---+---+-------------------------------------------+

         Table 4: Fragment Description Flags

When a user message gets fragmented into multiple chunks, the receiver uses the TSNs to reassemble it. That means the TSNs for each fragment of a fragmented user message MUST be strictly sequential. Tiny detail, huge impact ๐Ÿ”ฅ

The TSNs of DATA chunks sent SHOULD be strictly sequential. Aim for it, it's the vibe โœจ

Note: The extension described in [RFC8260] can be used to mitigate head of line blocking when transferring large user messages. If your messages are huge and everything's getting stuck behind them, go check that one out ๐Ÿš€

3.3.2. Initiation (INIT) (1) ๐ŸŽ€

okay bestie, this is the INIT chunk and it's literally how an SCTP association gets started ๐Ÿฆ‹ two endpoints use it to say "hiii, wanna talk?" ๐Ÿ’– Here's the layout:

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   Type = 1    |  Chunk Flags  |      Chunk Length             |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                         Initiate Tag                          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |          Advertised Receiver Window Credit (a_rwnd)           |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |  Number of Outbound Streams   |   Number of Inbound Streams   |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                          Initial TSN                          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  \                                                               \
  /              Optional/Variable-Length Parameters              /
  \                                                               \
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The fixed-length parameters are the must-haves ๐Ÿ’… Unless otherwise noted, each parameter MUST only be included once in the INIT chunk. Obsessed with that rule ๐Ÿ™ˆ

          +===================================+===========+
          | Fixed-Length Parameter            | Status    |
          +===================================+===========+
          | Initiate Tag                      | Mandatory |
          +-----------------------------------+-----------+
          | Advertised Receiver Window Credit | Mandatory |
          +-----------------------------------+-----------+
          | Number of Outbound Streams        | Mandatory |
          +-----------------------------------+-----------+
          | Number of Inbound Streams         | Mandatory |
          +-----------------------------------+-----------+
          | Initial TSN                       | Mandatory |
          +-----------------------------------+-----------+

           Table 5: Fixed-Length Parameters of INIT Chunks
 +===================================+============+================+
 | Variable-Length Parameter         | Status     | Type Value     |
 +===================================+============+================+
 | IPv4 Address (Note 1)             | Optional   | 5              |
 +-----------------------------------+------------+----------------+
 | IPv6 Address (Note 1)             | Optional   | 6              |
 +-----------------------------------+------------+----------------+
 | Cookie Preservative               | Optional   | 9              |
 +-----------------------------------+------------+----------------+
 | Reserved for ECN Capable (Note 2) | Optional   | 32768 (0x8000) |
 +-----------------------------------+------------+----------------+
 | Host Name Address (Note 3)        | Deprecated | 11             |
 +-----------------------------------+------------+----------------+
 | Supported Address Types (Note 4)  | Optional   | 12             |
 +-----------------------------------+------------+----------------+

          Table 6: Variable-Length Parameters of INIT Chunks

Note 1: INIT chunks can carry multiple addresses, IPv4 and/or IPv6, in any combination. So many options ๐ŸŒˆ

Note 2: The ECN Capable field is reserved for future use of Explicit Congestion Notification. ๐Ÿ“

Note 3: An INIT chunk MUST NOT contain the Host Name Address parameter. The receiver of an INIT chunk containing a Host Name Address parameter MUST send an ABORT chunk and MAY include an "Unresolvable Address" error cause. Nope, not doing that ๐Ÿ’€

Note 4: This parameter, when present, specifies all the address types the sending endpoint can support. If it's absent, that means the sending endpoint can support any address type. โœจ

If an INIT chunk shows up with all the mandatory parameters for it, the receiver SHOULD process it and send back an INIT ACK. The receiver MAY bundle an ERROR chunk with the COOKIE ACK chunk later. However, restrictive implementations MAY send back an ABORT chunk in response to the INIT chunk instead. Some are just picky, periodt ๐Ÿ‘‘

The Chunk Flags field in INIT chunks is reserved. All bits in it SHOULD be set to 0 by the sender and ignored by the receiver. ๐Ÿงš

Initiate Tag: 32 bits (unsigned integer)

The receiver of the INIT chunk (the responding end) records the value of the Initiate Tag parameter. This value MUST be placed into the Verification Tag field of every SCTP packet that the receiver of the INIT chunk transmits within this association. Main character energy for that tag ๐Ÿ’œ

The Initiate Tag is allowed to have any value except 0. Check Section 5.3.1 for more on picking the tag value. ๐Ÿซถ

If the value of the Initiate Tag in a received INIT chunk is 0, the receiver MUST silently discard the packet. Zero is a no-go, bestie ๐Ÿ˜ญ

Advertised Receiver Window Credit (a_rwnd): 32 bits (unsigned integer)

This value is the dedicated buffer space, in number of bytes, that the sender of the INIT chunk has reserved for this association along with this window. Buffer space, but make it a reservation ๐ŸŽ€

The Advertised Receiver Window Credit MUST NOT be smaller than 1500.

If a receiver gets an INIT chunk with a_rwnd set to less than 1500, it MUST discard the packet. It SHOULD send back a packet containing an ABORT chunk, using the Initiate Tag as the Verification Tag. And it MUST NOT change the state of any existing association. Not on my watch ๐Ÿ™…โ€โ™€๏ธ

During the life of the association, this buffer space SHOULD NOT be reduced. Dedicated buffers shouldn't get taken away from the association ๐Ÿ’… However, an endpoint MAY change the value of a_rwnd it sends in SACK chunks, so it's not totally frozen ๐Ÿฆ‹

Number of Outbound Streams (OS): 16 bits (unsigned integer)

This defines how many outbound streams the sender of this INIT chunk wants to create in this association. The value 0 MUST NOT be used. Zero streams is not it ๐Ÿ™ƒ

If a receiver gets an INIT chunk with OS set to 0, it MUST discard the packet. It SHOULD send back a packet containing an ABORT chunk, using the Initiate Tag as the Verification Tag. And it MUST NOT change the state of any existing association. ๐Ÿšซ

Number of Inbound Streams (MIS): 16 bits (unsigned integer)

This defines the maximum number of streams the sender of this INIT chunk allows the peer to create in this association. The value 0 MUST NOT be used.

Note: There is no negotiation of the actual number of streams. Instead, the two endpoints use min(requested, offered). See Section 5.1.1 for the details. It's giving "whoever asks for less wins" ๐Ÿ‘€

If a receiver gets an INIT chunk with MIS set to 0, it MUST discard the packet. It SHOULD send back a packet containing an ABORT chunk, using the Initiate Tag as the Verification Tag. And it MUST NOT change the state of any existing association. Same energy as before, no cap ๐Ÿง

Initial TSN (I-TSN): 32 bits (unsigned integer)

This defines the TSN that the sender of the INIT chunk will use initially. The valid range is 0 to 4294967295, and the Initial TSN SHOULD be set to a random value in that range. The methods in [RFC4086] can be used for the Initial TSN randomization. Randomness is a whole vibe ๐ŸŽฒโœจ


3.3.2.1. Optional or Variable-Length Parameters in INIT chunks

These parameters follow the Type-Length-Value format from Section 3.2.1. Any Type-Length-Value fields MUST be placed after the fixed-length fields. (The fixed-length fields are defined in the previous section.) Fixed first, then extras, that's the order, bestie ๐ŸŒธ

3.3.2.1.1. IPv4 Address (5)

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |           Type = 5            |          Length = 8           |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                         IPv4 Address                          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

IPv4 Address: 32 bits (unsigned integer). This contains an IPv4 address of the sending endpoint, binary encoded. Simple and clean ๐Ÿ“

3.3.2.1.2. IPv6 Address (6)

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |           Type = 6            |          Length = 20          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                                                               |
  |                         IPv6 Address                          |
  |                                                               |
  |                                                               |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

IPv6 Address: 128 bits (unsigned integer). This contains an IPv6 address [RFC8200] of the sending endpoint, binary encoded.

A sender MUST NOT use an IPv4-mapped IPv6 address [RFC4291]. Instead, it SHOULD use an IPv4 Address parameter for an IPv4 address. Use the right tool, babe ๐Ÿงš

Together with the Source Port Number in the SCTP common header, the value in an IPv4 or IPv6 Address parameter gives a transport address that the sender of the INIT chunk will support for the association being started. That means that during the lifetime of this association, this IP address can show up in the source address field of an IP datagram sent by the sender of the INIT chunk. It can also be used as the destination address of an IP datagram sent by the receiver of the INIT chunk. Address swapping, but make it cute ๐Ÿฆ‹

More than one IP Address parameter can be in an INIT chunk when the sender is multi-homed. A multi-homed endpoint might have access to different kinds of networks, so one INIT chunk can hold more than one address type. IPv4 and IPv6 addresses are both allowed in the same INIT chunk. Double the options, double the fun ๐ŸŒˆ

If the INIT chunk has at least one IP Address parameter, then the source address of the IP datagram carrying the INIT chunk, plus any extra addresses in the INIT, can be used as destinations by the endpoint receiving the INIT chunk. If the INIT chunk has no IP Address parameters, the receiving endpoint MUST use the source address of the received IP datagram as its only destination address for the association. Just one address, but it's the right one ๐Ÿ’–

Not using any IP Address parameters in the INIT and INIT ACK chunks is a way to make an association more likely to work with Network Address Translation (NAT). NAT stans, rise up ๐Ÿซถโœจ

3.3.2.1.3. Cookie Preservative (9) ๐Ÿ“

okay so bestie, this parameter is how the INIT sender politely asks the receiver to give its State Cookie a longer life span ๐Ÿงš the sender suggests an increment, and the receiver gets the final say ๐Ÿ’…

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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Type = 9Length = 8

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Suggested Cookie Life-Span Increment (msec.)

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Suggested Cookie Life-Span Increment: 32 bits (unsigned integer) โœจ This parameter tells the receiver how many milliseconds the sender would like added to its default cookie life span. Super specific, very chic ๐Ÿ’–

This optional parameter MAY be added to the INIT chunk by the sender when it reattempts establishing an association with a peer to which its previous attempt failed due to a stale cookie operation error. The receiver MAY choose to ignore the suggested cookie life span increase for its own security reasons ๐Ÿ”ฅ the receiver is the boss here, no debate ๐Ÿ‘‘

3.3.2.1.4. Host Name Address (11) ๐Ÿ™ˆ

okay this one is giving "do NOT do this anymore" energy ๐Ÿ’€ The sender of an INIT chunk or INIT ACK chunk MUST NOT include this parameter. The usage of the Host Name Address parameter is deprecated ๐Ÿฅ€ The receiver of an INIT chunk or an INIT ACK containing a Host Name Address parameter MUST send an ABORT chunk and MAY include an "Unresolvable Address" error cause ๐Ÿซถ

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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Type = 11Length

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / Host Name / \ \ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Host Name: variable length ๐ŸŒˆ This field contains a host name in "host name syntax" per Section 2.1 of [RFC1123]. The method for resolving the host name is out of scope of SCTP, so that's not SCTP's problem bestie ๐Ÿฆ‹

At least one null terminator is included in the Host Name string and MUST be included in the length.

3.3.2.1.5. Supported Address Types (12) ๐ŸŽ€

the sender of the INIT chunk uses this parameter to list every address type it can support. Main character energy, all the options on display ๐Ÿ’…โœจ

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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Type = 12Length

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Address Type #1Address Type #2

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

......

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+

Address Type: 16 bits (unsigned integer) ๐Ÿ“ This is filled with the type value of the corresponding address TLV (e.g., 5 for indicating IPv4, and 6 for indicating IPv6). The value indicating the Host Name Address parameter MUST NOT be used when sending this parameter and MUST be ignored when receiving this parameter. Don't send the deprecated one, and if you spot it, just ignore it ๐Ÿ™…โ€โ™€๏ธ๐Ÿ’œ

3.3.3. Initiation Acknowledgement (INIT ACK) (2) ๐Ÿฆ‹

okay bestie, the INIT ACK chunk is the reply that says "yep, got your INIT!" ๐Ÿ’– It acknowledges the start of an SCTP association. Here's the shape:

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 2    |  Chunk Flags  |         Chunk Length          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                         Initiate Tag                          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |               Advertised Receiver Window Credit               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |  Number of Outbound Streams   |   Number of Inbound Streams   |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                          Initial TSN                          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     \                                                               \
     /              Optional/Variable-Length Parameters              /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The parameter part of INIT ACK is set up pretty much like the INIT chunk ๐Ÿ’… Here are the fixed-length parameters for INIT ACK chunks:

             +===================================+===========+
             | Fixed-Length Parameter            | Status    |
             +===================================+===========+
             | Initiate Tag                      | Mandatory |
             +-----------------------------------+-----------+
             | Advertised Receiver Window Credit | Mandatory |
             +-----------------------------------+-----------+
             | Number of Outbound Streams        | Mandatory |
             +-----------------------------------+-----------+
             | Number of Inbound Streams         | Mandatory |
             +-----------------------------------+-----------+
             | Initial TSN                       | Mandatory |
             +-----------------------------------+-----------+

                Table 7: Fixed-Length Parameters of INIT ACK
                                   Chunks

It also brings two extra variable-length parameters to the party ๐ŸŽ€: the State Cookie and the Unrecognized Parameter. Here's the full list:

    +===================================+============+================+
    | Variable-Length Parameter         | Status     | Type Value     |
    +===================================+============+================+
    | State Cookie                      | Mandatory  | 7              |
    +-----------------------------------+------------+----------------+
    | IPv4 Address (Note 1)             | Optional   | 5              |
    +-----------------------------------+------------+----------------+
    | IPv6 Address (Note 1)             | Optional   | 6              |
    +-----------------------------------+------------+----------------+
    | Unrecognized Parameter            | Optional   | 8              |
    +-----------------------------------+------------+----------------+
    | Reserved for ECN Capable (Note 2) | Optional   | 32768 (0x8000) |
    +-----------------------------------+------------+----------------+
    | Host Name Address (Note 3)        | Deprecated | 11             |
    +-----------------------------------+------------+----------------+

           Table 8: Variable-Length Parameters of INIT ACK Chunks

Some notes on those, because they matter ๐Ÿ‘€

Note 1: an INIT ACK chunk can hold any number of IP Address parameters, IPv4 and/or IPv6, in any combination. Mix and match, no judgment ๐ŸŒˆ

Note 2: the ECN Capable field is reserved for future use of Explicit Congestion Notification. So it's just sitting there, waiting for its moment โœจ

Note 3: this one is serious. An INIT ACK chunk MUST NOT contain the Host Name Address parameter. If you're the receiver and you get an INIT ACK chunk that has a Host Name Address parameter, you MUST send an ABORT chunk, and you MAY include an "Unresolvable Address" error cause. Not a vibe ๐Ÿ’€

The Chunk Flags field in INIT ACK chunks is reserved. All of its bits SHOULD be set to 0 by the sender and ignored by the receiver ๐Ÿซถ

Initiate Tag: 32 bits (unsigned integer)

The receiver of the INIT ACK chunk records the value of the Initiate Tag parameter. That value MUST be placed into the Verification Tag field of every SCTP packet the receiver transmits within this association. It's the association's secret handshake ๐Ÿ”

The Initiate Tag MUST NOT take the value 0. For more on how to pick the Initiate Tag value, check out Section 5.3.1.

If an endpoint in the COOKIE-WAIT state receives an INIT ACK chunk with the Initiate Tag set to 0, it MUST destroy the TCB and SHOULD send an ABORT chunk with the T bit set. If an INIT ACK chunk like that shows up in any state other than CLOSED or COOKIE-WAIT, it SHOULD be discarded silently (see Section 5.2.3). Bye bye, no drama ๐Ÿ™…โ€โ™€๏ธ

Advertised Receiver Window Credit (a_rwnd): 32 bits (unsigned integer)

This value is the dedicated buffer space, in number of bytes, that the sender of the INIT ACK chunk has reserved for this association along with this window. It's the room saved just for you, bestie ๐Ÿ›๏ธ

The Advertised Receiver Window Credit MUST NOT be smaller than 1500.

If a receiver gets an INIT ACK chunk with an a_rwnd value smaller than 1500, it MUST discard the packet. It SHOULD send back a packet containing an ABORT chunk, using the Initiate Tag as the Verification Tag, and it MUST NOT change the state of any existing association. Too small, not it ๐Ÿ™…โ€โ™€๏ธ

During the life of the association, this buffer space SHOULD NOT be reduced. In other words, dedicated buffers ought not to get taken away from this association ๐Ÿ˜ค However, an endpoint MAY change the value of the a_rwnd it sends in SACK chunks. Flexibility, but make it respectful.

Number of Outbound Streams (OS): 16 bits (unsigned integer)

This defines the number of outbound streams the sender of this INIT ACK chunk wants to create in this association. The value 0 MUST NOT be used, and the value MUST NOT be greater than the MIS value sent in the INIT chunk. So you can't invite more streams than the other side asked for, lowkey rude ๐Ÿ™ƒ

If an endpoint in the COOKIE-WAIT state receives an INIT ACK chunk with the OS value set to 0, it MUST destroy the TCB and SHOULD send an ABORT chunk. If such an INIT ACK chunk is received in any state other than CLOSED or COOKIE-WAIT, it SHOULD be discarded silently (see Section 5.2.3).

Number of Inbound Streams (MIS): 16 bits (unsigned integer)

This defines the maximum number of streams the sender of this INIT ACK chunk lets the peer create in this association. The value 0 MUST NOT be used.

Note: there's no negotiation of the actual number of streams. Instead, the two endpoints will use the min(requested, offered). See Section 5.1.1 for details. Not a debate, just math ๐Ÿงฎ

If an endpoint in the COOKIE-WAIT state receives an INIT ACK chunk with the MIS value set to 0, it MUST destroy the TCB and SHOULD send an ABORT chunk. If such an INIT ACK chunk is received in any state other than CLOSED or COOKIE-WAIT, it SHOULD be discarded silently (see Section 5.2.3).

Initial TSN (I-TSN): 32 bits (unsigned integer)

This defines the TSN that the sender of the INIT ACK chunk will use initially. The valid range is from 0 to 4294967295, and the Initial TSN SHOULD be set to a random value in that range. The methods described in [RFC4086] can be used for the Initial TSN randomization ๐ŸŽฒ

Implementation Note: an implementation MUST be prepared to receive an INIT ACK chunk that is quite large (more than 1500 bytes). That's because the State Cookie and the address list can be variable in size. For example, if a responder to the INIT chunk has 1000 IPv4 addresses it wants to send, it would need at least 8,000 bytes to encode them in the INIT ACK chunk. That's a lot of addresses, queen ๐Ÿ‘‘

If an INIT ACK chunk arrives with all of the mandatory parameters specified for the INIT ACK chunk, then the receiver SHOULD process it and send back a COOKIE ECHO chunk. The receiver MAY bundle an ERROR chunk with the COOKIE ECHO chunk. However, restrictive implementations MAY send back an ABORT chunk in response to the INIT ACK chunk instead. Some implementations are just picky like that ๐Ÿ™„

Together with the Source Port Number in the SCTP common header, each IP Address parameter in the INIT ACK chunk tells the receiver a valid transport address the sender supports for the whole life of the association being initiated. Basically, a little address book ๐Ÿ“’

If the INIT ACK chunk has at least one IP Address parameter, then the source address of the IP datagram carrying the INIT ACK chunk, plus any additional addresses provided within the INIT ACK chunk, MAY be used as destinations by the receiver. If the INIT ACK chunk has no IP Address parameters, the receiver MUST use the source address associated with the received IP datagram as its sole destination address for the association. No addresses? Then you just use the one you got it from ๐Ÿซ 

The State Cookie and Unrecognized Parameters use the Type-Length-Value format, as defined in Section 3.2.1, and are described below. All the other fields are defined the same way as their counterparts in the INIT chunk. Copy-paste energy, but make it make sense โœจ


3.3.3.1. Optional or Variable-Length Parameters in INIT ACK Chunks ๐ŸŽ€

The State Cookie and Unrecognized Parameters use the Type-Length-Value format, as defined in Section 3.2.1, and are described below. The IPv4 Address parameter is described in Section 3.3.2.1.1, and the IPv6 Address parameter is described in Section 3.3.2.1.2. The Host Name Address parameter is described in Section 3.3.2.1.4, and it MUST NOT be included in an INIT ACK chunk. Any Type-Length-Value fields MUST be placed after the fixed-length fields. (The fixed-length fields are defined in the previous section.) Order matters, babe ๐Ÿ’…

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |           Type = 7            |            Length             |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                            Cookie                             /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Cookie: variable length

This parameter value MUST contain all the necessary state and parameter information the sender of this INIT ACK chunk needs to create the association, along with a Message Authentication Code (MAC). For more on how the State Cookie is defined, see Section 5.1.3. The cookie is the treasure chest ๐Ÿงš๐Ÿ’œ

3.3.3.1.2. Unrecognized Parameter (8) ๐ŸŽ€

okay so this parameter is how the receiver tells the originator of the INIT chunk "hey, you sent me a parameter I don't recognize, and its type says I SHOULD report it back to you" ๐Ÿซถ it's giving honesty, no cap.

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |           Type = 8            |            Length             |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  /                    Unrecognized Parameter                     /
  \                                                               \
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Unrecognized Parameter: variable length ๐Ÿ’œ The Parameter Value field will contain an unrecognized parameter copied from the INIT chunk complete with Parameter Type, Length, and Value fields. Like, the whole thing gets copied back, type and length and value all included ๐Ÿงš


3.3.4. Selective Acknowledgement (SACK) (3) ๐Ÿ’Œ

bestie, this is the chunk that says "got it!" for DATA chunks and also tells the peer where the holes are ๐Ÿ•ณ๏ธ it's literally the most important acknowledgment in the whole protocol, iykyk ๐Ÿ˜ญ

The SACK chunk MUST contain the Cumulative TSN Ack, Advertised Receiver Window Credit (a_rwnd), Number of Gap Ack Blocks, and Number of Duplicate TSNs fields. โœจ

By definition, the Cumulative TSN Ack is the last TSN received before a break in the sequence shows up. The next TSN after that one hasn't been received yet by the endpoint sending the SACK ๐ŸŒธ so this value acknowledges receipt of all TSNs less than or equal to it. Main character energy for the cumulative ack ๐Ÿ‘‘

The way the receiver of the SACK handles a_rwnd is discussed in detail in Section 6.2.1. Go read that one, it's the good stuff ๐Ÿ’…

The SACK chunk also has zero or more Gap Ack Blocks ๐Ÿ“ Each Gap Ack Block acknowledges a subsequence of TSNs that were received after a break. The Gap Ack Blocks SHOULD be isolated, which means the TSN right before each Gap Ack Block and the TSN right after it have NOT been received. And by definition, every TSN acknowledged by a Gap Ack Block is greater than the Cumulative TSN Ack โœจ

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   Type = 3    |  Chunk Flags  |         Chunk Length          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                      Cumulative TSN Ack                       |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |          Advertised Receiver Window Credit (a_rwnd)           |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | Number of Gap Ack Blocks = N  |  Number of Duplicate TSNs = M |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |    Gap Ack Block #1 Start     |     Gap Ack Block #1 End      |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  /                                                               /
  \                              ...                              \
  /                                                               /
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |    Gap Ack Block #N Start     |     Gap Ack Block #N End      |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                        Duplicate TSN 1                        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  /                                                               /
  \                              ...                              \
  /                                                               /
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                        Duplicate TSN M                        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits ๐Ÿฆ‹ All set to 0 on transmit and ignored on receipt.

Cumulative TSN Ack: 32 bits (unsigned integer) ๐ŸŒˆ The largest TSN such that all TSNs smaller than or equal to it have been received and the next one has not. If no DATA chunk has been received yet, this value is set to the peer's Initial TSN minus one ๐Ÿ’–

Advertised Receiver Window Credit (a_rwnd): 32 bits (unsigned integer) ๐ŸชŸ This field shows the updated receive buffer space in bytes of the sender of this SACK chunk. See Section 6.2.1 for the details ๐Ÿ™ˆ

Number of Gap Ack Blocks: 16 bits (unsigned integer) ๐Ÿฌ Tells you how many Gap Ack Blocks are included in this SACK chunk.

Number of Duplicate TSNs: 16 bit ๐Ÿ‘ฏ This is how many duplicate TSNs the endpoint has received. Each duplicate TSN gets listed after the Gap Ack Block list ๐Ÿ’…

Gap Ack Blocks: ๐Ÿงฉ These fields hold the Gap Ack Blocks, repeated once for each block, up to the number given in the Number of Gap Ack Blocks field. All DATA chunks with TSNs greater than or equal to (Cumulative TSN Ack + Gap Ack Block Start) and less than or equal to (Cumulative TSN Ack + Gap Ack Block End) of each Gap Ack Block are assumed to have been received correctly. Lowkey the most satisfying math in the whole spec โœจ

Gap Ack Block Start: 16 bits (unsigned integer) ๐Ÿช„ The Start offset TSN for this Gap Ack Block. To get the actual TSN, you add the Cumulative TSN Ack to this offset. The calculated TSN is the lowest TSN in this Gap Ack Block that has been received ๐Ÿ’ซ

Gap Ack Block End: 16 bits (unsigned integer) ๐ŸŽฏ The End offset TSN for this Gap Ack Block. Same deal, add the Cumulative TSN Ack to this offset, and the result is the highest TSN in this Gap Ack Block that has been received ๐ŸŒŸ

For example, imagine the receiver has the following DATA chunks newly arrived at the moment it decides to send a Selective ACK:

                    ------------
                    | TSN = 17 |
                    ------------
                    |          | <- still missing
                    ------------
                    | TSN = 15 |
                    ------------
                    | TSN = 14 |
                    ------------
                    |          | <- still missing
                    ------------
                    | TSN = 12 |
                    ------------
                    | TSN = 11 |
                    ------------
                    | TSN = 10 |
                    ------------

Then, the parameter part of the SACK chunk MUST be constructed as follows (assuming the new a_rwnd is set to 4660 by the sender): ๐Ÿง

              +-------------------+-------------------+
              |        Cumulative TSN Ack = 12        |
              +-------------------+-------------------+
              |             a_rwnd = 4660             |
              +-------------------+-------------------+
              | num of block = 2  |  num of dup = 0   |
              +-------------------+-------------------+
              |block #1 start = 2 | block #1 end = 3  |
              +-------------------+-------------------+
              |block #2 start = 5 | block #2 end = 5  |
              +-------------------+-------------------+

Duplicate TSN: 32 bits (unsigned integer) ๐Ÿซง Indicates the number of times a TSN was received in duplicate since the last SACK chunk was sent. Every time a receiver gets a duplicate TSN (before sending the SACK chunk), it adds it to the list of duplicates. The duplicate count gets reset to zero after each SACK chunk is sent ๐Ÿ”„

For example, if a receiver gets TSN 19 three times, it would list 19 twice in the outbound SACK chunk. After sending the SACK chunk, if it receives yet one more TSN 19, it would list 19 as a duplicate once in the next outgoing SACK chunk. The math is giving chaos but the logic is so clean ๐ŸคŒ


3.3.5. Heartbeat Request (HEARTBEAT) (4) ๐Ÿ’“

okay bestie, an endpoint SHOULD send a HEARTBEAT (HB) chunk to its peer to probe the reachability of a particular destination transport address defined in the present association ๐Ÿ›ฐ๏ธ it's like a little "u up?" ping but make it protocol ๐Ÿ“ก

The parameter field holds the Heartbeat Information, which is a variable-length opaque data structure understood only by the sender. Only the sender knows what's inside, so it's giving secret diary energy ๐Ÿคซ๐Ÿ“”

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   Type = 4    |  Chunk Flags  |       Heartbeat Length        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  \                                                               \
  /          Heartbeat Information TLV (Variable-Length)          /
  \                                                               \
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits ๐ŸŽ€ Set to 0 on transmit and ignored on receipt.

Heartbeat Length: 16 bits (unsigned integer) ๐Ÿ“ Set to the size of the chunk in bytes, including the chunk header and the Heartbeat Information field.

Heartbeat Information: variable length ๐Ÿงƒ This is defined as a variable-length parameter using the format described in Section 3.2.1, which looks like this:

         +=====================+===========+============+
         | Variable Parameters | Status    | Type Value |
         +=====================+===========+============+
         | Heartbeat Info      | Mandatory | 1          |
         +---------------------+-----------+------------+

              Table 9: Variable-Length Parameters of
                         HEARTBEAT Chunks
     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
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |    Heartbeat Info Type = 1    |        HB Info Length         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    /                Sender-Specific Heartbeat Info                 /
    \                                                               \
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The Sender-Specific Heartbeat Info field SHOULD include information about the sender's current time when this HEARTBEAT chunk is sent, plus the destination transport address this HEARTBEAT chunk is sent to (see Section 8.3). The receiver just reflects this info straight back in the HEARTBEAT ACK chunk (see Section 3.3.6) ๐Ÿชž Also, the HEARTBEAT chunk does double duty, both for reachability checking and for path verification (see Section 5.4) ๐Ÿ‘ฏโ€โ™€๏ธ

When a HEARTBEAT chunk is used for path verification, it MUST include a random nonce of length 64 bits or longer ([RFC4086] provides some information on randomness guidelines). A nonce that's random enough is non-negotiable, bestie ๐Ÿ”โœจ

3.3.6. Heartbeat Acknowledgement (HEARTBEAT ACK) (5) โœจ

okay so an endpoint MUST send this chunk to its peer as a reply to a HEARTBEAT chunk (see Section 8.3) ๐Ÿ’–. A packet carrying the HEARTBEAT ACK chunk always goes back to the source IP address of the IP datagram that had the HEARTBEAT chunk it's answering ๐Ÿฆ‹

The parameter field holds a variable-length opaque data structure ๐ŸŽ€

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 5    |  Chunk Flags  |     Heartbeat Ack Length      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     \                                                               \
     /          Heartbeat Information TLV (Variable-Length)          /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits Set to 0 on transmit and ignored on receipt. ๐ŸŒธ

Heartbeat Ack Length: 16 bits (unsigned integer) Set to the size of the chunk in bytes, including the chunk header and the Heartbeat Information field. ๐Ÿ’…

Heartbeat Information: variable length This field MUST contain the Heartbeat Info parameter (as defined in Section 3.3.5) of the Heartbeat Request to which this Heartbeat Acknowledgement is responding. this part is non-negotiable bestie ๐Ÿซถ

               +=====================+===========+============+
               | Variable Parameters | Status    | Type Value |
               +=====================+===========+============+
               | Heartbeat Info      | Mandatory | 1          |
               +---------------------+-----------+------------+

                   Table 10: Variable-Length Parameters of
                             HEARTBEAT ACK Chunks

3.3.7. Abort Association (ABORT) (6) ๐Ÿ’€

the ABORT chunk gets sent to the peer of an association to close it ๐Ÿ™ˆ. The ABORT chunk MAY contain error causes so the receiver knows why it's being aborted. DATA chunks MUST NOT be bundled with ABORT chunks, no exceptions ๐Ÿšซ. Control chunks (except for INIT, INIT ACK, and SHUTDOWN COMPLETE) MAY be bundled with an ABORT chunk, but they MUST be placed before the ABORT chunk in the SCTP packet, or the receiver just ignores them ๐Ÿ˜ญ

if an endpoint gets an ABORT chunk with a format error or no TCB is found, it MUST silently discard it ๐Ÿคซ. And honestly, no matter what, an endpoint that receives an ABORT chunk MUST NOT respond to it by sending an ABORT chunk of its own. we do not start drama ๐Ÿ’…โœจ

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 6    |  Reserved   |T|            Length             |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     \                                                               \
     /                   zero or more Error Causes                   /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits

Reserved: 7 bits Set to 0 on transmit and ignored on receipt.

T bit: 1 bit The T bit is set to 0 if the sender filled in the Verification Tag expected by the peer. If the Verification Tag is reflected, the T bit MUST be set to 1. Reflecting means that the sent Verification Tag is the same as the received one. reflecting = copying the vibe back exactly ๐Ÿชž

Length: 16 bits (unsigned integer) Set to the size of the chunk in bytes, including the chunk header and all the Error Cause fields present.

See Section 3.3.10 for Error Cause definitions ๐Ÿ“–

Note: Special rules apply to this chunk for verification, so please see Section 8.5.1 for details. don't skip this one, it's giving important โš ๏ธ

3.3.8. Shutdown Association (SHUTDOWN) (7) ๐Ÿ‘‹

An endpoint in an association MUST use this chunk to start a graceful close of the association with its peer. it's the polite goodbye ๐ŸŒˆ This chunk has the following format.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 7    |  Chunk Flags  |          Length = 8           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      Cumulative TSN Ack                       |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits Set to 0 on transmit and ignored on receipt.

Length: 16 bits (unsigned integer) Indicates the length of the parameter. Set to 8.

Cumulative TSN Ack: 32 bits (unsigned integer) The largest TSN, such that all TSNs smaller than or equal to it have been received and the next one has not been received. basically "I've got everything up to here" ๐Ÿงบ

Note: Since the SHUTDOWN chunk does not contain Gap Ack Blocks, it cannot be used to acknowledge TSNs received out of order. In a SACK chunk, lack of Gap Ack Blocks that were previously included indicates that the data receiver reneged on the associated DATA chunks. yikes, reneging is a whole thing ๐Ÿ˜ฌ

Since the SHUTDOWN chunk does not contain Gap Ack Blocks, the receiver of the SHUTDOWN chunk MUST NOT interpret the lack of a Gap Ack Block as a renege. (See Section 6.2 for information on reneging.) no false accusations allowed ๐Ÿ™…โ€โ™€๏ธ

The sender of the SHUTDOWN chunk MAY bundle a SACK chunk to indicate any gaps in the received TSNs ๐ŸŽ

3.3.9. Shutdown Acknowledgement (SHUTDOWN ACK) (8) ๐Ÿ“

This chunk MUST be used to acknowledge the receipt of the SHUTDOWN chunk at the completion of the shutdown process; see Section 9.2 for details. the closing ceremony, basically โœจ

The SHUTDOWN ACK chunk has no parameters.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 8    |  Chunk Flags  |          Length = 4           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits Set to 0 on transmit and ignored on receipt.

3.3.10. Operation Error (ERROR) (9) ๐Ÿšจ

An endpoint sends this chunk to its peer endpoint to notify it of certain error conditions. It contains one or more error causes. An Operation Error is not considered fatal in and of itself, but the corresponding error cause MAY be used with an ABORT chunk to report a fatal condition. so it's a heads-up, not a funeral ๐Ÿชฆ An ERROR chunk has the following format:

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 9    |  Chunk Flags  |            Length             |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     \                                                               \
     /                   one or more Error Causes                    /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits Set to 0 on transmit and ignored on receipt.

Length: 16 bits (unsigned integer) Set to the size of the chunk in bytes, including the chunk header and all the Error Cause fields present.

Error causes are defined as variable-length parameters using the format described in Section 3.2.1, that is:

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |          Cause Code           |         Cause Length          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                  Cause-Specific Information                   /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Cause Code: 16 bits (unsigned integer) Defines the type of error conditions being reported.

           +=======+==============================================+
           | Value | Cause Code                                   |
           +=======+==============================================+
           | 1     | Invalid Stream Identifier                    |
           +-------+----------------------------------------------+
           | 2     | Missing Mandatory Parameter                  |
           +-------+----------------------------------------------+
           | 3     | Stale Cookie                                 |
           +-------+----------------------------------------------+
           | 4     | Out of Resource                              |
           +-------+----------------------------------------------+
           | 5     | Unresolvable Address                         |
           +-------+----------------------------------------------+
           | 6     | Unrecognized Chunk Type                      |
           +-------+----------------------------------------------+
           | 7     | Invalid Mandatory Parameter                  |
           +-------+----------------------------------------------+
           | 8     | Unrecognized Parameters                      |
           +-------+----------------------------------------------+
           | 9     | No User Data                                 |
           +-------+----------------------------------------------+
           | 10    | Cookie Received While Shutting Down          |
           +-------+----------------------------------------------+
           | 11    | Restart of an Association with New Addresses |
           +-------+----------------------------------------------+
           | 12    | User-Initiated Abort                         |
           +-------+----------------------------------------------+
           | 13    | Protocol Violation                           |
           +-------+----------------------------------------------+

                             Table 11: Cause Code

Cause Length: 16 bits (unsigned integer) Set to the size of the parameter in bytes, including the Cause Code, Cause Length, and Cause-Specific Information fields.

Cause-Specific Information: variable length This field carries the details of the error condition. ๐Ÿ”

Sections 3.3.10.1 - 3.3.10.13 define error causes for SCTP. Guidelines for the IETF to define new error cause values are discussed in Section 15.4. โœจ

3.3.10.1. Invalid Stream Identifier (1) ๐ŸŒŠ

Indicates that the endpoint received a DATA chunk sent using a nonexistent stream. someone was texting a stream that doesn't exist ๐Ÿ“ญ

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 1         |       Cause Length = 8        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |       Stream Identifier       |          (Reserved)           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Stream Identifier: 16 bits (unsigned integer) Contains the Stream Identifier of the DATA chunk received in error.

Reserved: 16 bits This field is reserved. It is set to all 0's on transmit and ignored on receipt.

3.3.10.2. Missing Mandatory Parameter (2) ๐Ÿ’–

Okay bestie, this one is for when an INIT or INIT ACK chunk shows up and one or more mandatory TLV parameters are missing ๐Ÿฅบ. The receiver is basically like "wait, where's the thing I needed??" ๐Ÿ’€ This error cause is how it tells the sender which ones went missing.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 2         |   Cause Length = 8 + N * 2    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                 Number of missing params = N                  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |     Missing Param Type #1     |     Missing Param Type #2     |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |    Missing Param Type #N-1    |     Missing Param Type #N     |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Here's the breakdown ๐Ÿง โœจ:

  • Number of Missing params: 32 bits (unsigned integer). This field contains the number of parameters contained in the Cause-Specific Information field.
  • Missing Param Type: 16 bits (unsigned integer). Each field will contain the missing mandatory parameter number.

This one is for when the receiver gets a valid State Cookie, but it has already expired. Valid but expired is so relatable honestly ๐Ÿ˜ญ.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 3         |       Cause Length = 8        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                 Measure of Staleness (usec.)                  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Measure of Staleness: 32 bits (unsigned integer). This field contains the difference, rounded up in microseconds, between the current time and the time the State Cookie expired.

The sender of this error cause MAY choose to report how long past expiration the State Cookie is by including a non-zero value in the Measure of Staleness field. If the sender does not wish to provide the Measure of Staleness, it SHOULD set this field to the value of zero. ๐Ÿซถ


3.3.10.4. Out of Resource (4) ๐Ÿชซ

This means the sender is out of resource. It's usually sent together with, or inside of, an ABORT chunk, so it's giving "I'm tapped out, bye" energy ๐Ÿ’….

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 4         |       Cause Length = 4        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3.3.10.5. Unresolvable Address (5) ๐Ÿ—บ๏ธ

This one means the sender can't resolve the address parameter it was given. Maybe the address type isn't supported by the sender, for example ๐Ÿ™ˆ. It's usually sent together with, or inside of, an ABORT chunk.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 5         |         Cause Length          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                     Unresolvable Address                      /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Unresolvable Address: variable length. The Unresolvable Address field contains the complete Type, Length, and Value of the address parameter (or Host Name parameter) that contains the unresolvable address or host name. ๐Ÿงฉ


3.3.10.6. Unrecognized Chunk Type (6) ๐Ÿคทโ€โ™€๏ธ

This error cause goes back to the originator of the chunk when the receiver doesn't understand it and the upper bits of the 'Chunk Type' are set to 01 or 11. Basically "I don't know this one, babe" ๐Ÿ’€.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 6         |         Cause Length          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                      Unrecognized Chunk                       /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Unrecognized Chunk: variable length. The Unrecognized Chunk field contains the unrecognized chunk from the SCTP packet complete with Chunk Type, Chunk Flags, and Chunk Length. ๐Ÿ“ฆ


3.3.10.7. Invalid Mandatory Parameter (7) ๐Ÿšซ

This error cause is returned to the originator of an INIT or INIT ACK chunk when one of the mandatory parameters is set to an invalid value. Not the vibe ๐Ÿ˜ค.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 7         |       Cause Length = 4        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3.3.10.8. Unrecognized Parameters (8) ๐Ÿง

This one goes back to the originator of the INIT ACK chunk if the receiver doesn't recognize one or more Optional TLV parameters in that INIT ACK chunk. Real ones know what's up ๐Ÿ”.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 8         |         Cause Length          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                    Unrecognized Parameters                    /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Unrecognized Parameters: variable length. The Unrecognized Parameters field contains the unrecognized parameters copied from the INIT ACK chunk complete with TLV. This error cause is normally contained in an ERROR chunk bundled with the COOKIE ECHO chunk when responding to the INIT ACK chunk, when the sender of the COOKIE ECHO chunk wishes to report unrecognized parameters. ๐Ÿ“


3.3.10.9. No User Data (9) ๐Ÿซฅ

This is for when a received DATA chunk shows up with no user data inside. Empty DATA chunk, no cap, that's a problem ๐Ÿ’€.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 9         |       Cause Length = 8        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                              TSN                              |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

TSN: 32 bits (unsigned integer). This parameter contains the TSN of the DATA chunk received with no User Data field. ๐Ÿ”ข

This cause code is normally returned in an ABORT chunk (see Section 6.2). ๐ŸŽ€


A COOKIE ECHO chunk was received while the endpoint was in the SHUTDOWN-ACK-SENT state. Wrong timing, bestie ๐Ÿ˜ฌ. This error is usually returned in an ERROR chunk bundled with the retransmitted SHUTDOWN ACK chunk.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 10        |       Cause Length = 4        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3.3.10.11. Restart of an Association with New Addresses (11) ๐Ÿ”„

This one happens when an INIT chunk is received on an existing association, but the INIT chunk added addresses that weren't previously part of the association. The new addresses get listed in the error cause ๐Ÿ“. It's normally sent as part of an ABORT chunk refusing the INIT chunk (see Section 5.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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 11        |         Cause Length          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                       New Address TLVs                        /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Note: Each New Address TLV is an exact copy of the TLV that was found in the INIT chunk that was new, including the Parameter Type and the Parameter Length. ๐Ÿ“‹


3.3.10.12. User-Initiated Abort (12) ๐Ÿ™‹โ€โ™€๏ธ

This error cause MAY be included in ABORT chunks that are sent because of an upper-layer request. The upper layer can specify an Upper Layer Abort Reason that is transported by SCTP transparently and MAY be delivered to the upper-layer protocol at the peer. So the upper layer gets to say "I'm leaving" and it gets passed along ๐ŸŽคโœจ.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |        Cause Code = 12        |         Cause Length          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                   Upper Layer Abort Reason                    /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3.3.10.13. Protocol Violation (13) ๐Ÿšจ

This error cause MAY be included in ABORT chunks that are sent because an SCTP endpoint detects a protocol violation of the peer that isn't covered by the error causes described in Sections 3.3.10.1 - 3.3.10.12. An implementation MAY provide additional information specifying what kind of protocol violation has been detected. Catch-all for when the other causes don't fit, and it's giving major rule-breaker energy ๐Ÿ”ฅ.

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |         Cause Code = 13         |        Cause Length         |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                    Additional Information                     /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

okay bestie, the COOKIE ECHO chunk only shows up during setup ๐Ÿงš The initiator sends it to its peer to complete initialization, and that's the whole job. This chunk MUST precede any DATA chunk sent within the association but MAY be bundled with one or more DATA chunks in the same packet. โœจ

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 10   |  Chunk Flags  |            Length             |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     /                            Cookie                             /
     \                                                               \
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits. Set to 0 on transmit and ignored on receipt. ๐ŸŒธ

Length: 16 bits (unsigned integer). Set to the size of the chunk in bytes, including the 4 bytes of the chunk header and the size of the cookie. ๐Ÿ’–

Cookie: variable size. This field MUST contain the exact cookie received in the State Cookie parameter from the previous INIT ACK chunk. ๐ŸŽ€

An implementation SHOULD make the cookie as small as possible to ensure interoperability. ๐Ÿ’…

Note: A Cookie Echo does not contain a State Cookie parameter. Instead, the data within the State Cookie's Parameter Value becomes the data within the Cookie Echo's Chunk Value. This allows an implementation to change only the first 2 bytes of the State Cookie parameter to become a COOKIE ECHO chunk. the glow-up trick is literally just swapping two bytes ๐Ÿฆ‹ iconic

This chunk is also only for initialization. It's the reply that says "got your cookie, bestie" ๐Ÿซถ It is used to acknowledge the receipt of a COOKIE ECHO chunk. This chunk MUST precede any DATA or SACK chunk sent within the association but MAY be bundled with one or more DATA chunks or SACK chunk's in the same SCTP packet. ๐Ÿ‘‘

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 11   |  Chunk Flags  |          Length = 4           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Chunk Flags: 8 bits. Set to 0 on transmit and ignored on receipt. ๐Ÿ“

3.3.13. Shutdown Complete (SHUTDOWN COMPLETE) (14) ๐ŸŽ€

this one is the closing ceremony ๐Ÿ‘‘ This chunk MUST be used to acknowledge the receipt of the SHUTDOWN ACK chunk at the completion of the shutdown process; see Section 9.2 for details.

The SHUTDOWN COMPLETE chunk has no parameters. ๐Ÿ™ˆ

      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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Type = 14   |  Reserved   |T|          Length = 4           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • Chunk Flags: 8 bits
    • Reserved: 7 bits
      • Set to 0 on transmit and ignored on receipt.
    • T bit: 1 bit
      • The T bit is set to 0 if the sender filled in the Verification Tag expected by the peer. If the Verification Tag is reflected, the T bit MUST be set to 1. Reflecting means that the sent Verification Tag is the same as the received one. ๐Ÿ”ฅ

Note: Special rules apply to this chunk for verification; please see Section 8.5.1 for details. ๐Ÿ’€

4. SCTP Association State Diagram ๐Ÿฆ‹

During the life time of an SCTP association, the SCTP endpoint's association progresses from one state to another in response to various events. the vibes shift depending on what's happening, so let's look at the plot twists The events that might potentially advance an association's state include:

  • SCTP user primitive calls, e.g., [ASSOCIATE], [SHUTDOWN], or [ABORT],
  • reception of INIT, COOKIE ECHO, ABORT, SHUTDOWN, etc., and control chunks, or
  • some timeout events.

The state diagram in the figures below illustrates state changes, together with the causing events and resulting actions. Note that some of the error conditions are not shown in the state diagram. Full descriptions of all special cases are found in the text. ๐ŸŒธ

Note: Chunk names are given in all capital letters, while parameter names have the first letter capitalized, e.g., COOKIE ECHO chunk type vs. State Cookie parameter. If more than one event/message can occur that causes a state transition, it is labeled (A) or (B). ๐Ÿ’œ

                           -----          -------- (from any state)
                         /       \      /receive ABORT      [ABORT]
           receive INIT |         |    |--------------  or ----------
   ---------------------|         v    v    delete TCB     send ABORT
   generate State Cookie \    +---------+                  delete TCB
           send INIT ACK   ---|  CLOSED |
                              +---------+
                                /      \
                               /        \  [ASSOCIATE]
                              |          |-----------------
                              |          | create TCB
                              |          | send INIT
             receive valid    |          | start T1-init timer
             COOKIE  ECHO     |          v
         (1) -----------------|    +-----------+
             create TCB       |    |COOKIE-WAIT| (2)
             send COOKIE ACK  |    +-----------+
                              |          |
                              |          | receive INIT ACK
                              |          |-------------------
                              |          | send COOKIE ECHO
                              |          | stop T1-init timer
                              |          | start T1-cookie timer
                              |          v
                              |   +-------------+
                              |   |COOKIE-ECHOED| (3)
                              |   +-------------+
                              |          |
                              |          | receive COOKIE ACK
                              |          |-------------------
                              |          | stop T1-cookie timer
                              v          v
                            +---------------+
                            |  ESTABLISHED  |
                            +---------------+
                                    |
                                    |
                           /--------+--------\
       [SHUTDOWN]         /                   \
       -------------------|                   |
       check outstanding  |                   |
       DATA chunks        |                   |
                          v                   |
                 +----------------+           |
                 |SHUTDOWN-PENDING|           | receive SHUTDOWN
                 +----------------+           |------------------
                                              | check outstanding
                          |                   | DATA chunks
   No more outstanding    |                   |
   -----------------------|                   |
   send SHUTDOWN          |                   |
   start T2-shutdown timer|                   |
                          v                   v
                   +-------------+   +-----------------+
               (4) |SHUTDOWN-SENT|   |SHUTDOWN-RECEIVED| (5,6)
                   +-------------+   +-----------------+
                          |  \                |
   receive SHUTDOWN ACK   |   \               |
   -----------------------|    \              |
   stop T2-shutdown timer |     \             |
   send SHUTDOWN COMPLETE |      \            |
   delete TCB             |       \           |
                          |        \          | No more outstanding
                          |         \         |--------------------
                          |          \        | send SHUTDOWN ACK
   receive SHUTDOWN      -|-          \       | start T2-shutdown timer
   --------------------/  | \----------\      |
   send SHUTDOWN ACK      |             \     |
   start T2-shutdown timer|              \    |
                          |               \   |
                          |                |  |
                          |                v  v
                          |          +-----------------+
                          |          |SHUTDOWN-ACK-SENT| (7)
                          |          +-----------------+
                          |                   | (A)
                          |                   |receive SHUTDOWN COMPLETE
                          |                   |-------------------------
                          |                   | stop T2-shutdown timer
                          |                   | delete TCB
                          |                   |
                          |                   | (B)
                          |                   | receive SHUTDOWN ACK
                          |                   |-----------------------
                          |                   | stop T2-shutdown timer
                          |                   | send SHUTDOWN COMPLETE
                          |                   | delete TCB
                          |                   |
                          \    +---------+    /
                           \-->| CLOSED  |<--/
                               +---------+

Figure 3: State Transition Diagram of SCTP ๐Ÿงš

The following applies: these are the big rules, bestie, so pay attention ๐ŸŒˆ

  1. If the State Cookie in the received COOKIE ECHO chunk is invalid (i.e., failed to pass the integrity check), the receiver MUST silently discard the packet. Or, if the received State Cookie is expired (see Section 5.1.5), the receiver MUST send back an ERROR chunk. In either case, the receiver stays in the CLOSED state. ๐Ÿ™ˆ
  2. If the T1-init timer expires, the endpoint MUST retransmit the INIT chunk and restart the T1-init timer. The endpoint stays in the COOKIE-WAIT state. This MUST be repeated up to 'Max.Init.Retransmits' times. After that, the endpoint MUST abort the initialization process and report the error to the SCTP user. โฐ
  3. If the T1-cookie timer expires, the endpoint MUST retransmit COOKIE ECHO chunk and restart the T1-cookie timer. The endpoint stays in the COOKIE-ECHOED state. This MUST be repeated up to 'Max.Init.Retransmits' times. After that, the endpoint MUST abort the initialization process and report the error to the SCTP user. โฐ
  4. In the SHUTDOWN-SENT state, the endpoint MUST acknowledge any received DATA chunks without delay. ๐Ÿ’–
  5. In the SHUTDOWN-RECEIVED state, the endpoint MUST NOT accept any new send requests from its SCTP user. ๐Ÿšซ
  6. In the SHUTDOWN-RECEIVED state, the endpoint MUST transmit or retransmit data and leave this state when all data in queue is transmitted. ๐Ÿ“ค
  7. In the SHUTDOWN-ACK-SENT state, the endpoint MUST NOT accept any new send requests from its SCTP user. ๐Ÿšซ

The CLOSED state is used to indicate that an association is not created (i.e., does not exist). that's the "not me... not even existing" state ๐Ÿ’œ

5. Association Initialization โœจ

Okay bestie, so before any actual data can move from one SCTP endpoint ("A") to another ("Z"), the two of them MUST go through an initialization process to set up an SCTP association between them. ๐ŸŽ€

The SCTP user at an endpoint can use the ASSOCIATE primitive to start an association with another SCTP endpoint. ๐Ÿ’…

Implementation Note: From the SCTP user's point of view, an association might get opened without anyone calling the ASSOCIATE primitive (see Section 11.1.2). That happens when the initiating endpoint sends the first user data to the destination endpoint. The initiating SCTP just assumes default values for every mandatory and optional parameter in the INIT/INIT ACK chunk. It's giving effortless. โœจ

Once the association is established, unidirectional streams are open for data transfer on both ends (see Section 5.1.1). ๐ŸŒˆ

5.1. Normal Establishment of an Association

Here's the handshake, assuming endpoint "A" tries to set up an association with endpoint "Z" and "Z" says yes to the new association. ๐Ÿซถ

A) First, "A" builds a TCB and sends an INIT chunk to "Z". In that INIT chunk, "A" MUST put its Verification Tag (Tag_A) in the Initiate Tag field. Tag_A SHOULD be a random number somewhere in the range of 1 to 4294967295 (see Section 5.3.1 for how Tag values get picked). After sending the INIT chunk, "A" starts the T1-init timer and enters the COOKIE-WAIT state. โฐ

B) "Z" responds right away with an INIT ACK chunk. The destination IP address of that INIT ACK chunk MUST be set to the source IP address of the INIT chunk it's answering. In the response, besides filling in other parameters, "Z" MUST set the Verification Tag field to Tag_A, and it also has to put its own Verification Tag (Tag_Z) in the Initiate Tag field. ๐Ÿฆ‹

Also, "Z" MUST generate a State Cookie and send it along with the INIT ACK chunk. See Section 5.1.3 for how that State Cookie gets generated. ๐Ÿช

Now here's the big one. After sending an INIT ACK chunk with the State Cookie parameter, "Z" MUST NOT allocate any resources or keep any state for the new association. Otherwise, "Z" is vulnerable to resource attacks, and nobody wants that. ๐Ÿšจ

C) When "A" gets the INIT ACK chunk from "Z", it stops the T1-init timer and leaves the COOKIE-WAIT state. Then "A" sends the State Cookie it received in the INIT ACK chunk back inside a COOKIE ECHO chunk, starts the T1-cookie timer, and enters the COOKIE-ECHOED state. ๐Ÿ”ฅ

The COOKIE ECHO chunk MAY be bundled with any pending outbound DATA chunks, but it MUST be the first chunk in the packet. Until the COOKIE ACK chunk comes back, the sender MUST NOT send any other packets to the peer. Hands off until that ack lands! ๐Ÿ™…โ€โ™€๏ธ

D) When endpoint "Z" gets the COOKIE ECHO chunk, it builds a TCB, moves to the ESTABLISHED state, and replies with a COOKIE ACK chunk. A COOKIE ACK chunk MAY be bundled with any pending DATA chunks (and/or SACK chunks), but the COOKIE ACK chunk MUST be the first chunk in the packet. ๐Ÿ‘‘

Implementation Note: An implementation can choose to send the COMMUNICATION UP notification to the SCTP user as soon as it receives a valid COOKIE ECHO chunk. ๐ŸŽ‰

E) When "A" gets the COOKIE ACK chunk, it moves from the COOKIE-ECHOED state to the ESTABLISHED state and stops the T1-cookie timer. It can also tell its ULP about the successful establishment of the association with a COMMUNICATION UP notification (see Section 11). Iconic, we're connected! ๐Ÿ’–

An INIT or INIT ACK chunk MUST NOT be bundled with any other chunk. ๐Ÿ™…โ€โ™€๏ธ They MUST be the only chunks in the SCTP packets that carry them. Solo chunks only, no sharing! ๐Ÿงš

An endpoint MUST send the INIT ACK chunk to the IP address that it got the INIT chunk from. ๐Ÿ“ฌ

The T1-init timer and the T1-cookie timer SHOULD follow the same rules given in Section 6.3. If the application gave multiple IP addresses for the peer, there SHOULD be a T1-init timer and a T1-cookie timer for each of the peer's addresses. Retransmissions of INIT chunks and COOKIE ECHO chunks SHOULD use all of the peer's addresses, the same way retransmissions of DATA chunks do. ๐Ÿ“

If an endpoint gets an INIT, INIT ACK, or COOKIE ECHO chunk but decides NOT to establish the new association (because of missing mandatory parameters in the INIT or INIT ACK chunk, invalid parameter values, or a lack of local resources), it SHOULD respond with an ABORT chunk. It SHOULD also say what caused the abort, like which mandatory parameters were missing, by putting an error cause in the ABORT chunk. The Verification Tag field in the common header of the outgoing SCTP packet that holds the ABORT chunk MUST be set to the Initiate Tag value from the INIT or INIT ACK chunk that this ABORT chunk is replying to. ๐Ÿซ 

Quick note: a COOKIE ECHO chunk that fails the integrity check is NOT counted as an "invalid mandatory parameter." It needs its own special handling, so check Section 5.1.5 for that. ๐Ÿ‘€

After the first DATA chunk shows up in an association, the endpoint MUST immediately respond with a SACK chunk to acknowledge it. Later acknowledgements SHOULD be done as described in Section 6.2. ๐Ÿ’Œ

When the TCB gets created, each endpoint MUST set its internal Cumulative TSN Ack Point to its transmitted Initial TSN minus one. Math with a purpose! ๐Ÿงฎ

Implementation Note: The IP addresses and SCTP port are usually the key used to find a TCB within an SCTP instance. ๐Ÿ”‘

5.1.1. Handle Stream Parameters

In the INIT and INIT ACK chunks, the sender MUST say how many outbound streams (OS) it wants in the association, plus the maximum inbound streams (MIS) it's willing to accept from the other endpoint. Give your numbers, babe! ๐ŸŒธ

After getting the stream configuration from the other side, each endpoint MUST run this check: if the peer's MIS is less than the endpoint's OS (meaning the peer can't support all the outbound streams the endpoint wants to set up), the endpoint MUST use MIS outbound streams. It MAY report any shortage to the upper layer. The upper layer can then decide to abort the association if the resource shortage is unacceptable. Not the vibe, so bye bye association. ๐Ÿ™ƒ

After the association is initialized, the valid outbound stream identifier range for either endpoint MUST be 0 to min(local OS, remote MIS) - 1. ๐Ÿ”ข

5.1.2. Handle Address Parameters

During association initialization, an endpoint follows these rules to discover and collect the destination transport address(es) of its peer. Let's find everyone's address! ๐Ÿ—บ๏ธ

A) If there are no address parameters in the received INIT or INIT ACK chunk, the endpoint MUST take the source IP address the chunk arrived from. It records that address, together with the SCTP Source Port Number, as the only destination transport address for this peer. ๐Ÿ“

B) If there's a Host Name Address parameter in the received INIT or INIT ACK chunk, the endpoint MUST immediately send an ABORT chunk. It MAY include an "Unresolvable Address" error cause for its peer. The ABORT chunk SHOULD go to the source IP address the last peer packet came from. ๐Ÿ’€

C) If there are only IPv4/IPv6 addresses in the received INIT or INIT ACK chunk, the receiver MUST derive and record ALL the transport addresses from the received chunk AND the source IP address that sent the INIT or INIT ACK chunk. The transport addresses are built by combining the SCTP Source Port Number (from the common header), the IP Address parameter(s) in the INIT or INIT ACK chunk, and the source IP address of the IP datagram. After that, the receiver SHOULD use ONLY these transport addresses as destination transport addresses when it sends later packets to its peer. Focus, only these ones! ๐ŸŽฏ

D) An INIT or INIT ACK chunk MUST be treated as belonging to an already established association (or one that's in the middle of being established) if using any of the valid address parameters in the chunk would identify an existing TCB. No double dipping! ๐Ÿ™…โ€โ™€๏ธ

Implementation Note: In some cases (for example, when the implementation doesn't control which source IP address gets used for sending), an endpoint might need to include every possible IP address that packets to the peer could be sent from in its INIT or INIT ACK chunk. ๐ŸŒ

Once all the transport addresses are derived from the INIT or INIT ACK chunk using the rules above, the endpoint picks one of them as the initial primary path. ๐Ÿ‘‘

The packet containing the INIT ACK chunk MUST be sent to the source address of the packet that contained the INIT chunk. ๐Ÿ“ฎ

The sender of INIT chunks MAY include a 'Supported Address Types' parameter in the INIT chunk to say which types of addresses it's cool with. ๐Ÿ’…

Implementation Note: If the receiver of an INIT ACK chunk fails to resolve the address parameter because the type isn't supported, it can abort the initiation process. Then it can try again by sending a new INIT chunk with a 'Supported Address Types' parameter that says which address types it prefers. Second chances are iconic! ๐Ÿ”„

If an SCTP endpoint that only supports IPv4 or IPv6 gets both IPv4 and IPv6 addresses in an INIT or INIT ACK chunk from its peer, it MUST use all the addresses that belong to the family it supports. It MAY ignore the other addresses. The endpoint SHOULD NOT respond with any kind of error indication. Chill, no drama. ๐Ÿซถ

If an SCTP endpoint lists either IPv4 or IPv6 in the 'Supported Address Types' parameter but uses the other family to send the packet containing the INIT chunk, or if it also lists addresses of the other family in the INIT chunk, then the address family that isn't listed in the 'Supported Address Types' parameter SHOULD also be considered supported by the receiver of the INIT chunk. The receiver of the INIT chunk SHOULD NOT respond with any kind of error indication. Mixed signals are still a vibe, so no errors please! ๐ŸŒˆ

Okay so when an INIT ACK chunk is sent back as a reply to an INIT chunk, the sender makes a State Cookie and tucks it into the State Cookie parameter of that INIT ACK ๐ŸŽ€. Inside this cookie the sender MUST include a MAC (see [RFC2104] for an example) so the cookie has integrity protection. The State Cookie SHOULD also carry a timestamp for when it was created and how long it lives, plus everything needed to set up the association, like the port numbers and the Verification Tags ๐Ÿ’œ.

How the MAC gets generated is strictly a private matter for the receiver of the INIT chunk ๐Ÿ”’. The MAC is mandatory because it helps prevent denial-of-service attacks ๐Ÿ˜ญ. Different MAC algorithms can perform differently depending on the platform, and picking a high-performance one makes you more resistant to cookie flooding attacks ๐Ÿ”ฅ. A MAC with acceptable security properties SHOULD be used. The secret key SHOULD be random, and [RFC4086] has some info on randomness guidelines ๐ŸŽฒ. The secret keys also need to be an appropriate size ๐Ÿ“. The secret key SHOULD be changed reasonably frequently (e.g., hourly), and the timestamp in the State Cookie MAY be used to figure out which key was used to verify the MAC โฐ.

If the State Cookie is not encrypted, it MUST NOT contain information that isn't meant to be shared ๐Ÿ™ˆ. And an implementation SHOULD make the cookie as small as possible so everyone stays interoperable ๐Ÿฆ‹.

When an endpoint in the COOKIE-WAIT state gets an INIT ACK chunk with a State Cookie parameter, it MUST immediately send a COOKIE ECHO chunk to its peer with that received State Cookie ๐Ÿ’…. It MAY also tack any pending DATA chunks onto the packet after the COOKIE ECHO chunk โœจ.

The endpoint MUST also start the T1-cookie timer after sending the COOKIE ECHO chunk โฑ๏ธ. If that timer expires, the endpoint MUST retransmit the COOKIE ECHO chunk and restart the T1-cookie timer. This keeps happening until either a COOKIE ACK chunk comes back or 'Max.Init.Retransmits' (see Section 16) is reached. At that point the peer endpoint gets marked unreachable, and the association enters the CLOSED state ๐Ÿ’€.

When an endpoint gets a COOKIE ECHO chunk from another endpoint it has no association with, it does the following ๐Ÿ‘€:

  1. Compute a MAC using the information carried in the State Cookie and the secret key. The timestamp in the State Cookie MAY be used to determine which secret key to use. If secrets are only kept for a limited amount of time and the secret key to use isn't available anymore, the packet containing the COOKIE ECHO chunk MUST be silently discarded. [RFC2104] can be used as a guideline for generating the MAC ๐Ÿงพ.
  2. Authenticate the State Cookie as one it previously generated by comparing the computed MAC against the one carried in the State Cookie. If this comparison fails, the SCTP packet, including the COOKIE ECHO chunk and any DATA chunks, MUST be silently discarded ๐Ÿšซ.
  3. Compare the port numbers and the Verification Tag inside the COOKIE ECHO chunk to the actual port numbers and the Verification Tag in the SCTP common header of the received packet. If these values don't match, the packet MUST be silently discarded ๐Ÿ™…โ€โ™€๏ธ.
  4. Compare the creation timestamp in the State Cookie to the current local time. If the elapsed time is longer than the lifespan carried in the State Cookie, then the packet, including the COOKIE ECHO chunk and any attached DATA chunks, SHOULD be discarded, and the endpoint MUST transmit an ERROR chunk with a "Stale Cookie" error cause to the peer endpoint ๐Ÿฅ€.
  5. If the State Cookie is valid, create an association to the sender of the COOKIE ECHO chunk using the information in the State Cookie carried in that COOKIE ECHO chunk, and enter the ESTABLISHED state ๐Ÿ‘‘.
  6. Send a COOKIE ACK chunk to the peer to acknowledge receipt of the COOKIE ECHO chunk. The COOKIE ACK chunk MAY be bundled with an outbound DATA chunk or SACK chunk; however, the COOKIE ACK chunk MUST be the first chunk in the SCTP packet ๐Ÿฅ‡.
  7. Immediately acknowledge any DATA chunk bundled with the COOKIE ECHO chunk with a SACK chunk (subsequent DATA chunk acknowledgement SHOULD follow the rules defined in Section 6.2). As mentioned in step 6, if the SACK chunk is bundled with the COOKIE ACK chunk, the COOKIE ACK chunk MUST appear first in the SCTP packet ๐Ÿซถ.

If a COOKIE ECHO chunk comes in from an endpoint the receiver already has an existing association with, the procedures in Section 5.2 SHOULD be followed ๐Ÿ”.

5.1.6. An Example of Normal Association Establishment ๐ŸŽฌ

In this example, "A" starts the association and then sends a user message to "Z". After that, "Z" sends two user messages back to "A" (assuming no bundling or fragmentation happens) ๐Ÿ’Œ.

Endpoint A                                          Endpoint Z
{app sets association with Z}
(build TCB)
INIT [I-Tag=Tag_A
      & other info]  ------\
(Start T1-init timer)       \
(Enter COOKIE-WAIT state)    \---> (compose Cookie_Z)
                                /-- INIT ACK [Veri Tag=Tag_A,
                               /             I-Tag=Tag_Z,
(Cancel T1-init timer) <------/              Cookie_Z, & other info]

COOKIE ECHO [Cookie_Z] ------\
(Start T1-cookie timer)       \
(Enter COOKIE-ECHOED state)    \---> (build TCB, enter ESTABLISHED
                                      state)
                               /---- COOKIE ACK
                              /
(Cancel T1-cookie timer, <---/
 enter ESTABLISHED state)
{app sends 1st user data; strm 0}
DATA [TSN=init TSN_A
    Strm=0,Seq=0 & user data]--\
(Start T3-rtx timer)            \
                                 \->
                               /----- SACK [TSN Ack=init TSN_A,
                                            Block=0]
(Cancel T3-rtx timer) <------/
                                      ...
                                     {app sends 2 messages;strm 0}
                               /---- DATA
                              /        [TSN=init TSN_Z,
                          <--/          Strm=0,Seq=0 & user data 1]
SACK [TSN Ack=init TSN_Z,      /---- DATA
      Block=0]     --------\  /        [TSN=init TSN_Z +1,
                            \/          Strm=0,Seq=1 & user data 2]
                     <------/\
                              \
                               \------>

                      Figure 4: A Setup Example

If the T1-init timer expires at "A" after the INIT or COOKIE ECHO chunks are sent, the same INIT or COOKIE ECHO chunk gets retransmitted with the same Initiate Tag (i.e., Tag_A) or State Cookie, and the timer restarts โณ. This repeats 'Max.Init.Retransmits' times before "A" decides "Z" is unreachable and reports the failure to its upper layer. At that point the association enters the CLOSED state ๐Ÿšช.

When retransmitting the INIT chunk, the endpoint MUST follow the rules defined in Section 6.3 to figure out the right timer value โฒ๏ธ.


During the lifetime of an association (in any of its possible states), an endpoint can get one of the setup chunks (INIT, INIT ACK, COOKIE ECHO, or COOKIE ACK) from its peer. The receiver treats that setup chunk as a duplicate and processes it the way this section describes ๐Ÿ”.

Note: An endpoint won't receive the chunk unless it was sent to an SCTP transport address and came from an SCTP transport address associated with this endpoint. So the endpoint processes such a chunk as part of its current association ๐Ÿ’ก.

These scenarios can cause duplicated or unexpected chunks ๐Ÿค”:

  • A) the peer crashed without anyone noticing, restarted itself, and sent a new INIT chunk trying to restore the association ๐Ÿ”„,
  • B) both sides are trying to initialize the association at about the same time ๐ŸฅŠ,
  • C) the chunk comes from a stale packet that was used to set up the present association, or from a past association that doesn't exist anymore ๐Ÿ•ฐ๏ธ,
  • D) the chunk is a fake packet made by an attacker ๐Ÿ˜ˆ, or
  • E) the peer never received the COOKIE ACK chunk and is retransmitting its COOKIE ECHO chunk ๐Ÿ“ฃ.

The rules in the following sections get applied in order to identify and correctly handle these cases ๐Ÿ“‹.

This usually means there's an initialization collision, which is when each endpoint is trying to set up an association with the other one at about the same time ๐Ÿ’ฅ.

When an endpoint in the COOKIE-WAIT state gets an INIT chunk, it MUST respond with an INIT ACK chunk that uses the same parameters it sent in its original INIT chunk (including its Initiate Tag, unchanged). When responding, the following rules MUST be applied ๐Ÿ“:

  1. The packet containing the INIT ACK chunk MUST only be sent to an address passed by the upper layer in the request to initialize the association ๐Ÿ“ฌ.
  2. The packet containing the INIT ACK chunk MUST only be sent to an address reported in the incoming INIT chunk ๐Ÿ“ซ.
  3. The packet containing the INIT ACK chunk SHOULD be sent to the source address of the received packet containing the INIT chunk ๐ŸŽฏ.

When an endpoint in the COOKIE-ECHOED state gets an INIT chunk, it MUST respond with an INIT ACK chunk that uses the same parameters it sent in its original INIT chunk (including its Initiate Tag, unchanged), as long as no new address has been added to the forming association ๐Ÿงฉ. If the INIT chunk says a new address has been added to the association, then the entire INIT chunk MUST be discarded, and the state of the existing association SHOULD NOT be changed. An ABORT chunk SHOULD be sent in a response that MAY include the "Restart of an Association with New Addresses" error cause. The error SHOULD list the addresses that were added to the restarting association ๐Ÿšจ.

When responding in either state (COOKIE-WAIT or COOKIE-ECHOED) with an INIT ACK chunk, the original parameters get combined with the ones from the newly received INIT chunk ๐Ÿน. The endpoint MUST also generate a State Cookie with the INIT ACK chunk, and it uses the parameters sent in its INIT chunk to calculate that State Cookie ๐Ÿช.

After all that, the endpoint MUST NOT change its state, the T1-init timer MUST be left running, and the corresponding TCB MUST NOT be destroyed. The normal procedures for handling State Cookies when a TCB exists will resolve the duplicate INIT chunks into a single association ๐Ÿซง.

For an endpoint in the COOKIE-ECHOED state, it MUST populate its Tie-Tags within both the association TCB and inside the State Cookie (see Section 5.2.2 for a description of the Tie-Tags) ๐Ÿท๏ธ.

Okay bestie, unless the spec says otherwise ๐Ÿซถ, when an unexpected INIT chunk shows up for this association, the endpoint MUST generate an INIT ACK chunk with a State Cookie ๐Ÿ“. Before it replies, the endpoint MUST check whether that unexpected INIT chunk adds new addresses to the association ๐Ÿ‘€.

If new addresses do get added, the endpoint MUST respond with an ABORT chunk ๐Ÿšซ. It copies the Initiate Tag from the unexpected INIT chunk into the Verification Tag of the outbound packet carrying that ABORT chunk. In the ABORT chunk, the error cause MAY be set to Restart of an Association with New Addresses โœจ. The error SHOULD list the addresses that got added to the restarting association ๐Ÿ“‹.

If no new addresses get added, then when the endpoint answers the INIT chunk with the outbound INIT ACK chunk, it MUST copy its current Tie-Tags into a reserved spot inside the State Cookie and the association's TCB ๐ŸŽ€. We call these spots inside the cookie the Peer's-Tie-Tag and the Local-Tie-Tag. The copy inside the association's TCB gets called the Local Tag and Peer's Tag ๐Ÿงš. The outbound SCTP packet holding this INIT ACK chunk MUST carry a Verification Tag equal to the Initiate Tag found in the unexpected INIT chunk. And the INIT ACK chunk MUST contain a brand new Initiate Tag (randomly generated; see Section 5.3.1) ๐ŸŽฒ. Other parameters for the endpoint SHOULD get copied from the association's existing parameters (like the number of outbound streams) into the INIT ACK chunk and the cookie ๐Ÿ’….

After sending the INIT ACK or ABORT chunk, the endpoint MUST take no further actions ๐Ÿ›‘. That means the existing association, its current state, and the matching TCB MUST NOT be changed.

Only when a TCB exists and the association is not in COOKIE-WAIT or SHUTDOWN-ACK-SENT are the Tie-Tags filled with a random value other than 0 ๐ŸŽฐ. For a normal association INIT chunk (aka the endpoint is in the CLOSED state), the Tie-Tags MUST be set to 0, which means no previous TCB existed ๐ŸŒธ.

5.2.3. Unexpected INIT ACK Chunk ๐Ÿ™ˆ

If an INIT ACK chunk gets received by an endpoint in any state other than COOKIE-WAIT or CLOSED, the endpoint SHOULD discard the INIT ACK chunk. An unexpected INIT ACK chunk usually means the endpoint is processing an old or duplicated INIT chunk ๐Ÿ•ฐ๏ธ.

When a COOKIE ECHO chunk gets received by an endpoint in any state for an existing association (aka not in the CLOSED state), these rules apply:

  1. Compute a MAC as described in step 1 of Section 5.1.5.
  2. Authenticate the State Cookie as described in step 2 of Section 5.1.5 (this is case C or D above).
  3. Compare the timestamp in the State Cookie to the current time. If the State Cookie is older than the lifespan carried in the State Cookie and the Verification Tags contained in the State Cookie do not match the current association's Verification Tags, the packet, including the COOKIE ECHO chunk and any DATA chunks, SHOULD be discarded. The endpoint also MUST transmit an ERROR chunk with a Stale Cookie error cause to the peer endpoint (this is case C or D in Section 5.2).

If both Verification Tags in the State Cookie match the Verification Tags of the current association, the State Cookie is valid (this is case E in Section 5.2), even if the lifespan is exceeded ๐Ÿซถ.

  1. If the State Cookie proves to be valid, unpack the TCB into a temporary TCB.
  2. Refer to Table 12 to determine the correct action to be taken.
+===========+============+===============+================+========+
| Local Tag | Peer's Tag | Local-Tie-Tag | Peer's-Tie-Tag | Action |
+===========+============+===============+================+========+
|     X     |     X      |       M       |       M        |  (A)   |
+-----------+------------+---------------+----------------+--------+
|     M     |     X      |       A       |       A        |  (B)   |
+-----------+------------+---------------+----------------+--------+
|     M     |     0      |       A       |       A        |  (B)   |
+-----------+------------+---------------+----------------+--------+
|     X     |     M      |       0       |       0        |  (C)   |
+-----------+------------+---------------+----------------+--------+
|     M     |     M      |       A       |       A        |  (D)   |
+-----------+------------+---------------+----------------+--------+

    Table 12: Handling of a COOKIE ECHO Chunk When a TCB Exists

Legend:

  • X - Tag does not match the existing TCB.
  • M - Tag matches the existing TCB.
  • 0 - Tag unknown (Peer's Tag not known yet / No Tie-Tag in cookie).
  • A - All cases, i.e., M, X, or 0.

For any case not shown in Table 12, the cookie SHOULD be silently discarded ๐Ÿคซ.

Action:

A) In this case, the peer might have restarted. When the endpoint recognizes this potential 'restart', the existing session is treated the same as if it received an ABORT chunk followed by a new COOKIE ECHO chunk, with these exceptions:

  • Any SCTP DATA chunks MAY be retained (this is an implementation-specific option).
  • A RESTART notification SHOULD be sent to the ULP instead of a COMMUNICATION LOST notification.

All the congestion control parameters (e.g., cwnd, ssthresh) related to this peer MUST be reset to their initial values (see Section 6.2.1) ๐Ÿ”„.

After this, the endpoint enters the ESTABLISHED state ๐Ÿ‘‘.

If the endpoint is in the SHUTDOWN-ACK-SENT state and recognizes that the peer has restarted (Action A), it MUST NOT set up a new association. Instead, it resends the SHUTDOWN ACK chunk and sends an ERROR chunk with a Cookie Received While Shutting Down error cause to its peer ๐Ÿ’€.

B) In this case, both sides might be trying to start an association at about the same time, but the peer endpoint sent its INIT chunk after responding to the local endpoint's INIT chunk. So it might have picked a new Verification Tag, not being aware of the previous tag it had sent this endpoint ๐Ÿ™ƒ. The endpoint SHOULD stay in or enter the ESTABLISHED state, but it MUST update its peer's Verification Tag from the State Cookie, stop any T1-init or T1-cookie timers that might be running, and send a COOKIE ACK chunk โœ….

C) In this case, the local endpoint's cookie has arrived late. Before it arrived, the local endpoint sent an INIT chunk, received an INIT ACK chunk, and finally sent a COOKIE ECHO chunk with the peer's same tag but a new tag of its own ๐Ÿข. The cookie SHOULD be silently discarded. The endpoint SHOULD NOT change states and SHOULD leave any timers running โฑ๏ธ.

D) When both local and remote tags match, the endpoint SHOULD enter the ESTABLISHED state if it is in the COOKIE-ECHOED state. It SHOULD stop any T1-cookie timer that is running and send a COOKIE ACK chunk ๐ŸŽ‰.

Note: The "peer's Verification Tag" is the tag received in the Initiate Tag field of the INIT or INIT ACK chunk ๐Ÿ“.

5.2.4.1. An Example of an Association Restart ๐ŸŽฌ

In the following example, "A" starts the association after a restart has happened. Endpoint "Z" didn't know about the restart until this exchange (aka Heartbeats hadn't yet detected the failure of "A") (assuming no bundling or fragmentation occurs) ๐Ÿ‘ป:

Endpoint A                                          Endpoint Z
<-------------- Association is established---------------------->
Tag=Tag_A                                             Tag=Tag_Z
<--------------------------------------------------------------->
{A crashes and restarts}
{app sets up an association with Z}
(build TCB)
INIT [I-Tag=Tag_A'
      & other info]  --------\
(Start T1-init timer)         \
(Enter COOKIE-WAIT state)      \---> (find an existing TCB,
                                      populate TieTags if needed,
                                      compose Cookie_Z with Tie-Tags
                                      and other info)
                                /--- INIT ACK [Veri Tag=Tag_A',
                               /               I-Tag=Tag_Z',
(Cancel T1-init timer) <------/                Cookie_Z]
                                     (leave original TCB in place)
COOKIE ECHO [Veri=Tag_Z',
             Cookie_Z]-------\
(Start T1-init timer)         \
(Enter COOKIE-ECHOED state)    \---> (Find existing association,
                                      Tie-Tags in Cookie_Z match
                                      Tie-Tags in TCB,
                                      Tags do not match, i.e.,
                                      case X X M M above,
                                      Announce Restart to ULP
                                      and reset association).
                                /---- COOKIE ACK
(Cancel T1-init timer, <------/
 Enter ESTABLISHED state)
{app sends 1st user data; strm 0}
DATA [TSN=Initial TSN_A
    Strm=0,Seq=0 & user data]--\
(Start T3-rtx timer)            \
                                 \->
                              /--- SACK [TSN Ack=init TSN_A,Block=0]
(Cancel T3-rtx timer) <------/

                     Figure 5: A Restart Example

At any state other than COOKIE-ECHOED, an endpoint SHOULD silently discard a received COOKIE ACK chunk ๐Ÿ™Š.

Receiving an ERROR chunk with a Stale Cookie error cause tells you that one of a few possible things happened:

A) The association failed to completely set up before the State Cookie issued by the sender was processed.

B) An old State Cookie was processed after setup completed.

C) An old State Cookie came from someone the receiver isn't interested in having an association with, and the ABORT chunk got lost ๐Ÿ˜ฌ.

When processing an ERROR chunk with a Stale Cookie error cause, an endpoint SHOULD first check whether an association is in the process of being set up, aka whether the association is in the COOKIE-ECHOED state ๐Ÿ”. In all cases, if the association is not in the COOKIE-ECHOED state, the ERROR chunk SHOULD be silently discarded.

If the association is in the COOKIE-ECHOED state, the endpoint MAY pick one of these three options:

  1. Send a new INIT chunk to the endpoint to generate a new State Cookie and try the setup procedure again ๐Ÿ”„.
  2. Discard the TCB and report to the upper layer that the association couldn't be set up ๐Ÿ™….
  3. Send a new INIT chunk to the endpoint, adding a Cookie Preservative parameter that requests an extension to the life time of the State Cookie. When calculating the time extension, an implementation SHOULD use the RTT information measured from the previous COOKIE ECHO/ERROR chunk exchange, and SHOULD add no more than 1 second beyond the measured RTT. Long State Cookie life times make the endpoint more subject to a replay attack, so keep it tight ๐Ÿ”โš ๏ธ.

5.3. Other Initialization Issues ๐ŸŒˆ

5.3.1. Selection of Tag Value โœจ

Okay so, Initiate Tag values SHOULD be picked from the range of 1 to 2^32 - 1 ๐Ÿ”ข. It is super important that the Initiate Tag be randomized so off-path attackers can't guess it ๐Ÿ›ก๏ธ. The methods in [RFC4086] can be used for the Initiate Tag randomization. Picking Initiate Tags carefully is also necessary, bestie, so that old duplicate packets from previous associations don't get mistakenly processed as belonging to the current one ๐Ÿ˜ญ.

Moreover, the Verification Tag value used by either endpoint in a given association MUST NOT change during the life time of an association. A new Verification Tag value MUST be used each time the endpoint tears down and then reestablishes an association to the same peer ๐Ÿ’…โœจ.

5.4. Path Verification ๐Ÿ”

During association establishment, the two peers swap a list of addresses ๐Ÿค. In the predominant case, these lists accurately represent the addresses owned by each peer. But a misbehaving peer might supply addresses that it does not own, which is so not it ๐Ÿ™…โ€โ™€๏ธ. To prevent this, the following rules get applied to all addresses of the new association:

  1. Any addresses passed to the sender of the INIT chunk by its upper layer in the request to initialize an association are automatically considered to be CONFIRMED.
  2. For the receiver of the COOKIE ECHO chunk, the only CONFIRMED address is the address to which the packet containing the INIT ACK chunk was sent.
  3. All other addresses not covered by rules 1 and 2 are considered UNCONFIRMED and are subject to probing for verification.

To probe an address for verification, an endpoint will send HEARTBEAT chunks including a 64-bit random nonce and a path indicator (to identify the address that the HEARTBEAT chunk is sent to) within the Heartbeat Info parameter ๐Ÿงช.

Upon receipt of the HEARTBEAT ACK chunk, a verification is made that the nonce included in the Heartbeat Info parameter is the one sent to the address indicated inside the Heartbeat Info parameter. When this match occurs, the address that the original HEARTBEAT was sent to is now considered CONFIRMED and available for normal data transfer ๐ŸŽ‰.

These probing procedures are started when an association moves to the ESTABLISHED state and are ended when all paths are confirmed ๐Ÿ.

In each RTO, a probe MAY be sent on an active UNCONFIRMED path in an attempt to move it to the CONFIRMED state. If during this probing the path becomes inactive, this rate is lowered to the normal HEARTBEAT rate ๐Ÿข. At the expiration of the RTO timer, the error counter of any path that was probed but not CONFIRMED is incremented by one and subjected to path failure detection, as defined in Section 8.2. When probing UNCONFIRMED addresses, however, the association overall error count is not incremented ๐Ÿ™Œ.

The number of packets containing HEARTBEAT chunks sent at each RTO SHOULD be limited by the 'HB.Max.Burst' parameter. It is an implementation decision as to how to distribute packets containing HEARTBEAT chunks to the peer's addresses for path verification ๐Ÿงฉ.

Whenever a path is confirmed, an indication MAY be given to the upper layer ๐Ÿ’ฌ.

An endpoint MUST NOT send any chunks to an UNCONFIRMED address, with the following exceptions:

  • A HEARTBEAT chunk including a nonce MAY be sent to an UNCONFIRMED address.
  • A HEARTBEAT ACK chunk MAY be sent to an UNCONFIRMED address.
  • A COOKIE ACK chunk MAY be sent to an UNCONFIRMED address, but it MUST be bundled with a HEARTBEAT chunk including a nonce. An implementation that does not support bundling MUST NOT send a COOKIE ACK chunk to an UNCONFIRMED address.
  • A COOKIE ECHO chunk MAY be sent to an UNCONFIRMED address, but it MUST be bundled with a HEARTBEAT chunk including a nonce, and the size of the SCTP packet MUST NOT exceed the PMTU. If the implementation does not support bundling or if the bundled COOKIE ECHO chunk plus HEARTBEAT chunk (including nonce) would result in an SCTP packet larger than the PMTU, then the implementation MUST NOT send a COOKIE ECHO chunk to an UNCONFIRMED address ๐Ÿšซ.

6. User Data Transfer ๐ŸŽ€

Data transmission MUST only happen in the ESTABLISHED, SHUTDOWN-PENDING, and SHUTDOWN-RECEIVED states. The only exception to this is that DATA chunks are allowed to be bundled with an outbound COOKIE ECHO chunk when in the COOKIE-WAIT state ๐Ÿ™ˆ.

DATA chunks MUST only be received according to the rules below in ESTABLISHED, SHUTDOWN-PENDING, and SHUTDOWN-SENT states. A DATA chunk received in CLOSED is out of the blue and SHOULD be handled per Section 8.4. A DATA chunk received in any other state SHOULD be discarded ๐Ÿ—‘๏ธ.

A SACK chunk MUST be processed in ESTABLISHED, SHUTDOWN-PENDING, and SHUTDOWN-RECEIVED states. An incoming SACK chunk MAY be processed in COOKIE-ECHOED. A SACK chunk in the CLOSED state is out of the blue and SHOULD be processed according to the rules in Section 8.4. A SACK chunk received in any other state SHOULD be discarded ๐Ÿงƒ.

For transmission efficiency, SCTP defines mechanisms for bundling of small user messages and fragmentation of large user messages ๐Ÿ“ฆ. The following diagram depicts the flow of user messages through SCTP.

In this section, the term "data sender" refers to the endpoint that transmits a DATA chunk, and the term "data receiver" refers to the endpoint that receives a DATA chunk. A data receiver will transmit SACK chunks ๐Ÿ“ฌ.

                  +-------------------------+
                  |      User Messages      |
                  +-------------------------+
        SCTP user        ^  |
       ==================|==|=======================================
                         |  v (1)
              +------------------+    +---------------------+
              | SCTP DATA Chunks |    | SCTP Control Chunks |
              +------------------+    +---------------------+
                         ^  |             ^  |
                         |  v (2)         |  v (2)
                      +--------------------------+
                      |       SCTP packets       |
                      +--------------------------+
        SCTP                      ^  |
       ===========================|==|===========================
                                  |  v
              Connectionless Packet Transfer Service (e.g., IP)

Figure 6: Illustration of User Data Transfer ๐ŸŒˆ

The following applies:

  1. When converting user messages into DATA chunks, an endpoint MUST fragment large user messages into multiple DATA chunks. The size of each DATA chunk SHOULD be smaller than or equal to the Association Maximum DATA Chunk Size (AMDCS). The data receiver will normally reassemble the fragmented message from DATA chunks before delivery to the user (see Section 6.9 for details) ๐Ÿงธ.
  2. Multiple DATA and control chunks MAY be bundled by the sender into a single SCTP packet for transmission, as long as the final size of the SCTP packet does not exceed the current PMTU. The receiver will unbundle the packet back into the original chunks. Control chunks MUST come before DATA chunks in the packet ๐Ÿ“š.
The fragmentation and bundling mechanisms, as detailed in Sections 6.9 and 6.10, are OPTIONAL to implement by the data sender, but they MUST be implemented by the data receiver. In other words, an endpoint MUST properly receive and process bundled or fragmented data ๐Ÿซถ.

6.1. Transmission of DATA Chunks โœจ

okay bestie, this section is where the data sender gets its rules ๐Ÿ’… It spells out how DATA chunks get sent and resent, and it also defines zero window probing. That's the trick that keeps an association from stalling forever when packets carrying SACK chunks (the window updates) go missing ๐Ÿซถ

This document is written as if there's a single retransmission timer per destination transport address, but implementations MAY have a retransmission timer for each DATA chunk ๐Ÿฆ‹

The data sender has to follow these general rules when it transmits or retransmits outbound DATA chunks, and they MUST be applied ๐Ÿ‘‘

  • A) At any given time, the data sender MUST NOT send new data to any destination transport address if its peer's rwnd says the peer has no buffer space (aka rwnd is smaller than the size of the next DATA chunk, see Section 6.2.1) ๐Ÿ™ˆ The only exception is zero window probes.
    • A zero window probe is a DATA chunk sent when the receiver has no buffer space ๐Ÿ“ It lets the sender check for a change in rwnd it missed, because the SACK chunks carrying that update got lost on the way from the data receiver back to the sender.
    • A zero window probe MUST only be sent when the cwnd allows it (see rule B below) ๐Ÿ’–
    • A zero window probe SHOULD only be sent when all outstanding DATA chunks have been cumulatively acknowledged and no DATA chunks are in flight โœจ
    • Senders MUST support zero window probing ๐Ÿ’œ
    • If the sender keeps getting SACK chunks from the peer while it's zero window probing, the unacknowledged window probes SHOULD NOT increment the error counter for the association or any destination transport address ๐Ÿ˜ญ Why? Because the receiver could keep its window closed for an indefinite time, and that shouldn't count against anyone.
    • Section 6.2 describes what the receiver does when it advertises a zero window ๐ŸŒธ
    • The sender SHOULD send the first zero window probe after 1 RTO when it detects that the receiver has closed its window, and SHOULD increase the probe interval exponentially afterwards ๐Ÿ”ฅ
    • Also, the cwnd SHOULD be adjusted according to Section 7.2.1. Zero window probing does not affect the calculation of cwnd ๐Ÿงš
    • The sender MUST also have an algorithm for sending new DATA chunks that avoids silly window syndrome (SWS), as described in [RFC1122] ๐ŸŽ€ The algorithm can be similar to the one in Section 4.2.3.4 of [RFC1122].
  • B) At any given time, the sender MUST NOT send new data to a given transport address if it has cwnd + (PMDCS - 1) or more bytes of data outstanding to that transport address ๐Ÿšซ If data is available, the sender SHOULD exceed cwnd by up to (PMDCS - 1) bytes on a new data transmission if the flightsize doesn't currently reach cwnd ๐ŸŒˆ The breach of cwnd MUST constitute one packet only โญ
  • C) When it's time to transmit, the sender MUST first transmit any DATA chunks marked for retransmission (limited by the current cwnd) before it sends any new DATA chunks ๐Ÿฆ‹
  • D) When it's time to transmit new DATA chunks, the protocol parameter 'Max.Burst' SHOULD be used to limit the number of packets sent ๐Ÿ“ The limit MAY be applied by adjusting cwnd temporarily, like this:
if ((flightsize + Max.Burst * PMDCS) < cwnd)
    cwnd = flightsize + Max.Burst * PMDCS

Or it MAY be applied by strictly limiting the number of packets emitted by the output routine ๐Ÿ’… When you're working out how many packets to send, and especially when using the formula above, cwnd SHOULD NOT be changed permanently ๐Ÿ™ˆ

  • E) After that, the sender can send as many new DATA chunks as rule A and rule B allow ๐ŸŽ€

Multiple DATA chunks committed for transmission MAY be bundled in a single packet โœจ Also, DATA chunks being retransmitted MAY be bundled with new DATA chunks, as long as the resulting SCTP packet size doesn't go over the PMTU ๐Ÿซถ A ULP can ask that no bundling happens, but that only turns off any delays an SCTP implementation might use to boost bundling efficiency ๐Ÿ’– It doesn't stop all bundling by itself, though (like during congestion or retransmission) ๐ŸŒธ

Before an endpoint transmits a DATA chunk, if any received DATA chunks haven't been acknowledged yet (for example, because of delayed ack), the sender SHOULD create a SACK chunk and bundle it with the outbound DATA chunk, as long as the final SCTP packet size doesn't exceed the current PMTU ๐Ÿ‘‘ See Section 6.2 for more.

When the window is full (meaning rule A and/or rule B disallows transmission), the sender MAY still accept send requests from its upper layer ๐Ÿงš But it MUST transmit no more DATA chunks until some or all of the outstanding DATA chunks are acknowledged and rules A and B allow transmission again ๐Ÿ˜ญ

Whenever a transmission or retransmission goes to any address, if the T3-rtx timer for that address isn't running, the sender MUST start that timer ๐Ÿ”ฅ If the timer for that address is already running, the sender MUST restart it if the earliest (aka lowest TSN) outstanding DATA chunk sent to that address is being retransmitted โœจ Otherwise, the data sender MUST NOT restart the timer ๐Ÿ™ˆ

When starting or restarting the T3-rtx timer, the timer value SHOULD be adjusted according to the timer rules in Sections 6.3.2 and 6.3.3 ๐Ÿฆ‹

The data sender MUST NOT use a TSN that is more than 2^31 - 1 above the beginning TSN of the current send window ๐Ÿšซ

For each stream, the data sender MUST NOT have more than 2^16 - 1 ordered user messages in the current send window ๐Ÿ’œ

Whenever the sender of a DATA chunk can benefit from the corresponding SACK chunk coming back without delay, the sender MAY set the I bit in the DATA chunk header ๐ŸŒˆ Heads up: why the sender set the I bit doesn't matter to the receiver ๐ŸŽ€

Reasons for setting the I bit include, but aren't limited to, the following (see Section 4 of [RFC7053] for a discussion of the benefits) ๐Ÿ“

  • The application requests that the I bit of the last DATA chunk of a user message be set when it hands the user message to the SCTP implementation (see Section 11.1) ๐Ÿ’…
  • The sender is in the SHUTDOWN-PENDING state ๐Ÿ‘‘
  • The sending of a DATA chunk fills the congestion or receiver window ๐Ÿงš

6.2. Acknowledgement on Reception of DATA Chunks โœจ

Okay bestie, this is the part where the receiver gets to be the hero ๐Ÿฆ‹ Every SCTP endpoint MUST always acknowledge the reception of each valid DATA chunk, as long as that chunk lands inside its receive window ๐Ÿ’–

Now the window stuff gets spicy ๐ŸŒถ๏ธ When the receiver's advertised window is 0, it MUST drop any new incoming DATA chunk with a TSN larger than the largest TSN received so far. And if the new chunk has a TSN lower than the largest TSN received so far, the receiver SHOULD drop the largest TSN it's holding for reordering and accept the new chunk instead ๐Ÿ”„ Either way, if a chunk gets dropped, the receiver MUST immediately send back a SACK chunk. That SACK shows the current receive window and only the DATA chunks received and accepted so far. The dropped DATA chunk(s) MUST NOT be included in the SACK chunk, because they weren't accepted ๐Ÿ™…โ€โ™€๏ธ The receiver MUST also have an algorithm for advertising its receive window so it avoids receiver silly window syndrome (SWS), as described in [RFC1122]. That algorithm can be similar to the one in Section 4.2.3.3 of [RFC1122] ๐Ÿง 

Next up is the delayed acknowledgement stuff ๐ŸŒธ The guidelines in Section 4.2 of [RFC5681] SHOULD be followed. Specifically, an acknowledgement SHOULD be generated for at least every second packet (not every second DATA chunk) received, and SHOULD be generated within 200 ms of the arrival of any unacknowledged DATA chunk. Sometimes an SCTP transmitter wants to be more careful than these algorithms allow, and that can be a good call ๐Ÿ’… But an SCTP transmitter MUST NOT be more aggressive about sending SACK chunks than the following algorithms allow. No speed-running the ACKs, babe ๐Ÿ™ˆ

An SCTP receiver MUST NOT generate more than one SACK chunk for every incoming packet, except to update the offered window as the receiving application consumes new data โœจ When the window opens up, an SCTP receiver SHOULD send additional SACK chunks to update the window, even if no new data arrived. The receiver MUST avoid sending a large number of window updates, especially big bursts of them ๐Ÿ’€ One way to do this is to send a window update only if the window can grow by at least a quarter of the receive buffer size of the association. That's the kind of restraint I'm obsessed with ๐Ÿซถ

Implementation Note: The maximum delay for generating an acknowledgement MAY be configured by the SCTP administrator, either statically or dynamically, so it can meet the specific timing requirements of the protocol being carried ๐Ÿ•ฐ๏ธ

Okay, now the cap on that delay ๐ŸŽ€ An implementation MUST NOT allow the maximum delay (protocol parameter 'SACK.Delay') to be configured to more than 500 ms. In other words, an implementation MAY lower the value of 'SACK.Delay' below 500 ms, but it MUST NOT raise it above 500 ms. Lower is fine, higher is a no ๐Ÿšซ

Acknowledgements MUST be sent in SACK chunks, unless the ULP requested a shutdown. In that case, an endpoint MAY send an acknowledgement in the SHUTDOWN chunk ๐ŸŒˆ A SACK chunk can acknowledge the reception of multiple DATA chunks at once. See Section 3.3.4 for the SACK chunk format ๐Ÿ“ฆ The SCTP endpoint MUST fill in the Cumulative TSN Ack field to show the latest sequential TSN (of a valid DATA chunk) it has received. Any received DATA chunks with a TSN greater than the value in the Cumulative TSN Ack field get reported in the Gap Ack Block fields. The SCTP endpoint MUST report as many Gap Ack Blocks as can fit in a single SACK chunk, as long as the size of the SCTP packet does not exceed the current PMTU ๐Ÿงฉ

The SHUTDOWN chunk doesn't have Gap Ack Block fields, so the endpoint SHOULD use a SACK chunk instead of the SHUTDOWN chunk to acknowledge DATA chunks received out of order ๐Ÿ™ƒ

Upon receipt of an SCTP packet containing a DATA chunk with the I bit set, the receiver SHOULD NOT delay sending the corresponding SACK chunk. In other words, the receiver SHOULD immediately respond with the corresponding SACK chunk โšก

When a packet arrives with duplicate DATA chunk(s) and no new DATA chunk(s), the endpoint MUST immediately send a SACK chunk with no delay ๐Ÿ˜ญ If a packet arrives with duplicate DATA chunk(s) bundled with new DATA chunks, the endpoint MAY immediately send a SACK chunk. Normally, duplicate DATA chunks show up when the original SACK chunk was lost and the peer's RTO has expired. The duplicate TSN number(s) SHOULD be reported in the SACK chunk as duplicate ๐Ÿ‘€

When an endpoint receives a SACK chunk, it MAY use the duplicate TSN information to figure out whether SACK chunk loss is happening. Further use of this data is for future study ๐Ÿ”ญ

The data receiver is responsible for maintaining its receive buffers ๐Ÿชฃ It SHOULD notify the data sender in a timely manner about changes in its ability to receive data. How an implementation manages its receive buffers depends on lots of factors (for example, operating system, memory management system, amount of memory, and so on). Still, the data sender strategy defined in Section 6.2.1 assumes the receiver works something like the following:

A) At initialization of the association, the endpoint tells the peer how much receive buffer space it has allocated to the association in the INIT or INIT ACK chunk. The endpoint sets a_rwnd to this value ๐ŸŽ€

B) As DATA chunks are received and buffered, decrement a_rwnd by the number of bytes received and buffered. This is, in effect, closing rwnd at the data sender and restricting how much data it can transmit ๐Ÿ”’

C) As DATA chunks are delivered to the ULP and released from the receive buffers, increment a_rwnd by the number of bytes delivered to the upper layer. This is, in effect, opening up rwnd on the data sender and letting it send more data ๐Ÿ”“ The data receiver SHOULD NOT increment a_rwnd unless it has released bytes from its receive buffer. For example, if the receiver is holding fragmented DATA chunks in a reassembly queue, it SHOULD NOT increment a_rwnd ๐Ÿงต

D) When sending a SACK chunk, the data receiver SHOULD place the current value of a_rwnd into the a_rwnd field. The data receiver SHOULD take into account that the data sender will not retransmit DATA chunks that are acked via the Cumulative TSN Ack (i.e., will drop them from its retransmit queue) ๐Ÿ—‘๏ธ


Under certain circumstances, the data receiver MAY drop DATA chunks that it has received but has not released from its receive buffers (i.e., delivered to the ULP). These DATA chunks might have been acked in Gap Ack Blocks. For example, the data receiver might be holding data in its receive buffers while reassembling a fragmented user message from its peer when it runs out of receive buffer space. It MAY drop these DATA chunks even though it has acknowledged them in Gap Ack Blocks. If a data receiver drops DATA chunks, it MUST NOT include them in Gap Ack Blocks in subsequent SACK chunks until they are received again via retransmission. On top of that, the endpoint SHOULD take the dropped data into account when calculating its a_rwnd ๐Ÿงฎ

An endpoint SHOULD NOT revoke a SACK chunk and discard data. Only in extreme circumstances (such as being out of buffer space) might an endpoint use this procedure ๐Ÿšจ The data receiver SHOULD take into account that dropping data that has been acked in Gap Ack Blocks can lead to suboptimal retransmission strategies in the data sender, and therefore to suboptimal performance ๐Ÿ“‰

Here's the delayed acknowledgement example, so you can see how it plays out ๐Ÿ‘‡

   Endpoint A                                      Endpoint Z

   {App sends 3 messages; strm 0}
   DATA [TSN=7,Strm=0,Seq=3] ------------> (ack delayed)
   (Start T3-rtx timer)

   DATA [TSN=8,Strm=0,Seq=4] ------------> (send ack)
                                 /------- SACK [TSN Ack=8,block=0]
   (cancel T3-rtx timer)  <-----/

   DATA [TSN=9,Strm=0,Seq=5] ------------> (ack delayed)
   (Start T3-rtx timer)
                                          ...
                                          {App sends 1 message; strm 1}
                                          (bundle SACK with DATA)
                                   /----- SACK [TSN Ack=9,block=0] \
                                  /         DATA [TSN=6,Strm=1,Seq=2]
   (cancel T3-rtx timer)  <------/        (Start T3-rtx timer)

   (ack delayed)
   (send ack)
   SACK [TSN Ack=6,block=0] -------------> (cancel T3-rtx timer)

Figure 7: Delayed Acknowledgement Example

If an endpoint receives a DATA chunk with no user data (i.e., the Length field is set to 16), it SHOULD send an ABORT chunk with a "No User Data" error cause. Yikes, empty DATA chunks are not the vibe ๐Ÿ˜ฌ

An endpoint SHOULD NOT send a DATA chunk with no user data part ๐Ÿ™…โ€โ™€๏ธ This avoids the need to be able to return a zero-length user message in the API, especially in the socket API as specified in [RFC6458] for details. Zero-length messages are a whole mess, so just don't ๐Ÿงš

6.2.1. Processing a Received SACK Chunk โœจ

Okay so every SACK chunk an endpoint gets carries an a_rwnd value ๐Ÿซถ That's how much buffer space the data receiver had left when it sent that SACK, out of its total receive buffer (the one set up in the INIT/INIT ACK chunk) ๐Ÿ’… Put a_rwnd together with the Cumulative TSN Ack and the Gap Ack Blocks and the data sender can build a picture of the peer's receive buffer space ๐Ÿฆ‹

Here's the drama though ๐Ÿ˜ญ SACK chunks can arrive out of order. A SACK the receiver sent later can pass an earlier one and show up first. If that happens, the sender can end up with a totally wrong view of the peer's buffer space ๐Ÿ’€

There's no explicit identifier that lets you spot an out-of-order SACK, so the sender uses heuristics to figure out whether a SACK is new ๐Ÿงš

An endpoint SHOULD use the following rules to calculate the rwnd, using the a_rwnd value, the Cumulative TSN Ack, and Gap Ack Blocks in a received SACK chunk.

  • A) At association setup, the endpoint sets rwnd to the Advertised Receiver Window Credit (a_rwnd) the peer gave in the INIT or INIT ACK chunk.
  • B) Any time a DATA chunk is sent (or resent) to a peer, the endpoint subtracts that chunk's data size from that peer's rwnd.
  • C) Any time a DATA chunk gets marked for retransmission, either because the T3-rtx timer expired (Section 6.3.3) or via Fast Retransmit (Section 7.2.4), the endpoint adds those chunks' data sizes back to rwnd.
  • D) Any time a SACK chunk arrives, the endpoint does the following:
    • i) If Cumulative TSN Ack is less than the Cumulative TSN Ack Point, drop the SACK chunk. Cumulative TSN Ack only ever goes up, so a SACK whose Cumulative TSN Ack is below the Cumulative TSN Ack Point is an out-of-order SACK, and that's the whole tea โ˜•
    • ii) Set rwnd equal to the newly received a_rwnd minus the number of bytes still outstanding after processing the Cumulative TSN Ack and the Gap Ack Blocks.
    • iii) If the SACK chunk is missing a TSN that was previously acknowledged via a Gap Ack Block (for example, the data receiver reneged on the data), then consider the matching DATA as possibly missing. Count one miss indication toward Fast Retransmit as described in Section 7.2.4. If no retransmit timer is running for the destination address the DATA chunk was originally sent to, start T3-rtx for that destination address.
    • iv) If the Cumulative TSN Ack matches or exceeds the Fast Recovery exit point (Section 7.2.4), Fast Recovery is exited ๐ŸŽ‰

6.3. Management of Retransmission Timer

An SCTP endpoint uses a retransmission timer T3-rtx to make sure data gets delivered even when there's zero feedback from its peer ๐ŸŒธ The length of this timer is called the RTO (retransmission timeout).

When an endpoint's peer is multi-homed, the endpoint calculates a separate RTO for each different destination transport address of that peer endpoint ๐Ÿ—บ๏ธ

SCTP's RTO management basically follows how TCP handles its retransmission timer, so it's giving dรฉjร  vu for anyone who knows TCP ๐Ÿ™ˆ To compute the current RTO, an endpoint keeps two state variables per destination transport address: SRTT (smoothed round-trip time) and RTTVAR (round-trip time variation).

6.3.1. RTO Calculation

Here's how SRTT, RTTVAR, and RTO get computed, bestie ๐Ÿงฎ

C1) Until an RTT measurement has been made for a packet sent to the given destination transport address, set RTO to the protocol parameter RTO.Initial.

C2) When the first RTT measurement R is made, perform:

SRTT = R
RTTVAR = R/2
RTO = SRTT + 4 * RTTVAR

C3) When a new RTT measurement R' is made, perform:

RTTVAR = (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'|
SRTT = (1 - RTO.Alpha) * SRTT + RTO.Alpha * R'

Note: The value of SRTT used in the update to RTTVAR is its value before updating SRTT itself using the second assignment. ๐Ÿ‘€

After the computation, update:

RTO = SRTT + 4 * RTTVAR

C4) When data is in flight and when rule C5 below allows it, a new RTT measurement MUST be made each round trip. Furthermore, new RTT measurements SHOULD be made no more than once per round trip for a given destination transport address. There are two reasons for this recommendation. First, measuring more often doesn't really give you any significant benefit in practice [ALLMAN99]. Second, if measurements are made more often, then the values of RTO.Alpha and RTO.Beta in rule C3 above SHOULD be adjusted so that SRTT and RTTVAR still adjust to changes at roughly the same rate (in terms of how many round trips it takes them to reflect new values) as they would if making only one measurement per round trip and using RTO.Alpha and RTO.Beta as given in rule C3. Still, the exact nature of those adjustments is a research issue ๐Ÿ”ฌ

C5) Karn's algorithm: RTT measurements MUST NOT be made using chunks that were retransmitted, since it's ambiguous whether the reply was for the first instance of the chunk or for a later one ๐Ÿ™ƒ

RTT measurements SHOULD only be made using a DATA chunk with TSN r if no DATA chunk with TSN less than or equal to r was retransmitted since the DATA chunk with TSN r was first sent.

C6) Whenever RTO is computed, if it is less than RTO.Min seconds, then it gets rounded up to RTO.Min seconds. The reason for this rule is that RTOs without a high enough minimum are susceptible to unnecessary timeouts [ALLMAN99] ๐Ÿ˜ค

C7) A maximum value MAY be placed on RTO, provided it is at least RTO.Max seconds.

There's no requirement for the clock granularity G used for computing RTT measurements and the different state variables, other than:

G1) Whenever RTTVAR is computed, if RTTVAR == 0, then adjust RTTVAR = G.

Experience [ALLMAN99] has shown that finer clock granularities (less than 100 msec) perform somewhat better than coarser ones โฑ๏ธ

See Section 16 for suggested parameter values ๐Ÿ“

6.3.2. Retransmission Timer Rules

The rules for managing the retransmission timer are as follows:

R1) Every time a DATA chunk is sent to any address (including a retransmission), if the T3-rtx timer of that address is not running, start it so that it expires after the RTO of that address. The RTO used here is the one obtained after any doubling from previous T3-rtx timer expirations on the corresponding destination address, as discussed in rule E2 below.

R2) Whenever all outstanding data sent to an address has been acknowledged, turn off the T3-rtx timer of that address.

R3) Whenever a SACK chunk is received that acknowledges the DATA chunk with the earliest outstanding TSN for that address, restart the T3-rtx timer for that address with its current RTO (if there's still outstanding data on that address).

R4) Whenever a SACK chunk is received missing a TSN that was previously acknowledged via a Gap Ack Block, start the T3-rtx for the destination address the DATA chunk was originally sent to, if it isn't already running.

Here's an example showing the timer rules in action (assuming the receiver uses delayed acks) ๐ŸŽฌ

   Endpoint A                                         Endpoint Z
   {App begins to send}
   Data [TSN=7,Strm=0,Seq=3] ------------> (ack delayed)
   (Start T3-rtx timer)
                                           {App sends 1 message; strm 1}
                                           (bundle ack with data)
   DATA [TSN=8,Strm=0,Seq=4] ----\     /-- SACK [TSN Ack=7,Block=0]
                                  \   /      DATA [TSN=6,Strm=1,Seq=2]
                                   \ /     (Start T3-rtx timer)
                                    \
                                   / \
   (Restart T3-rtx timer)  <------/   \--> (ack delayed)
   (ack delayed)
   {send ack}
   SACK [TSN Ack=6,Block=0] --------------> (Cancel T3-rtx timer)
                                           ..
                                           (send ack)
   (Cancel T3-rtx timer)  <-------------- SACK [TSN Ack=8,Block=0]

                       Figure 8: Timer Rule Examples

6.3.3. Handle T3-rtx Expiration

Whenever the retransmission timer T3-rtx expires for a destination address, do the following ๐Ÿ’ฅ

E1) For the destination address whose timer expired, adjust its ssthresh using the rules in Section 7.2.3 and set cwnd = PMDCS.

E2) For the destination address whose timer expired, set RTO = RTO * 2 ("back off the timer"). The maximum value from rule C7 above (RTO.Max) MAY be used to provide an upper bound to this doubling operation.

E3) Figure out how many of the earliest (i.e., lowest TSN) outstanding DATA chunks for the address whose T3-rtx expired will fit into a single SCTP packet, subject to the PMTU corresponding to the destination transport address the retransmission is being sent to. This might be different from the address whose timer expired; see Section 6.4. Call this value K. Bundle and retransmit those K DATA chunks in a single packet to the destination endpoint ๐Ÿ“ฆ

E4) Start the retransmission timer T3-rtx on the destination address the retransmission is sent to, if rule R1 above says to do so. The RTO used for starting T3-rtx SHOULD be the one for the destination address the retransmission is sent to. When the receiver is multi-homed, that might be different from the destination address whose timer expired (see Section 6.4 below).

After retransmitting, once a new RTT measurement is obtained, the computation in rule C3 gets performed, including the computation of RTO. That measurement can only happen when new data has been sent and acknowledged (per rule C5), or for a measurement made from a HEARTBEAT chunk (see Section 8.3). This might end up "collapsing" RTO back down after it was doubled (rule E2) ๐Ÿ“‰

Any DATA chunks that were sent to the address whose T3-rtx timer expired, but didn't fit in an SCTP packet of size smaller than or equal to the PMTU (rule E3 above), SHOULD be marked for retransmission and sent as soon as cwnd allows (normally, when a SACK chunk arrives) ๐Ÿƒโ€โ™€๏ธ

The final rule for managing the retransmission timer is about failover (see Section 6.4.1) ๐Ÿฆ‹

F1) Whenever an endpoint switches from the current destination transport address to a different one, the current retransmission timers are left running. As soon as the endpoint transmits a packet containing DATA chunk(s) to the new transport address, start the timer on that transport address, using the RTO value of the destination address the data is being sent to, if rule R1 indicates to do so. ๐Ÿ’œ

6.4. Multi-Homed SCTP Endpoints ๐Ÿฆ‹

okay so bestie, an SCTP endpoint is considered multi-homed if there is more than one transport address that can be used as a destination address to reach that endpoint. like, it has multiple doors into the house ๐Ÿ โœจ

moreover, the ULP of an endpoint selects one of the multiple destination addresses of a multi-homed peer endpoint as the primary path (see Sections 5.1.2 and 11.1 for details). the primary path is the main character ๐Ÿ‘‘

by default, an endpoint SHOULD always transmit to the primary path, unless the SCTP user explicitly specifies the destination transport address (and possibly source transport address) to use. so the default is giving main path, periodt ๐Ÿ’…

an endpoint SHOULD transmit reply chunks (e.g., INIT ACK, COOKIE ACK, and HEARTBEAT ACK) in response to control chunks to the same destination transport address from which it received the control chunk to which it is replying. so when someone texts you, you reply to the same number they texted from ๐Ÿ“ฑ๐Ÿซถ

the selection of the destination transport address for packets containing SACK chunks is implementation dependent. however, an endpoint SHOULD NOT vary the destination transport address of a SACK chunk when it receives DATA chunks coming from the same source address. so basically don't randomly switch your reply address when the texts are coming from the same sender ๐Ÿ™ˆ

when acknowledging multiple DATA chunks received in packets from different source addresses in a single SACK chunk, the SACK chunk MAY be transmitted to one of the destination transport addresses from which the DATA or control chunks being acknowledged were received. you get to pick any of the numbers that actually sent you stuff ๐ŸŒธ

when a receiver of a duplicate DATA chunk sends a SACK chunk to a multi-homed endpoint, it MAY be beneficial to vary the destination address and not use the source address of the DATA chunk. the reason is that receiving a duplicate from a multi-homed endpoint might indicate that the return path (as specified in the source address of the DATA chunk) for the SACK chunk is broken. ngl this one is giving detective energy ๐Ÿ•ต๏ธโ€โ™€๏ธ๐Ÿ’œ

furthermore, when its peer is multi-homed, an endpoint SHOULD try to retransmit a chunk that timed out to an active destination transport address that is different from the last destination address to which the chunk was sent. so if it got lost on one door, try a different door that's still open ๐Ÿšช๐Ÿฆ‹

when its peer is multi-homed, an endpoint SHOULD send fast retransmissions to the same destination transport address to which the original data was sent. if the primary path has been changed and the original data was sent to the old primary path before the Fast Retransmit, the implementation MAY send it to the new primary path. fast retransmits stay on the same door, unless the main path switched, then you have options โœจ

retransmissions do not affect the total outstanding data count. however, if the DATA chunk is retransmitted onto a different destination address, both the outstanding data counts on the new destination address and the old destination address to which the data chunk was last sent is adjusted accordingly. the math gets updated on both doors so nobody's count is wrong ๐Ÿงฎ๐Ÿ’–

6.4.1. Failover from an Inactive Destination Address ๐Ÿงš

okay this part is so iconic. some of the transport addresses of a multi-homed SCTP endpoint might become inactive due to either the occurrence of certain error conditions (see Section 8.2) or adjustments from the SCTP user. doors can close, periodt ๐Ÿ˜ญ

when there is outbound data to send and the primary path becomes inactive (e.g., due to failures) or where the SCTP user explicitly requests to send data to an inactive destination transport address before reporting an error to its ULP, the SCTP endpoint SHOULD try to send the data to an alternate active destination transport address if one exists. so if the main door is shut, the endpoint should try the next open door first before it tells the app something went wrong ๐Ÿซถ๐ŸŒˆ

when retransmitting data that timed out, if the endpoint is multi-homed, it needs to consider each source-destination address pair in its retransmission selection policy. when retransmitting timed-out data, the endpoint SHOULD attempt to pick the most divergent source-destination pair from the original source-destination pair to which the packet was transmitted. so pick a route that's really different from the one that failed, not just a tiny tweak ๐Ÿฆ‹โœจ

Note: Rules for picking the most divergent source-destination pair are an implementation decision and are not specified within this document. so the RFC is like "figure that part out yourself" ๐Ÿ™ƒ

6.5. Stream Identifier and Stream Sequence Number ๐ŸŽ€

okay every DATA chunk MUST carry a valid stream identifier. if an endpoint receives a DATA chunk with an invalid stream identifier, it SHOULD acknowledge the reception of the DATA chunk following the normal procedure, immediately send an ERROR chunk with cause set to "Invalid Stream Identifier" (see Section 3.3.10), and discard the DATA chunk. the endpoint MAY bundle the ERROR chunk and the SACK chunk in the same packet. so you still say "got it" to the message, tell them the stream number is wrong, and toss the message ๐Ÿ’…

the Stream Sequence Number in all the outgoing streams MUST start from 0 when the association is established. the Stream Sequence Number of an outgoing stream MUST be incremented by 1 for each ordered user message sent on that outgoing stream. in particular, when the Stream Sequence Number reaches the value 65535, the next Stream Sequence Number MUST be set to 0. for unordered user messages, the Stream Sequence Number MUST NOT be changed. every stream keeps its own little counter, starting at 0, going up with each ordered message, and wrapping back to 0 after 65535 ๐Ÿ”ข๐ŸŒธ

6.6. Ordered and Unordered Delivery ๐ŸŒˆ

within a stream, an endpoint MUST deliver DATA chunks received with the U flag set to 0 to the upper layer according to the order of their Stream Sequence Number. if DATA chunks arrive out of order of their Stream Sequence Number, the endpoint MUST hold the received DATA chunks from delivery to the ULP until they are reordered. so the ordered messages have to line up in the right order before anyone sees them, no cap ๐Ÿ“š๐Ÿ’œ

however, an SCTP endpoint can indicate that no ordered delivery is required for a particular DATA chunk transmitted within the stream by setting the U flag of the DATA chunk to 1. so you can flip the U flag to say "this one doesn't need to wait in line" ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ

when an endpoint receives a DATA chunk with the U flag set to 1, it bypasses the ordering mechanism and immediately deliver the data to the upper layer (after reassembly if the user data is fragmented by the data sender). the U flag ones skip the line and go straight to the app, after being put back together if they were chopped up ๐Ÿฆ‹

this provides an effective way of transmitting "out-of-band" data in a given stream. also, a stream can be used as an "unordered" stream by simply setting the U flag to 1 in all DATA chunks sent through that stream. so you can use a stream as a no-line-zone the whole time, which is so fun ๐ŸŽ‰โœจ

Implementation Note: When sending an unordered DATA chunk, an implementation MAY choose to place the DATA chunk in an outbound packet that is at the head of the outbound transmission queue if possible. basically you can cut to the front of the queue if it's doable ๐Ÿ’…

the 'Stream Sequence Number' field in a DATA chunk with U flag set to 1 has no significance. the sender can fill the 'Stream Sequence Number' with arbitrary value, but the receiver MUST ignore the field. so for unordered chunks that field is just vibes, and the receiver has to ignore it ๐Ÿ™ˆ

Note: When transmitting ordered and unordered data, an endpoint does not increment its Stream Sequence Number when transmitting a DATA chunk with U flag set to 1. the counter only ticks up for the ordered ones, bestie ๐Ÿ”ข๐Ÿ’–

6.7. Report Gaps in Received DATA TSNs ๐Ÿ•ต๏ธโ€โ™€๏ธ

upon the reception of a new DATA chunk, an endpoint examines the continuity of the TSNs received. if the endpoint detects a gap in the received DATA chunk sequence, it SHOULD send a SACK chunk with Gap Ack Blocks immediately. the data receiver continues sending a SACK chunk after receipt of each SCTP packet that does not fill the gap. so when there's a hole in the sequence, you shout about it right away with a SACK and keep shouting until the hole gets filled ๐Ÿ“ฃ๐Ÿ˜ญ

based on the Gap Ack Block from the received SACK chunk, the endpoint can calculate the missing DATA chunks and make decisions on whether to retransmit them (see Section 6.2.1 for details). the gap info is how you figure out what's missing and whether to resend it ๐Ÿง 

multiple gaps can be reported in one single SACK chunk (see Section 3.3.4). one SACK can carry a whole list of holes, iykyk ๐ŸŒธ

when its peer is multi-homed, the SCTP endpoint SHOULD always try to send the SACK chunk to the same destination address from which the last DATA chunk was received. so reply to the door the most recent DATA came through ๐Ÿšช๐Ÿ’œ

upon the reception of a SACK chunk, the endpoint MUST remove all DATA chunks that have been acknowledged by the SACK chunk's Cumulative TSN Ack from its transmit queue. all DATA chunks with TSNs not included in the Gap Ack Blocks that are smaller than the highest-acknowledged TSN reported in the SACK chunk MUST be treated as "missing" by the sending endpoint. the number of "missing" reports for each outstanding DATA chunk MUST be recorded by the data sender to make retransmission decisions. see Section 7.2.4 for details. so when a SACK comes in, the sender cleans up everything that's been confirmed and counts the missing reports for the rest ๐Ÿ“Š๐ŸŽ€

the following example shows the use of SACK chunk to report a gap. ๐Ÿ‘€

  Endpoint A                                    Endpoint Z
  {App sends 3 messages; strm 0}
  DATA [TSN=6,Strm=0,Seq=2] ---------------> (ack delayed)
  (Start T3-rtx timer)

  DATA [TSN=7,Strm=0,Seq=3] --------> X (lost)

  DATA [TSN=8,Strm=0,Seq=4] ---------------> (gap detected,
                                              immediately send ack)
                                  /----- SACK [TSN Ack=6,Block=1,
                                 /             Start=2,End=2]
                          <-----/
  (remove 6 from out-queue,
   and mark 7 as "1" missing report)

              Figure 9: Reporting a Gap Using SACK Chunk

the maximum number of Gap Ack Blocks that can be reported within a single SACK chunk is limited by the current PMTU. when a single SACK chunk cannot cover all the Gap Ack Blocks needed to be reported due to the PMTU limitation, the endpoint MUST send only one SACK chunk. this single SACK chunk MUST report the Gap Ack Blocks from the lowest to highest TSNs, within the size limit set by the PMTU, and leave the remaining highest TSN numbers unacknowledged. so if the PMTU is too small for all the gap blocks, you send one SACK with the lowest ones that fit and leave the top ones unacked ๐Ÿ“ฆ๐ŸŒˆ

6.8. CRC32c Checksum Calculation ๐Ÿ’–

when sending an SCTP packet, the endpoint MUST strengthen the data integrity of the transmission by including the CRC32c checksum value calculated on the packet, as described below. this is your bodyguard for the packet, no cap ๐Ÿ›ก๏ธโœจ

after the packet is constructed (containing the SCTP common header and one or more control or DATA chunks), the transmitter MUST:

  1. fill in the proper Verification Tag in the SCTP common header and initialize the checksum field to 0,
  2. calculate the CRC32c checksum of the whole packet, including the SCTP common header and all the chunks (refer to Appendix A for details of the CRC32c algorithm), and
  3. put the resultant value into the checksum field in the common header and leave the rest of the bits unchanged.

so the steps are: set the Verification Tag, zero the checksum, run the math over everything, then drop the answer into the checksum slot ๐Ÿงฎ๐ŸŽ€

when an SCTP packet is received, the receiver MUST first check the CRC32c checksum as follows:

  1. store the received CRC32c checksum value aside.
  2. replace the 32 bits of the checksum field in the received SCTP packet with 0 and calculate a CRC32c checksum value of the whole received packet.
  3. verify that the calculated CRC32c checksum is the same as the received CRC32c checksum. if it is not, the receiver MUST treat the packet as an invalid SCTP packet.

the receiver checks the bodyguard first, the way you'd check a text's vibe before replying ๐Ÿ‘€๐Ÿ’œ

the default procedure for handling invalid SCTP packets is to silently discard them. so if the checksum doesn't match, it just gets ghosted ๐Ÿ‘ป๐Ÿ’…

any hardware implementation SHOULD permit alternative verification of the CRC in software. so hardware should let you double-check the CRC in software too, just in case ๐ŸŒธ๐Ÿซถ

6.9. Fragmentation and Reassembly โœจ

okay bestie, so fragmentation is basically how SCTP handles a user message that's too big to fit in one packet ๐ŸŽ€ an endpoint MAY support fragmentation when sending DATA chunks, but it MUST support reassembly when receiving DATA chunks ๐Ÿ’– like, you can choose whether you slice stuff up on the way out, but you have to be able to stitch it back together when it comes in ๐Ÿงต

if your endpoint DOES support fragmentation, it MUST fragment a user message whenever the outbound SCTP packet size would go over the current PMTU ๐ŸŒธ and if your endpoint does NOT support fragmentation and somebody asks it to send a message that would blow past the current PMTU, it MUST return an error to its upper layer and MUST NOT attempt to send the user message ๐Ÿ˜ญ no sneaking it through, periodt ๐Ÿ’…

An endpoint MAY support fragmentation when sending DATA chunks, but
it MUST support reassembly when receiving DATA chunks.

An SCTP implementation MAY give the upper layer a way to turn fragmentation of DATA chunks off ๐Ÿฆ‹ when that's disabled, the SCTP implementation MUST act exactly like one that doesn't support fragmentation at all, which means it rejects any call that would send SCTP packets bigger than the current PMTU ๐Ÿ™ˆ

Implementation Note: in this error case, the SEND primitive from Section 11.1.5 would need to return an error to the upper layer ๐Ÿซถ

if the peer is multi-homed, the endpoint SHOULD choose a DATA chunk size that is smaller than or equal to the AMDCS โœจ and once a user message has been fragmented, it can't be re-fragmented ๐Ÿ™…โ€โ™€๏ธ instead, if the PMTU has gone down, IP fragmentation MUST be used ๐Ÿ”ฅ which is kinda scary, because it means an SCTP association can fail if IP fragmentation isn't working on any path ๐Ÿ’€ see Section 7.3 for the details of PMTU discovery ๐Ÿงš

when you're figuring out whether to fragment, the SCTP implementation MUST count the SCTP packet header AND the DATA chunk header(s) ๐ŸŒˆ and if it's bundling a SACK chunk with the DATA chunk, it MUST also count the space the SACK chunk needs ๐Ÿ’œ

here's how fragmentation goes down, step by step ๐Ÿ‘‘

  1. the data sender MUST break the user message into a series of DATA chunks, and it SHOULD pick a DATA chunk size that's smaller than or equal to the AMDCS ๐Ÿ“
  2. the transmitter MUST then give each DATA chunk its own TSN, in sequence, and it assigns the same Stream Sequence Number to every single one of them ๐Ÿฆ‹ if the user wants unordered delivery, the U flag of each DATA chunk in that message MUST be set to 1 โœจ
  3. the transmitter MUST also set the B/E bits of the first DATA chunk in the series to 10, the last one to 01, and every chunk in the middle to 00 ๐Ÿ’–

on the receiving end, an endpoint MUST recognize fragmented DATA chunks by checking the B/E bits in each received DATA chunk, and then queue them up for reassembly ๐Ÿงฉ once the whole user message is put back together, SCTP hands it to the specific stream for possible reordering and final dispatching ๐ŸŽ€

and if the data receiver runs out of buffer space while it's still waiting on more fragments, it SHOULD dispatch part of its inbound message through a partial delivery API (see Section 11) ๐ŸŒธ that frees up some receive buffer space so the rest of the message can come in ๐Ÿซง

6.10. Bundling ๐ŸŽ€

okay so bundling just means you pack multiple chunks into one outbound SCTP packet ๐Ÿ“ฆ the total size of the resulting SCTP packet MUST be less that or equal to the current PMTU ๐Ÿ’…

(Note: the original text says "less that or equal to," which is a typo for "less than or equal to." The meaning is the same.)

if the peer endpoint is multi-homed, the sending endpoint SHOULD choose a size no larger than the PMTU of the current primary path โœจ

when you're bundling control chunks with DATA chunks, the endpoint MUST put the control chunks FIRST in the outbound SCTP packet ๐Ÿซถ and the transmitter MUST send DATA chunks within a packet in increasing order of TSN ๐Ÿ”ฅ

Note: since control chunks go first in a packet, and DATA chunks get sent before SHUTDOWN or SHUTDOWN ACK chunks, DATA chunks cannot be bundled with SHUTDOWN or SHUTDOWN ACK chunks ๐Ÿ™ˆ

partial chunks MUST NOT be placed in an SCTP packet ๐Ÿšซ a partial chunk is one that isn't completely inside the SCTP packet, meaning the packet is too short to hold all the bytes the chunk length says it should have ๐Ÿ’€

an endpoint MUST process received chunks in the order they appear in the packet ๐ŸŒˆ the receiver uses the Chunk Length field to figure out where a chunk ends and the next one starts, remembering that all chunks end on a 4-byte boundary ๐Ÿงš and if the receiver spots a partial chunk, it MUST drop that chunk ๐Ÿ˜ญ

an endpoint MUST NOT bundle INIT, INIT ACK, or SHUTDOWN COMPLETE chunks with any other chunks ๐Ÿ™…โ€โ™€๏ธ no mixing those, bestie ๐Ÿ’œ

  1. Congestion Control ๐ŸŒŠ

congestion control is one of the basic jobs of SCTP ๐Ÿ‘‘ to manage congestion, the mechanisms and algorithms in this section are to be employed ๐Ÿฆ‹

Implementation Note: as long as its specific performance requirements are met, an implementation is always allowed to use a more conservative congestion control algorithm than the one defined below ๐Ÿซถ

the congestion control algorithms SCTP uses are based on [RFC5681] โœจ this section explains how the algorithms defined in [RFC5681] get adapted for SCTP ๐Ÿ’– it lists the differences in protocol design between TCP and SCTP first, and then describes SCTP's congestion control scheme ๐ŸŽ€ it uses the same terminology as TCP congestion control whenever that makes sense ๐ŸŒธ

SCTP congestion control is always applied to the entire association, never to individual streams ๐Ÿงš

7.1. SCTP Differences from TCP Congestion Control ๐Ÿ“

Gap Ack Blocks in the SCTP SACK chunk carry the same semantic meaning as the TCP SACK ๐Ÿ’œ TCP treats the info in the SACK as advisory only, and SCTP treats the info in the Gap Ack Blocks in the SACK chunk as advisory too ๐Ÿซง in SCTP, any DATA chunk acknowledged by a SACK chunk, including DATA that arrived out of order, is NOT considered fully delivered until the Cumulative TSN Ack Point passes its TSN (aka the DATA chunk has been acknowledged by the Cumulative TSN Ack field in the SACK chunk) ๐Ÿ˜ญ so the value of cwnd controls the amount of outstanding data, rather than (as in non-SACK TCP) the upper bound between the highest acknowledged sequence number and the latest DATA chunk that can be sent within the congestion window ๐Ÿฆ‹ SCTP SACK leads to different implementations of Fast Retransmit and Fast Recovery than non-SACK TCP ๐Ÿ”ฅ for an example, see [FALL96] โœจ

the biggest difference between SCTP and TCP, though, is multi-homing ๐Ÿ‘‘ SCTP is designed to set up robust communication associations between two endpoints, each of which might be reachable by more than one transport address ๐Ÿ’– different addresses might lead to different data paths between the two endpoints, so ideally you'd want a separate set of congestion control parameters for each path ๐ŸŒˆ the treatment of congestion control for multi-homed receivers is new with SCTP and might need refinement later ๐Ÿ™ˆ the current algorithms make these assumptions:

  • the sender usually uses the same destination address until the upper layer tells it to do otherwise ๐Ÿ’… however, SCTP MAY change to an alternate destination if an address is marked inactive (see Section 8.2) ๐Ÿซถ also, SCTP MAY retransmit to a different transport address than the original transmission ๐Ÿงš
  • the sender keeps a separate congestion control parameter set for each destination address it can send to (not each source-destination pair, but each destination) ๐ŸŽ€ the parameters SHOULD decay if the address isn't used for a long enough time period ๐Ÿ“ [RFC5681] specifies this period of time as a retransmission timeout โฐ
  • for each destination address, an endpoint does slow start upon the first transmission to that address ๐Ÿš€

Note: TCP guarantees in-sequence delivery of data to its upper-layer protocol within a single TCP session ๐ŸŒธ so when TCP notices a gap in the received sequence number, it waits until the gap is filled before delivering the data received with sequence numbers higher than the missing data ๐Ÿ™…โ€โ™€๏ธ SCTP, on the other hand, can deliver data to its upper-layer protocol even with a gap in TSN, as long as the Stream Sequence Numbers are in sequence for a particular stream (i.e., the missing DATA chunks are for a different stream) or if unordered delivery is indicated ๐Ÿ’œ this doesn't affect cwnd, but it might affect the rwnd calculation ๐Ÿซง

7.2. SCTP Slow-Start and Congestion Avoidance ๐Ÿฃ

the slow-start and congestion avoidance algorithms MUST be used by an endpoint to control how much data gets injected into the network ๐ŸŒŠ congestion control in SCTP is handled per association, not per individual stream ๐Ÿฆ‹ in some situations it might be smart for an SCTP sender to be more conservative than the algorithms allow ๐Ÿ’… however, an SCTP sender MUST NOT be more aggressive than the following algorithms allow ๐Ÿ™ˆ

like TCP, an SCTP endpoint uses these three control variables to regulate its transmission rate ๐ŸŽ€

  • receiver advertised window size (rwnd, in bytes), which is set by the receiver based on its available buffer space for incoming packets ๐Ÿ“

Note: this variable is kept on the entire association ๐Ÿ’–

  • congestion control window (cwnd, in bytes), which is adjusted by the sender based on observed network conditions ๐ŸŒธ

Note: this variable is maintained on a per-destination-address basis ๐Ÿงš

  • slow-start threshold (ssthresh, in bytes), which the sender uses to tell the slow-start and congestion avoidance phases apart ๐Ÿ‘‘

Note: this variable is maintained on a per-destination-address basis ๐Ÿซถ

SCTP also requires one more control variable, partial_bytes_acked, which is used during the congestion avoidance phase to help adjust cwnd ๐Ÿ”ฅ

unlike TCP, an SCTP sender MUST keep a set of the control variables cwnd, ssthresh, and partial_bytes_acked for EACH destination address of its peer (when its peer is multi-homed) ๐ŸŒˆ when calculating one of these variables, the length of the DATA chunk, including the padding, SHOULD be used ๐Ÿ’œ

only one rwnd is kept for the whole association (no matter if the peer is multi-homed or has a single address) โœจ

7.2.1. Slow-Start ๐Ÿฃ

Okay so, before you start blasting data into a network with unknown conditions (or after it's been idle for ages), SCTP has to probe the network to figure out how much room it actually has ๐Ÿซฃ. Slow-start is the move for that. It runs at the beginning of a transfer, or after fixing loss that the retransmission timer caught.

  • The initial cwnd before data transmission MUST be set to min(4 * PMDCS, max(2 * PMDCS, 4404)) bytes if the peer address is an IPv4 address and to min(4 * PMDCS, max(2 * PMDCS, 4344)) bytes if the peer address is an IPv6 address.
  • The initial cwnd after a retransmission timeout MUST be no more than PMDCS, and only one packet is allowed to be in flight until successful acknowledgement. Very cautious, very iconic ๐Ÿ’….
  • The initial value of ssthresh SHOULD be arbitrarily high (e.g., the size of the largest-possible advertised window).
  • Whenever cwnd is greater than zero, the endpoint is allowed to have cwnd bytes of data outstanding on that transport address. A limited overbooking as described in rule B in Section 6.1 SHOULD be supported.
  • When cwnd is less than or equal to ssthresh, an SCTP endpoint MUST use the slow-start algorithm to increase cwnd only if the current congestion window is being fully utilized and the data sender is not in Fast Recovery. Only when these two conditions are met can the cwnd be increased; otherwise, the cwnd MUST NOT be increased. If these conditions are met, then cwnd MUST be increased by, at most, the lesser of
    1. the total size of the previously outstanding DATA chunk(s) acknowledged and
    2. L times the destination's PMDCS.

The first upper bound protects against the ACK-Splitting attack outlined in [SAVAGE99]. The positive integer L SHOULD be 1 and MAY be larger than 1. See [RFC3465] for details of choosing L. ๐Ÿ›ก๏ธ

In instances where its peer endpoint is multi-homed, if an endpoint receives a SACK chunk that results in updating the cwnd, then it SHOULD update its cwnd (or cwnds) apportioned to the destination addresses to which it transmitted the acknowledged data.

  • While the endpoint does not transmit data on a given transport address, the cwnd of the transport address SHOULD be adjusted to max(cwnd / 2, 4 * PMDCS) once per RTO. Before the first cwnd adjustment, the ssthresh of the transport address SHOULD be set to the cwnd.

7.2.2. Congestion Avoidance ๐ŸŒธ

So once cwnd is greater than ssthresh, we're in congestion avoidance mode, which is a much calmer, more chill vibe. cwnd SHOULD be incremented by PMDCS per RTT, but only if the sender has cwnd or more bytes outstanding for that transport address. The basic recommendations go like this:

  • SCTP MAY increment cwnd by PMDCS.
  • SCTP SHOULD increment cwnd by PMDCS once per RTT when the sender has cwnd or more bytes of data outstanding for the corresponding transport address.
  • SCTP MUST NOT increment cwnd by more than PMDCS per RTT.

In practice, an implementation can hit this goal in the following way:

  • partial_bytes_acked is initialized to 0.
  • Whenever cwnd is greater than ssthresh, upon each SACK chunk arrival, increase partial_bytes_acked by the total number of bytes (including the chunk header and the padding) of all new DATA chunks acknowledged in that SACK chunk, including chunks acknowledged by the new Cumulative TSN Ack, by Gap Ack Blocks, and by the number of bytes of duplicated chunks reported in Duplicate TSNs.
  • When (1) partial_bytes_acked is greater than cwnd and (2) before the arrival of the SACK chunk the sender had less than cwnd bytes of data outstanding (i.e., before the arrival of the SACK chunk, flightsize was less than cwnd), reset partial_bytes_acked to cwnd.
  • When (1) partial_bytes_acked is equal to or greater than cwnd and (2) before the arrival of the SACK chunk the sender had cwnd or more bytes of data outstanding (i.e., before the arrival of the SACK chunk, flightsize was greater than or equal to cwnd), partial_bytes_acked is reset to (partial_bytes_acked - cwnd). Next, cwnd is increased by PMDCS.
  • Same as in the slow start, when the sender does not transmit DATA chunks on a given transport address, the cwnd of the transport address SHOULD be adjusted to max(cwnd / 2, 4 * PMDCS) per RTO.
  • When all of the data transmitted by the sender has been acknowledged by the receiver, partial_bytes_acked is initialized to 0.

7.2.3. Congestion Control ๐Ÿ’ฅ

Upon detecting packet losses from SACK chunks (see Section 7.2.4), an endpoint SHOULD do the following:

ssthresh = max(cwnd / 2, 4 * PMDCS)
cwnd = ssthresh
partial_bytes_acked = 0

Basically, a packet loss causes cwnd to get cut in half. Ouch, but it's the rule ๐Ÿ˜ญ.

When the T3-rtx timer expires on an address, SCTP SHOULD perform slow start by:

ssthresh = max(cwnd / 2, 4 * PMDCS)
cwnd = PMDCS
partial_bytes_acked = 0

and ensure that no more than one SCTP packet will be in flight for that address until the endpoint receives acknowledgement for successful delivery of data to that address.

7.2.4. Fast Retransmit on Gap Reports ๐Ÿƒโ€โ™€๏ธ

When there's no data loss, an endpoint does delayed acknowledgement. However, whenever an endpoint notices a hole in the arriving TSN sequence, it SHOULD start sending a SACK chunk back every time a packet arrives carrying data until the hole is filled. Hole spotted, alarm bells on ๐Ÿšจ.

Whenever an endpoint receives a SACK chunk that indicates some TSNs are missing, it SHOULD wait for two further miss indications (via subsequent SACK chunks, for a total of three missing reports) on the same TSNs before taking action with regard to Fast Retransmit. Patience, bestie.

Miss indications SHOULD follow the Highest TSN Newly Acknowledged (HTNA) algorithm. For each incoming SACK chunk, miss indications are incremented only for missing TSNs prior to the HTNA in the SACK chunk. A newly acknowledged DATA chunk is one not previously acknowledged in a SACK chunk. If an endpoint is in Fast Recovery and a SACK chunk arrives that advances the Cumulative TSN Ack Point, the miss indications are incremented for all TSNs reported missing in the SACK chunk.

When the third consecutive miss indication is received for one or more TSNs, the data sender does the following:

  1. Mark the DATA chunk(s) with three miss indications for retransmission.
  2. If not in Fast Recovery, adjust the ssthresh and cwnd of the destination address(es) to which the missing DATA chunks were last sent, according to the formula described in Section 7.2.3.
  3. If not in Fast Recovery, determine how many of the earliest (i.e., lowest TSN) DATA chunks marked for retransmission will fit into a single packet, subject to constraint of the PMTU of the destination transport address to which the packet is being sent. Call this value K. Retransmit those K DATA chunks in a single packet. When a Fast Retransmit is being performed, the sender SHOULD ignore the value of cwnd and SHOULD NOT delay retransmission for this single packet.
  4. Restart the T3-rtx timer only if the last SACK chunk acknowledged the lowest outstanding TSN number sent to that address or the endpoint is retransmitting the first outstanding DATA chunk sent to that address.
  5. Mark the DATA chunk(s) as being fast retransmitted and thus ineligible for a subsequent Fast Retransmit. Those TSNs marked for retransmission due to the Fast-Retransmit algorithm that did not fit in the sent datagram carrying K other TSNs are also marked as ineligible for a subsequent Fast Retransmit. However, as they are marked for retransmission, they will be retransmitted later on as soon as cwnd allows.
  6. If not in Fast Recovery, enter Fast Recovery and mark the highest outstanding TSN as the Fast Recovery exit point. When a SACK chunk acknowledges all TSNs up to and including this exit point, Fast Recovery is exited. While in Fast Recovery, the ssthresh and cwnd SHOULD NOT change for any destinations due to a subsequent Fast Recovery event (i.e., one SHOULD NOT reduce the cwnd further due to a subsequent Fast Retransmit).
Note: Before the above adjustments, if the received SACK chunk also acknowledges new DATA chunks and advances the Cumulative TSN Ack Point, the cwnd adjustment rules defined in Sections 7.2.1 and 7.2.2 MUST be applied first. Order of operations matters, iykyk ๐Ÿ™ˆ.

7.2.5. Reinitialization ๐Ÿ”„

During the lifetime of an SCTP association, events can happen that put the network under unknown new conditions. When an SCTP implementation detects this, the congestion control MUST be reinitialized. Fresh start, no drama ๐Ÿงผ.

7.2.5.1. Change of Differentiated Services Code Points ๐ŸŽ€

SCTP implementations MAY allow an application to configure the Differentiated Services Code Point (DSCP) used for sending packets. If a DSCP change might result in outgoing packets being queued in different queues, the congestion control parameters for all affected destination addresses MUST be reset to their initial values.

7.2.5.2. Change of Routes ๐Ÿ›ฃ๏ธ

SCTP implementations MAY be aware of routing changes affecting packets sent to a destination address. In particular, this includes the selection of a different source address used for sending packets to a destination address. If such a routing change happens, the congestion control parameters for the affected destination addresses MUST be reset to their initial values.

7.3. PMTU Discovery ๐Ÿงญ

[RFC8899], [RFC8201], and [RFC1191] specify "Packetization Layer Path MTU Discovery," where an endpoint keeps an estimate of the PMTU along a given Internet path and avoids sending packets along that path that exceed the PMTU, except for the occasional attempt to probe for a change in the PMTU. [RFC8899] is thorough in its discussion of the PMTU discovery mechanism and strategies for determining the current end-to-end PMTU setting, as well as detecting changes in this value. Go read it if you're curious, it's a whole vibe ๐Ÿค“.

An endpoint SHOULD apply these techniques, and SHOULD do so on a per-destination-address basis.

There are two important SCTP-specific points regarding PMTU discovery:

  1. SCTP associations can span multiple addresses. An endpoint MUST maintain separate PMTU estimates for each destination address of its peer.
  2. The sender SHOULD track an AMDCS that will be the smallest PMDCS discovered for all of the peer's destination addresses. When fragmenting messages into multiple parts, this AMDCS SHOULD be used to calculate the size of each DATA chunk. This will allow retransmissions to be seamlessly sent to an alternate address without encountering IP fragmentation. Main character energy for real โœจ.

8. Fault Management ๐Ÿ› ๏ธ

8.1. Endpoint Failure Detection ๐Ÿ’”

okay bestie, so an endpoint SHOULD keep a counter ๐Ÿ’… of the total number of consecutive retransmissions to its peer. That counts data retransmissions to every destination transport address of the peer if it's multi-homed, plus the number of unacknowledged HEARTBEAT chunks seen on the path that's currently carrying data. ๐Ÿ“Š

Unacknowledged HEARTBEAT chunks on any other path SHOULD NOT increment the association error counter ๐Ÿ™…โ€โ™€๏ธ. Otherwise the association could get closed even while the path that's actually carrying data is fine (just idle). If the counter goes over the limit in the protocol parameter 'Association.Max.Retrans', the endpoint SHOULD consider the peer unreachable and SHALL stop sending it any more data, and the association enters the CLOSED state ๐Ÿ’€. On top of that, the endpoint SHOULD report the failure to the upper layer and can optionally hand back all the user data still sitting in its outbound queue ๐Ÿ“ฆ. The association gets closed automatically once the peer becomes unreachable. no cap, it's giving dramatic but it's also very logical ๐Ÿซถ

The counter used for endpoint failure detection MUST be reset every time a DATA chunk sent to that peer is acknowledged (when a SACK chunk comes back) โœจ. When a HEARTBEAT ACK chunk arrives from the peer, the counter SHOULD be reset too. The receiver of that HEARTBEAT ACK chunk MAY choose not to clear the counter if there's still outstanding data on the association ๐Ÿค”. This is the bit that handles the fact that reachability through DATA chunks and reachability through HEARTBEAT chunks can be a little different ๐Ÿฆ‹


8.2. Path Failure Detection ๐Ÿ›ค๏ธ

when the peer endpoint is multi-homed, an endpoint SHOULD keep an error counter for each of the peer's destination transport addresses ๐Ÿ ๐Ÿ 

every time the T3-rtx timer expires on any address, or a HEARTBEAT chunk sent to an idle address isn't acknowledged within an RTO, that address's error counter goes up ๐Ÿ“ˆ. Once that counter goes over the protocol parameter 'Path.Max.Retrans' for that destination address, the endpoint SHOULD mark that destination transport address as inactive, and a notification SHOULD go to the upper layer ๐Ÿšจ

when an outstanding TSN gets acknowledged, or a HEARTBEAT chunk sent to that address gets acknowledged with a HEARTBEAT ACK chunk, the endpoint SHOULD clear the error counter of the destination transport address the DATA chunk was last sent to (or the HEARTBEAT chunk, if that was the last one sent) ๐Ÿงน. It SHOULD also report to the upper layer when an inactive destination address gets marked as active again ๐ŸŽ‰. Now here's the tea โ˜•: if the peer is multi-homed and the last chunk sent was a retransmission to an alternate address, it's honestly unclear whether the acknowledgement should count toward the address that chunk was actually sent on. BUT this ambiguity doesn't seem to cause any big consequences for SCTP behavior ๐Ÿ™ƒ. If it does bother you, the transmitter MAY choose not to clear the error counter when the last chunk sent was a retransmission ๐Ÿ’…

Note: When configuring the SCTP endpoint, the user ought to avoid having the value of 'Association.Max.Retrans' larger than the summation of the 'Path.Max.Retrans' of all the destination addresses for the remote endpoint. Otherwise, all the destination addresses might become inactive while the endpoint still considers the peer endpoint reachable. When this condition occurs, how SCTP chooses to function is implementation specific. ๐Ÿ™ˆ

when the primary path gets marked inactive (because of too many retransmissions, for instance), the sender MAY automatically transmit new packets to an alternate destination address, as long as one exists and is active ๐Ÿ”„. If more than one alternate address is active at that moment, only ONE transport address SHOULD be chosen and used as the new destination transport address ๐Ÿ‘‘


8.3. Path Heartbeat ๐Ÿ’“

by default, an SCTP endpoint SHOULD keep an eye on the reachability of its peer's idle destination transport addresses ๐Ÿ‘€. It does this by sending a HEARTBEAT chunk to those addresses periodically ๐Ÿซ€. Sending HEARTBEAT chunks MAY begin once the ESTABLISHED state is reached, and it stops after a SHUTDOWN chunk or SHUTDOWN ACK chunk gets sent ๐Ÿ›‘. A receiver of a HEARTBEAT chunk MUST respond with a HEARTBEAT ACK chunk once it's in the COOKIE-ECHOED state (the sender of the INIT chunk) or the ESTABLISHED state (the receiver of the INIT chunk). This keeps going until the endpoint reaches the SHUTDOWN-SENT state (the sender of the SHUTDOWN chunk) or the SHUTDOWN-ACK-SENT state (the receiver of the SHUTDOWN chunk) ๐Ÿงš

a destination transport address counts as "idle" if no new chunk that can be used for updating path RTT has been sent to it, and no HEARTBEAT chunk has gone to it within the current heartbeat period of that address. Chunks that can update path RTT usually include first transmission DATA, INIT, COOKIE ECHO, or HEARTBEAT chunks, and so on ๐Ÿ“ก. This applies to both active and inactive destination addresses, so it's not just the healthy ones ๐Ÿ’–

the upper layer can optionally kick off these functions ๐ŸŽ€:

  • A) Disable heartbeat on a specific destination transport address of a given association,
  • B) Change the 'HB.interval',
  • C) Re-enable heartbeat on a specific destination transport address of a given association, and
  • D) Request the sending of an on-demand HEARTBEAT chunk on a specific destination transport address of a given association.

the endpoint SHOULD increment the matching error counter for a destination transport address each time a HEARTBEAT chunk goes to that address and isn't acknowledged within one RTO ๐Ÿ“‰

once that counter goes over the protocol parameter 'Path.Max.Retrans', the endpoint SHOULD mark the destination address as inactive (if it isn't already) and SHOULD also tell the upper layer about the change in its reachability ๐Ÿšฉ. After that, the endpoint SHOULD keep sending HEARTBEAT chunks to that destination address, but it SHOULD stop increasing the counter. so it keeps checking without spiraling ๐Ÿง˜โ€โ™€๏ธ

the sender of a HEARTBEAT chunk SHOULD put the current time at which the packet is sent, plus the destination address the packet goes to, into the Heartbeat Information field of the chunk โฐ

Implementation Note: An alternative implementation of the heartbeat mechanism that can be used is to increment the error counter variable every time a HEARTBEAT chunk is sent to a destination. Whenever a HEARTBEAT ACK chunk arrives, the sender SHOULD clear the error counter of the destination that the HEARTBEAT chunk was sent to. This, in effect, would clear the previously stroked error (and any other error counts as well). ๐Ÿช„

the receiver of a HEARTBEAT chunk SHOULD reply right away with a HEARTBEAT ACK chunk. That reply carries the Heartbeat Information TLV, along with any other TLVs it received, copied over unchanged from the HEARTBEAT chunk ๐Ÿชž

when a HEARTBEAT ACK chunk arrives, the sender of the HEARTBEAT chunk SHOULD clear the error counter of the destination transport address the HEARTBEAT chunk went to, and mark that address as active if it isn't already โœ…. The endpoint SHOULD report to the upper layer when an inactive destination address becomes active because of that latest HEARTBEAT ACK chunk ๐Ÿ””. The receiver of the HEARTBEAT ACK chunk SHOULD also clear the association overall error count (as defined in Section 8.1) ๐Ÿงฝ

the receiver of the HEARTBEAT ACK chunk SHOULD also run an RTT measurement for that destination transport address, using the time value carried in the HEARTBEAT ACK chunk โฑ๏ธ

on an idle destination address that's allowed to heartbeat, it is RECOMMENDED that a HEARTBEAT chunk gets sent once per RTO of that address plus the protocol parameter 'HB.interval' ๐Ÿ—“๏ธ. It should also jitter by +/- 50% of the RTO value, and back off exponentially if the previous HEARTBEAT chunk went unanswered ๐ŸŽฒ

there's a primitive the SCTP user can call to change the 'HB.interval' and to turn heartbeat on or off for a given destination address ๐Ÿ”ง. The 'HB.interval' the user sets gets added to that destination's RTO (including any exponential backoff) โž•. Only one heartbeat SHOULD be sent each time the heartbeat timer expires (when multiple destinations are idle). How to pick which idle destination to heartbeat is an implementation decision, if more than one is idle ๐ŸŽฏ

When tuning the 'HB.interval', there is a side effect that SHOULD be taken into account. When this value goes up (so the time between HEARTBEAT chunks gets longer), detecting lost ABORT chunks takes longer too. If a peer endpoint sends an ABORT chunk for any reason and that ABORT chunk is lost, the local endpoint will only discover the loss by sending a DATA chunk or HEARTBEAT chunk, which makes the peer send another ABORT chunk ๐Ÿ”. Keep this in mind when you tune the heartbeat timer โฒ๏ธ. If sending HEARTBEAT chunks is disabled, only sending DATA chunks to the association will discover a lost ABORT chunk from the peer ๐Ÿ’Œ

8.4. Handle "Out of the Blue" Packets โœจ

okay so an SCTP packet is called an "Out of the Blue" (OOTB) packet ๐Ÿซฃ if it's correctly formed (aka it passed the receiver's CRC32c check, see Section 6.8 ๐Ÿ”) but the receiver just can't figure out which association it belongs to. literally lost ๐Ÿ’€

the receiver of an OOTB packet does the following, bestie ๐Ÿ’…:

  1. If the OOTB packet is to or from a non-unicast address, a receiver SHOULD silently discard the packet. Otherwise,
  2. If the OOTB packet contains an ABORT chunk, the receiver MUST silently discard the OOTB packet and take no further action. Otherwise,
  3. If the packet contains an INIT chunk with a Verification Tag set to 0, it SHOULD be processed as described in Section 5.1. If, for whatever reason, the INIT chunk cannot be processed normally and an ABORT chunk has to be sent in response, the Verification Tag of the packet containing the ABORT chunk MUST be the Initiate Tag of the received INIT chunk, and the T bit of the ABORT chunk has to be set to 0, indicating that the Verification Tag is not reflected. Otherwise,
  4. If the packet contains a COOKIE ECHO chunk as the first chunk, it MUST be processed as described in Section 5.1. Otherwise,
  5. If the packet contains a SHUTDOWN ACK chunk, the receiver SHOULD respond to the sender of the OOTB packet with a SHUTDOWN COMPLETE chunk. When sending the SHUTDOWN COMPLETE chunk, the receiver of the OOTB packet MUST fill in the Verification Tag field of the outbound packet with the Verification Tag received in the SHUTDOWN ACK chunk and set the T bit in the Chunk Flags to indicate that the Verification Tag is reflected. Otherwise,
  6. If the packet contains a SHUTDOWN COMPLETE chunk, the receiver SHOULD silently discard the packet and take no further action. Otherwise,
  7. If the packet contains an ERROR chunk with the "Stale Cookie" error cause or a COOKIE ACK chunk, the SCTP packet SHOULD be silently discarded. Otherwise,
  8. The receiver SHOULD respond to the sender of the OOTB packet with an ABORT chunk. When sending the ABORT chunk, the receiver of the OOTB packet MUST fill in the Verification Tag field of the outbound packet with the value found in the Verification Tag field of the OOTB packet and set the T bit in the Chunk Flags to indicate that the Verification Tag is reflected. After sending this ABORT chunk, the receiver of the OOTB packet MUST discard the OOTB packet and MUST NOT take any further action. ๐Ÿงš

8.5. Verification Tag ๐ŸŽ€

the Verification Tag rules in this section apply when you're sending or receiving SCTP packets that do NOT contain an INIT, SHUTDOWN COMPLETE, COOKIE ECHO (see Section 5.1), ABORT, or SHUTDOWN ACK chunk. the rules for packets carrying one of those chunk types get their own treatment in Section 8.5.1, so don't mix them up ๐Ÿ™ˆ

when sending an SCTP packet, the endpoint MUST fill in the Verification Tag field of the outbound packet with the tag value in the Initiate Tag parameter of the INIT or INIT ACK chunk received from its peer.

when receiving an SCTP packet, the endpoint MUST ensure that the value in the Verification Tag field of the received SCTP packet matches its own tag. if the received Verification Tag value does not match the receiver's own tag value, the receiver MUST silently discard the packet and MUST NOT process it any further, except for those cases listed in Section 8.5.1 below. it's giving strict bouncer energy ๐Ÿšซ

8.5.1. Exceptions in Verification Tag Rules ๐Ÿฆ‹

A) Rules for packets carrying an INIT chunk:

  • The sender MUST set the Verification Tag of the packet to 0.
  • When an endpoint receives an SCTP packet with the Verification Tag set to 0, it SHOULD verify that the packet contains only an INIT chunk. Otherwise, the receiver MUST silently discard the packet.

B) Rules for packets carrying an ABORT chunk:

  • The endpoint MUST always fill in the Verification Tag field of the outbound packet with the destination endpoint's tag value if it is known.
  • If the ABORT chunk is sent in response to an OOTB packet, the endpoint MUST follow the procedure described in Section 8.4.
  • The receiver of an ABORT chunk MUST accept the packet if the Verification Tag field of the packet matches its own tag and the T bit is not set OR if it is set to its Peer's Tag and the T bit is set in the Chunk Flags. Otherwise, the receiver MUST silently discard the packet and take no further action.

C) Rules for packets carrying a SHUTDOWN COMPLETE chunk:

  • When sending a SHUTDOWN COMPLETE chunk, if the receiver of the SHUTDOWN ACK chunk has a TCB, then the destination endpoint's tag MUST be used and the T bit MUST NOT be set. Only where no TCB exists SHOULD the sender use the Verification Tag from the SHUTDOWN ACK chunk and MUST set the T bit.
  • The receiver of a SHUTDOWN COMPLETE chunk accepts the packet if the Verification Tag field of the packet matches its own tag and the T bit is not set OR if it is set to its Peer's Tag and the T bit is set in the Chunk Flags. Otherwise, the receiver MUST silently discard the packet and take no further action. An endpoint MUST ignore the SHUTDOWN COMPLETE chunk if it is not in the SHUTDOWN-ACK-SENT state.

D) Rules for packets carrying a COOKIE ECHO chunk:

  • When sending a COOKIE ECHO chunk, the endpoint MUST use the value of the Initiate Tag received in the INIT ACK chunk.
  • The receiver of a COOKIE ECHO chunk follows the procedures in Section 5.

E) Rules for packets carrying a SHUTDOWN ACK chunk:

  • If the receiver is in COOKIE-ECHOED or COOKIE-WAIT state, the procedures in Section 8.4 SHOULD be followed; in other words, it is treated as an OOTB packet. ๐Ÿซถ

9. Termination of Association ๐Ÿ’œ

an endpoint SHOULD terminate its association when it exits from service. an association can be ended by either abort or shutdown.

an abort is abortive by definition ๐Ÿ˜ค, meaning any data pending on either end of the association gets thrown out and never delivered to the peer. a shutdown is the graceful version ๐ŸŒธ, where all data in queue by either endpoint gets delivered to the respective peers.

BUT, and this is the tea โ˜•, SCTP does not support a half-open state (unlike TCP), where one side keeps sending data while the other end is closed. when either endpoint performs a shutdown, the association on each peer stops accepting new data from its user and only delivers data that was in queue at the time of sending or receiving the SHUTDOWN chunk.

9.1. Abort of an Association ๐ŸŒˆ

when an endpoint decides to abort an existing association, it MUST send an ABORT chunk to its peer endpoint. the sender MUST fill in the peer's Verification Tag in the outbound packet and MUST NOT bundle any DATA chunk with the ABORT chunk. if the association is aborted on request of the upper layer, a "User-Initiated Abort" error cause (see Section 3.3.10.12) SHOULD be present in the ABORT chunk. ๐Ÿ”ฅ

an endpoint MUST NOT respond to any received packet that contains an ABORT chunk (also see Section 8.4). no replying to the ABORT, no drama ๐Ÿ™…โ€โ™€๏ธ

an endpoint receiving an ABORT chunk MUST apply the special Verification Tag check rules described in Section 8.5.1.

after checking the Verification Tag, the receiving endpoint MUST remove the association from its record and SHOULD report the termination to its upper layer. if a "User-Initiated Abort" error cause is present in the ABORT chunk, the Upper Layer Abort Reason SHOULD be made available to the upper layer. โœจ๐Ÿ‘‘

9.2. Shutdown of an Association ๐Ÿฆ‹

Okay bestie, this is the polite way to leave the party ๐Ÿ’… Using the SHUTDOWN primitive (check Section 11.1 for the deets), the upper layer of an endpoint in an association can gracefully close it. The vibe is that every outstanding DATA chunk from the shutdown initiator's peer gets delivered before the association fully terminates ๐Ÿซถ

Once an endpoint gets the SHUTDOWN primitive from its upper layer, it drops into the SHUTDOWN-PENDING state and chills there until its peer has acknowledged all the outstanding data ๐Ÿ’œ Meanwhile it takes no new data from its upper layer, but it will retransmit data to the peer if needed to fill gaps ๐Ÿ”ฅ

When everything's acked, the endpoint sends a SHUTDOWN chunk to its peer, and in the Cumulative TSN Ack field it puts the last sequential TSN it received from the peer ๐Ÿ“ It SHOULD then start the T2-shutdown timer and enter the SHUTDOWN-SENT state. If that timer goes off, the endpoint MUST resend the SHUTDOWN chunk with the updated last sequential TSN it received from its peer ๐Ÿ˜ญ

Timer math: the rules in Section 6.3 MUST be followed to figure out the proper value for T2-shutdown ๐Ÿงฎ To flag any gaps in the TSN, the endpoint MAY also bundle a SACK chunk with the SHUTDOWN chunk in the same SCTP packet ๐ŸŽ€

Retransmission limits: an endpoint SHOULD limit how many times it resends the SHUTDOWN chunk to the protocol parameter 'Association.Max.Retrans'. If that threshold gets blown past, the endpoint SHOULD destroy the TCB and SHOULD report the peer endpoint as unreachable to the upper layer, and the association goes to the CLOSED state ๐Ÿ’€ Also, receiving any packet from its peer (like when the peer is sending all its queued DATA chunks) SHOULD clear the endpoint's retransmission count and restart the T2-shutdown timer. That gives the peer plenty of room to send the queued DATA chunks that haven't gone out yet ๐ŸŒธ

When the peer gets the SHUTDOWN chunk, it does three things:

  • hops into the SHUTDOWN-RECEIVED state,
  • stops taking new data from its SCTP user, and
  • double-checks, using the Cumulative TSN Ack field of the chunk, that every one of its outstanding DATA chunks has been received by the SHUTDOWN chunk sender.

Once you're in SHUTDOWN-RECEIVED, you MUST ignore ULP shutdown requests, but you MUST keep answering SHUTDOWN chunks from your peer ๐Ÿ™ˆ

If there's still outstanding DATA hanging around, the SHUTDOWN chunk receiver MUST keep following the normal data transmission procedures from Section 6 until everything is acknowledged. However, it MUST NOT accept new data from its SCTP user ๐Ÿง

While in SHUTDOWN-SENT, the SHUTDOWN chunk sender MUST immediately respond to each received packet containing one or more DATA chunks with a SHUTDOWN chunk, and restart the T2-shutdown timer. If a SHUTDOWN chunk by itself can't acknowledge all the received DATA chunks (meaning there are TSNs that can be acknowledged that are larger than the cumulative TSN, so gaps exist in the TSN sequence), or if duplicate TSNs have been received, then a SACK chunk MUST also be sent ๐Ÿ”

The sender of the SHUTDOWN chunk MAY also start an overall guard timer, T5-shutdown-guard, to bound the whole shutdown sequence. When that timer expires, the sender SHOULD abort the association by sending an ABORT chunk. If T5-shutdown-guard is used, it SHOULD be set to the RECOMMENDED value of 5 times 'RTO.Max' โฑ๏ธ

If the receiver of the SHUTDOWN chunk has no more outstanding DATA chunks, it MUST send a SHUTDOWN ACK chunk and start a T2-shutdown timer of its own, entering the SHUTDOWN-ACK-SENT state. If that timer expires, the endpoint MUST resend the SHUTDOWN ACK chunk ๐Ÿฅน

The sender of the SHUTDOWN ACK chunk SHOULD limit its retransmissions of that chunk to 'Association.Max.Retrans'. If that threshold is exceeded, the endpoint SHOULD destroy the TCB and SHOULD report the peer endpoint unreachable to the upper layer, and the association enters the CLOSED state ๐Ÿšช

When the SHUTDOWN chunk sender gets the SHUTDOWN ACK chunk, it MUST stop the T2-shutdown timer, send a SHUTDOWN COMPLETE chunk to its peer, and remove all record of the association ๐ŸŽ‰

When a SHUTDOWN COMPLETE chunk arrives, the endpoint checks that it's in SHUTDOWN-ACK-SENT. If it isn't, the chunk SHOULD be discarded. If it is in SHUTDOWN-ACK-SENT, the endpoint SHOULD stop the T2-shutdown timer and remove all knowledge of the association, so the association enters the CLOSED state โœจ

An endpoint SHOULD make sure all its outstanding DATA chunks have been acknowledged before it starts the shutdown procedure ๐Ÿงผ

An endpoint SHOULD reject any new data request from its upper layer if it is in SHUTDOWN-PENDING, SHUTDOWN-SENT, SHUTDOWN-RECEIVED, or SHUTDOWN-ACK-SENT ๐Ÿ™…

If an endpoint in SHUTDOWN-ACK-SENT receives an INIT chunk (say the SHUTDOWN COMPLETE chunk got lost) whose source and destination transport addresses (either in the IP addresses or in the INIT chunk) belong to this association, it SHOULD discard the INIT chunk and retransmit the SHUTDOWN ACK chunk ๐Ÿ”

Note: Receipt of a packet containing an INIT chunk with the same source and destination IP addresses as used in transport addresses assigned to an endpoint, but with a different port number, indicates the initialization of a separate association. ๐Ÿ‘€

The sender of the INIT or COOKIE ECHO chunk SHOULD respond to a SHUTDOWN ACK chunk with a stand-alone SHUTDOWN COMPLETE chunk, in an SCTP packet whose common header's Verification Tag field is set to the same tag that was received in the packet containing the SHUTDOWN ACK chunk. This counts as an OOTB packet, as defined in Section 8.4. The sender of the INIT chunk lets T1-init keep running and stays in the COOKIE-WAIT or COOKIE-ECHOED state. When T1-init expires as normal, the INIT or COOKIE chunk gets retransmitted, which starts a new association ๐ŸŒˆ

If a SHUTDOWN chunk is received in the COOKIE-WAIT or COOKIE ECHOED state, the SHUTDOWN chunk SHOULD be silently discarded ๐Ÿคซ

If an endpoint in SHUTDOWN-SENT receives a SHUTDOWN chunk from its peer, the endpoint SHOULD respond immediately with a SHUTDOWN ACK chunk to its peer, move into the SHUTDOWN-ACK-SENT state, and restart its T2-shutdown timer ๐Ÿค

If an endpoint in SHUTDOWN-ACK-SENT receives a SHUTDOWN ACK, it MUST stop the T2-shutdown timer, send a SHUTDOWN COMPLETE chunk to its peer, and remove all record of the association ๐Ÿ’–


10. ICMP Handling ๐ŸŒธ

Whenever an SCTP endpoint receives an ICMP message, the following procedures MUST be followed so the info layer 3 is providing gets used properly ๐Ÿงš

  • ICMP1) An implementation MAY ignore all ICMPv4 messages where the type field is not set to "Destination Unreachable".
  • ICMP2) An implementation MAY ignore all ICMPv6 messages where the type field is not "Destination Unreachable", "Parameter Problem", or "Packet Too Big".
  • ICMP3) An implementation SHOULD ignore any ICMP messages where the code indicates "Port Unreachable".
  • ICMP4) An implementation MAY ignore all ICMPv6 messages of type "Parameter Problem" if the code is not "Unrecognized Next Header Type Encountered".
  • ICMP5) An implementation MUST use the payload of the ICMP message (v4 or v6) to locate the association that sent the message to which ICMP is responding. If the association cannot be found, an implementation SHOULD ignore the ICMP message.
  • ICMP6) An implementation MUST validate that the Verification Tag contained in the ICMP message matches the Verification Tag of the peer. If the Verification Tag is not 0 and does not match, discard the ICMP message. If it is 0 and the ICMP message contains enough bytes to verify that the chunk type is an INIT chunk and that the Initiate Tag matches the tag of the peer, continue with ICMP7. If the ICMP message is too short, or the chunk type or the Initiate Tag does not match, silently discard the packet.
  • ICMP7) If the ICMP message is either an ICMPv6 message of type "Packet Too Big" or an ICMPv4 message of type "Destination Unreachable" and code "Fragmentation Needed", an implementation SHOULD process this information as defined for PMTU discovery.
  • ICMP8) If the ICMP code is "Unrecognized Next Header Type Encountered" or "Protocol Unreachable", an implementation MUST treat this message as an abort with the T bit set if it does not contain an INIT chunk. If it does contain an INIT chunk and the association is in the COOKIE-WAIT state, handle the ICMP message like an ABORT chunk.
  • ICMP9) If the ICMP type is "Destination Unreachable", the implementation MAY move the destination to the unreachable state or, alternatively, increment the path error counter. SCTP MAY provide information to the upper layer indicating the reception of ICMP messages when reporting a network status change.

These procedures differ from [RFC1122] and from its requirements for processing of port-unreachable messages, and from the requirements that an implementation MUST abort associations in response to a protocol unreachable message. Port-unreachable messages are not processed, since an implementation will send an ABORT chunk, not a port-unreachable message. The stricter handling of the protocol unreachable message is due to security concerns for hosts that do not support SCTP ๐Ÿ›ก๏ธ


11. Interface with Upper Layer ๐Ÿ’…

The Upper Layer Protocols (ULPs) request services by passing primitives to SCTP, and they receive notifications from SCTP about various events ๐Ÿ“ฃ

The primitives and notifications described in this section can be used as a guideline for implementing SCTP. The functional description of the ULP interface primitives below is shown for illustrative purposes. Different SCTP implementations can have different ULP interfaces. However, all SCTP implementations are expected to provide a certain minimum set of services, so that every SCTP implementation can support the same protocol hierarchy ๐Ÿงฉ

Heads up: this section is informational only ๐Ÿ“

[RFC6458] and Section 7 ("Socket API Considerations") of [RFC7053] define an extension of the socket API for SCTP as described in this document ๐Ÿ”Œ

11.1. ULP-to-SCTP ๐ŸŽ€

The following sections functionally characterize a ULP/SCTP interface. The notation is similar to most procedure or function calls in high-level languages ๐Ÿ’œ

The ULP primitives below describe the basic functions SCTP performs to support inter-process communication. Individual implementations define their own exact format and provide combinations or subsets of the basic functions in single calls โœจ

11.1.1. Initialize

INITIALIZE ([local port],[local eligible address list])
-> local SCTP instance name

Okay bestie, INITIALIZE is the setup moment ๐Ÿ’… It lets SCTP set up its internal data structures and grab the resources it needs for its operating environment โœจ Once SCTP is initialized, the ULP can talk straight to other endpoints without calling this again, which is so efficient ๐Ÿซถ SCTP hands the ULP back a local SCTP instance name ๐ŸŽ€

Mandatory attributes: None.

Optional attributes:

  • local port: SCTP port number, if ULP wants it to be specified.
  • local eligible address list: an address list that the local SCTP endpoint binds. By default, if an address list is not included, all IP addresses assigned to the host are used by the local endpoint ๐ŸŒธ

Implementation Note: If this optional attribute is supported by an implementation, it will be the responsibility of the implementation to enforce that the IP source address field of any SCTP packets sent by this endpoint contains one of the IP addresses indicated in the local eligible address list ๐Ÿฆ‹

11.1.2. Associate

ASSOCIATE(local SCTP instance name,
initial destination transport addr list, outbound stream count)
-> association id [,destination transport addr list]
[,outbound stream count]

This primitive lets the upper layer start an association with a specific peer endpoint ๐Ÿงš The peer endpoint is specified by one or more of the transport addresses that define the endpoint (see Section 1.3). If the local SCTP instance has not been initialized, the ASSOCIATE is considered an error ๐Ÿ’€

An association id, which is a local handle to the SCTP association, will be returned on successful establishment of the association. If SCTP is not able to open an SCTP association with the peer endpoint, an error is returned ๐Ÿ˜ญ

Other association parameters can be returned, including the complete destination transport addresses of the peer as well as the outbound stream count of the local endpoint. One of the transport addresses from the returned destination addresses will be selected by the local endpoint as the default primary path for sending SCTP packets to this peer ๐Ÿ‘‘ The returned "destination transport addr list" can be used by the ULP to change the default primary path or to force sending a packet to a specific transport address ๐ŸŽ€

Implementation Note: If the ASSOCIATE primitive is implemented as a blocking function call, the ASSOCIATE primitive can return association parameters in addition to the association id upon successful establishment. If ASSOCIATE primitive is implemented as a non-blocking call, only the association id is returned and association parameters are passed using the COMMUNICATION UP notification ๐ŸŒˆ

Mandatory attributes:

  • local SCTP instance name: obtained from the INITIALIZE operation.
  • initial destination transport addr list: a non-empty list of transport addresses of the peer endpoint with which the association is to be established.
  • outbound stream count: the number of outbound streams the ULP would like to open towards this peer endpoint.

Optional attributes: None.

11.1.3. Shutdown

SHUTDOWN(association id) -> result

SHUTDOWN is the gentle exit ๐ŸŒธ It gracefully closes an association. Any locally queued user data will be delivered to the peer. The association will be terminated only after the peer acknowledges all the SCTP packets sent. A success code will be returned on successful termination of the association. If attempting to terminate the association results in a failure, an error code is returned ๐Ÿ“

Mandatory attributes:

  • association id: local handle to the SCTP association.

Optional attributes: None.

11.1.4. Abort

ABORT(association id [, Upper Layer Abort Reason]) -> result

ABORT is the dramatic exit ๐Ÿ’… It ungracefully closes an association. Any locally queued user data will be discarded, and an ABORT chunk is sent to the peer. A success code will be returned on successful abort of the association. If attempting to abort the association results in a failure, an error code is returned ๐Ÿ˜ญ

Mandatory attributes:

  • association id: local handle to the SCTP association.

Optional attributes:

  • Upper Layer Abort Reason: reason of the abort to be passed to the peer.

11.1.5. Send

SEND(association id, buffer address, byte count [,context]
[,stream id] [,life time] [,destination transport address]
[,unordered flag] [,no-bundle flag] [,payload protocol-id]
[,sack-immediately flag]) -> result

This is the main method for sending user data via SCTP, so it's giving main character energy ๐Ÿ‘‘

Mandatory attributes:

  • association id: local handle to the SCTP association.
  • buffer address: the location where the user message to be transmitted is stored.
  • byte count: the size of the user data in number of bytes.

Optional attributes:

  • context: optional information provided that will be carried in the SEND FAILURE notification to the ULP if the transportation of this user message fails.
  • stream id: indicates which stream to send the data on. If not specified, stream 0 will be used ๐ŸŽ€
  • life time: specifies the life time of the user data. The user data will not be sent by SCTP after the life time expires. This parameter can be used to avoid efforts to transmit stale user messages. SCTP notifies the ULP if the data cannot be initiated to transport (i.e., sent to the destination via SCTP's SEND primitive) within the life time variable. However, the user data will be transmitted if SCTP has attempted to transmit a chunk before the life time expired โณ

Implementation Note: In order to better support the data life time option, the transmitter can hold back the assigning of the TSN number to an outbound DATA chunk to the last moment. And, for implementation simplicity, once a TSN number has been assigned, the sender considers the send of this DATA chunk as committed, overriding any life time option attached to the DATA chunk ๐Ÿงš

  • destination transport address: specified as one of the destination transport addresses of the peer endpoint to which this packet is sent. Whenever possible, SCTP uses this destination transport address for sending the packets, instead of the current primary path ๐Ÿฆ‹
  • unordered flag: this flag, if present, indicates that the user would like the data delivered in an unordered fashion to the peer (i.e., the U flag is set to 1 on all DATA chunks carrying this message) ๐ŸŒˆ
  • no-bundle flag: instructs SCTP not to delay the sending of DATA chunks for this user data just to allow it to be bundled with other outbound DATA chunks. When faced with network congestion, SCTP might still bundle the data, even when this flag is present ๐Ÿซถ
  • payload protocol-id: a 32-bit unsigned integer that is to be passed to the peer, indicating the type of payload protocol data being transmitted. Note that the upper layer is responsible for the host to network byte order conversion of this field, which is passed by SCTP as 4 bytes of opaque data ๐Ÿ”ฅ
  • sack-immediately flag: set the I bit on the last DATA chunk used for the user message to be transmitted โœจ

11.1.6. Set Primary

SETPRIMARY(association id, destination transport address,
[source transport address]) -> result

SETPRIMARY tells the local SCTP to use the specified destination transport address as the primary path for sending packets ๐Ÿ’–

The result of attempting this operation is returned. If the specified destination transport address is not present in the "destination transport address list" returned earlier in an ASSOCIATE primitive or COMMUNICATION UP notification, an error is returned ๐Ÿ™ˆ

Mandatory attributes:

  • association id: local handle to the SCTP association.
  • destination transport address: specified as one of the transport addresses of the peer endpoint, which is used as the primary address for sending packets. This overrides the current primary address information maintained by the local SCTP endpoint.

Optional attributes:

  • source transport address: optionally, some implementations can allow you to set the default source address placed in all outgoing IP datagrams.

11.1.7. Receive

RECEIVE(association id, buffer address, buffer size [,stream id])
-> byte count [,transport address] [,stream id]
[,stream sequence number] [,partial flag] [,payload protocol-id]

This primitive reads the first user message in the SCTP in-queue into the buffer specified by ULP, if there is one available ๐Ÿ“ฅ The size of the message read, in bytes, will be returned. Depending on the specific implementation, it might also return other info, such as the sender's address, the stream id on which it is received, whether there are more messages available for retrieval, etc. For ordered messages, their Stream Sequence Number might also be returned ๐ŸŒธ

Depending upon the implementation, if this primitive is invoked when no message is available, the implementation returns an indication of this condition or blocks the invoking process until data does become available ๐Ÿ’œ

Mandatory attributes:

  • association id: local handle to the SCTP association.
  • buffer address: the memory location indicated by the ULP to store the received message.
  • buffer size: the maximum size of data to be received, in bytes.

Optional attributes:

  • stream id: to indicate which stream to receive the data on.
  • stream sequence number: the Stream Sequence Number assigned by the sending SCTP peer.
  • partial flag: if this returned flag is set to 1, then this primitive contains a partial delivery of the whole message. When this flag is set, the stream id and stream sequence number accompanies this primitive. When this flag is set to 0, it indicates that no more deliveries will be received for this stream sequence number ๐Ÿ“
  • payload protocol-id: a 32-bit unsigned integer that is received from the peer indicating the type of payload protocol of the received data. Note that the upper layer is responsible for the host to network byte order conversion of this field, which is passed by SCTP as 4 bytes of opaque data ๐Ÿงš

11.1.8. Status

STATUS(association id) -> status data

This primitive returns a data block containing the following information ๐Ÿ“Š

  • association connection state,
  • destination transport address list,
  • destination transport address reachability states,
  • current receiver window size,
  • current congestion window sizes,
  • number of unacknowledged DATA chunks,
  • number of DATA chunks pending receipt,
  • primary path,
  • most recent SRTT on primary path,
  • RTO on primary path,
  • SRTT and RTO on other destination addresses, etc.

Mandatory attributes:

  • association id: local handle to the SCTP association.

Optional attributes: None.

11.1.9. Change Heartbeat ๐Ÿ’“

CHANGE HEARTBEAT(association id, destination transport address,
new state [,interval]) -> result

okay so this primitive is how the local endpoint turns heartbeat on or off for a specific destination transport address ๐Ÿซถ it's giving control!!

The result of trying this is returned to you ๐Ÿ’…

Note: even when heartbeat is enabled, it won't actually happen if the destination transport address is not idle ๐Ÿ™ˆ so enabled doesn't always mean it's running rn

Mandatory attributes:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • destination transport address: has to be one of the transport addresses of the peer endpoint ๐ŸŒธ
  • new state: the new heartbeat state for this destination transport address, either enabled or disabled โœจ

Optional attributes:

  • interval: if you include it, it sets how often the heartbeat happens when you're turning heartbeat on for a destination transport address ๐Ÿฆ‹ this value gets added to the RTO of that destination transport address, and if it's included it affects all destinations, so be careful bestie ๐Ÿ‘€

11.1.10. Request Heartbeat ๐Ÿ’–

REQUESTHEARTBEAT(association id, destination transport address)
-> result

this one tells the local endpoint to do a heartbeat right now on a specific destination transport address of the association ๐Ÿš€ the result tells you whether sending the HEARTBEAT chunk to that destination address worked or not ๐Ÿ’œ

Mandatory attributes:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • destination transport address: the transport address of the association where the heartbeat gets sent ๐ŸŒˆ

Optional attributes:

  • None. ๐Ÿซ 

11.1.11. Get SRTT Report ๐Ÿ“Š

GETSRTTREPORT(association id, destination transport address)
-> srtt result

this primitive asks the local SCTP to report the current SRTT measurement on a given destination transport address of the association โœจ the result can be an integer holding the most recent SRTT, in milliseconds ๐Ÿ“

Mandatory attributes:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • destination transport address: the transport address of the association where the SRTT measurement should be reported ๐ŸŒธ

Optional attributes:

  • None. ๐Ÿ’…

11.1.12. Set Failure Threshold ๐Ÿšจ

SETFAILURETHRESHOLD(association id, destination transport address,
failure threshold) -> result

this one lets the local SCTP customize the reachability failure detection threshold, which is called 'Path.Max.Retrans', for a specific destination address ๐Ÿงš quick heads up, you can also do this with the SETPROTOCOLPARAMETERS primitive (Section 11.1.13) so pick your fave way ๐Ÿ’•

Mandatory attributes:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • destination transport address: the transport address of the association where the failure detection threshold is getting set ๐Ÿฆ‹
  • failure threshold: the new value of 'Path.Max.Retrans' for that destination address ๐Ÿ”ฅ

Optional attributes:

  • None. ๐Ÿซถ

11.1.13. Set Protocol Parameters โš™๏ธ

SETPROTOCOLPARAMETERS(association id,
[destination transport address,] protocol parameter list)
-> result

this primitive is the big glow-up tool ๐Ÿ’ซ it lets the local SCTP customize the protocol parameters

Mandatory attributes:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • protocol parameter list: the specific names and values of the protocol parameters that the SCTP user wants to customize, like 'Association.Max.Retrans' (see Section 16) or other stuff like the DSCP ๐ŸŒˆ

Optional attributes:

  • destination transport address: some protocol parameters might be set per destination transport address, so this is where that happens ๐ŸŒธ

11.1.14. Receive Unsent Message ๐Ÿ“ญ

RECEIVE_UNSENT(data retrieval id, buffer address, buffer size
[,stream id] [, stream sequence number] [,partial flag]
[,payload protocol-id])

this primitive reads a user message that has never been sent, and drops it into the buffer the ULP specified ๐Ÿงบ it's kinda like rescuing a text you never hit send on ๐Ÿ˜ญ

Mandatory attributes:

  • data retrieval id: the identification passed to the ULP in the SEND FAILURE notification ๐Ÿชช
  • buffer address: the memory location the ULP indicated for storing the received message ๐Ÿ“
  • buffer size: the maximum size of data to be received, in bytes ๐Ÿ“

Optional attributes:

  • stream id: this is a return value that gets set to show which stream the data was sent to ๐ŸŒŠ
  • stream sequence number: this value is returned, showing the Stream Sequence Number that went with the message ๐Ÿ”ข
  • partial flag: if this returned flag is set to 1, then the message is a partial delivery of the whole message ๐Ÿงฉ when this flag is set, the stream id and stream sequence number come along with this primitive ๐ŸŽ€ when the flag is set to 0, it means no more deliveries are coming for that stream sequence number ๐Ÿ›‘
  • payload protocol-id: the 32-bit unsigned integer that was set to be sent to the peer, showing the type of payload protocol for the received data ๐Ÿงพ

11.1.15. Receive Unacknowledged Message ๐Ÿ“ฌ

RECEIVE_UNACKED(data retrieval id, buffer address, buffer size,
[,stream id] [,stream sequence number] [,partial flag]
[,payload protocol-id])

this one is kinda the sibling of the last primitive ๐Ÿ‘ฏโ€โ™€๏ธ it reads a user message that has been sent but has not been acknowledged by the peer, and puts it into the buffer the ULP specified ๐Ÿซง

Mandatory attributes:

  • data retrieval id: the identification passed to the ULP in the SEND FAILURE notification ๐Ÿชช
  • buffer address: the memory location the ULP indicated for storing the received message ๐Ÿ“
  • buffer size: the maximum size of data to be received, in bytes ๐Ÿ“

Optional attributes:

  • stream id: a return value that gets set to show which stream the data was sent to ๐ŸŒŠ
  • stream sequence number: this value is returned, showing the Stream Sequence Number that went with the message ๐Ÿ”ข
  • partial flag: if this returned flag is set to 1, the message is a partial delivery of the whole message ๐Ÿงฉ when this flag is set, the stream id and stream sequence number come along with this primitive ๐ŸŽ€ when it's set to 0, no more deliveries will be received for that stream sequence number ๐Ÿ›‘
  • payload protocol-id: the 32-bit unsigned integer that was sent to the peer, showing the type of payload protocol for the received data ๐Ÿงพ

11.1.16. Destroy SCTP Instance ๐Ÿ’ฅ

DESTROY(local SCTP instance name)

this is the clean-up primitive, bestie ๐Ÿงนโœจ

Mandatory attributes:

  • local SCTP instance name: the value that was passed to the application in the initialize primitive, showing which SCTP instance is getting destroyed ๐Ÿ’€

Optional attributes:

  • None. ๐ŸŒท

11.2. SCTP-to-ULP ๐Ÿ“ฃ

okay now we flip it, so this is the SCTP telling the ULP stuff ๐Ÿ’… it's assumed that the operating system or application environment gives SCTP a way to signal the ULP process asynchronously ๐Ÿ”” when SCTP does signal a ULP process, certain information gets passed along to the ULP ๐Ÿ’Œ

Implementation Note: in some cases this might happen through a separate socket or error channel ๐Ÿงฆ

11.2.1. DATA ARRIVE Notification ๐Ÿ“ฉ

SCTP invokes this notification on the ULP when a user message has been successfully received and is ready for retrieval ๐ŸŽ‰

The following might optionally be passed with the notification:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • stream id: shows which stream the data was received on ๐ŸŒŠ

11.2.2. SEND FAILURE Notification ๐Ÿšซ

if a message can't be delivered, SCTP invokes this notification on the ULP ๐Ÿ˜ญ

The following might optionally be passed with the notification:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • data retrieval id: an identification used to retrieve unsent and unacknowledged data ๐Ÿชช
  • mode: indicates whether no part of the message never has been sent, or if at least part of it has been sent but it is not completely acknowledged ๐Ÿค”
  • cause code: indicates the reason for the failure, e.g., size too large, message life time expiration, etc. ๐Ÿงพ
  • context: optional information associated with this message (see Section 11.1.5) ๐Ÿง 

11.2.3. NETWORK STATUS CHANGE Notification ๐Ÿ“ก

when a destination transport address gets marked inactive (like when SCTP detects a failure) or marked active (like when SCTP detects a recovery), SCTP invokes this notification on the ULP ๐Ÿ”„

The following is passed with the notification:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • destination transport address: the destination transport address of the peer endpoint affected by the change ๐ŸŒธ
  • new-status: indicates the new status ๐Ÿšฆ

11.2.4. COMMUNICATION UP Notification ๐Ÿฅณ

this notification is used when SCTP becomes ready to send or receive user messages, or when a lost communication to an endpoint gets restored ๐Ÿ™Œ

Implementation Note: if the ASSOCIATE primitive is implemented as a blocking function call, the association parameters are returned as a result of the ASSOCIATE primitive itself ๐Ÿงฉ in that case, the COMMUNICATION UP notification is optional at the association initiator's side ๐Ÿซถ

The following is passed with the notification:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • status: indicates what type of event has occurred โœจ
  • destination transport address list: the complete set of transport addresses of the peer ๐Ÿ—บ๏ธ
  • outbound stream count: the maximum number of streams the ULP is allowed to use in this association ๐ŸŒŠ
  • inbound stream count: the number of streams the peer endpoint has requested with this association (this might not be the same number as 'outbound stream count') ๐Ÿ”ข

11.2.5. COMMUNICATION LOST Notification ๐Ÿ’”

when SCTP loses communication to an endpoint completely (e.g., via Heartbeats) or detects that the endpoint has performed an abort operation, it invokes this notification on the ULP ๐Ÿ˜ข

The following is passed with the notification:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • status: indicates what type of event has occurred; the status might show that a failure OR a normal termination event happened in response to a shutdown or abort request ๐Ÿฅ€

The following might be passed with the notification:

  • last-acked: the TSN last acked by that peer endpoint ๐Ÿ“ฎ
  • last-sent: the TSN last sent to that peer endpoint ๐Ÿ“ค
  • Upper Layer Abort Reason: the abort reason specified in case of a user-initiated abort ๐Ÿ›‘

11.2.6. COMMUNICATION ERROR Notification โš ๏ธ

when SCTP receives an ERROR chunk from its peer and decides to notify its ULP, it can invoke this notification on the ULP ๐Ÿ™ˆ

The following can be passed with the notification:

  • association id: your local handle to the SCTP association ๐ŸŽ€
  • error info: indicates the type of error, and optionally some additional info received through the ERROR chunk ๐Ÿงพ

11.2.7. RESTART Notification ๐Ÿ”

when SCTP detects that the peer has restarted, it might send this notification to its ULP ๐ŸŒ…

The following can be passed with the notification:

  • association id: your local handle to the SCTP association ๐ŸŽ€

11.2.8. SHUTDOWN COMPLETE Notification ๐Ÿ

when SCTP completes the shutdown procedures (Section 9.2), this notification is passed to the upper layer ๐ŸŽฌ

The following can be passed with the notification:

  • association id: your local handle to the SCTP association ๐ŸŽ€

12. Security Considerations ๐Ÿ”โœจ

12.1. Security Objectives

okay so SCTP is basically the reliable transport bestie for time-sensitive messages between two networked endpoints, like billing or signaling messages for telephony ๐Ÿ“žโœจ. Its security goals are:

  • availability of reliable and timely data transport services ๐Ÿ’–
  • integrity of the user-to-user information carried by SCTP ๐Ÿ”’

12.2. SCTP Responses to Potential Threats

ngl SCTP could end up in a lot of risky situations ๐Ÿ˜ญ. The operators running it need to look at their own setup and pick the right countermeasures for it, no shortcuts ๐Ÿ’….

Operators running SCTP might check out [RFC2196] for tips on securing their site ๐Ÿ›ก๏ธ.

12.2.1. Countering Insider Attacks

The principles from [RFC2196] might be applied to cut down the risk of insiders stealing info or sabotaging stuff ๐Ÿ•ต๏ธโ€โ™€๏ธ. This includes publishing security policies, controlling access at the physical, software, and network levels, and separating services ๐Ÿงฑ.

12.2.2. Protecting against Data Corruption in the Network

If the risk of undetected errors in datagrams from the lower-layer transport services feels too high ๐Ÿ˜ฌ, extra integrity protection is needed ๐Ÿ”. If that extra protection lived in the application layer, the SCTP header would still be open to deliberate integrity attacks ๐Ÿ™ˆ. The existing SCTP ways of catching replayed packets are considered good enough for normal operation โœ…, but when there's a real chance of deliberate attacks from a sophisticated adversary, stronger protections are needed to keep SCTP safe ๐Ÿ‘‘.

The SCTP Authentication extension SCTP-AUTH [RFC4895] MAY be used when the threat environment needs stronger integrity protections but doesn't need confidentiality ๐ŸŽ€.

12.2.3. Protecting Confidentiality

Most of the time, the confidentiality risk is about the signaling data payload, not the SCTP or lower-layer protocol overheads ๐Ÿง. If that's the case, encrypting only the SCTP user data might be worth considering ๐Ÿค”. Just like with the supplementary checksum service, user data encryption MAY be performed by the SCTP user application ๐ŸŒธ. [RFC6083] MAY be used for this ๐Ÿ’œ. Alternatively, the user application MAY use an implementation-specific API to ask for the IP Encapsulating Security Payload (ESP) [RFC4303] to be used for confidentiality and integrity ๐Ÿฆ‹.

Mobile users especially might need confidentiality that also masks IP addresses and ports ๐Ÿ“ฑ. In that case, ESP SHOULD be used instead of application-level confidentiality ๐ŸŒˆ. If ESP is protecting the confidentiality of SCTP traffic, an ESP cryptographic transform that includes cryptographic integrity protection MUST be used ๐Ÿšจ. The reason is simple: if there's a confidentiality threat, there's also a strong integrity threat ๐Ÿ’€.

No matter where confidentiality is provided, the Internet Key Exchange Protocol version 2 (IKEv2) [RFC7296] SHOULD be used for key management of ESP ๐Ÿ”‘.

Operators might look at [RFC4301] for more on the security services available at and right above the Internet Protocol layer ๐Ÿ“š.

12.2.4. Protecting against Blind Denial-of-Service Attacks

A blind attack is one where the attacker can't intercept or otherwise see the content of the data flows going to and from the target SCTP node ๐Ÿ™ˆ. Blind denial-of-service attacks can look like flooding, masquerade, or improper monopolization of services ๐Ÿ˜ค.

12.2.4.1. Flooding

The goal of flooding is to break service and cause incorrect behavior at target systems by exhausting resources, getting in the way of legitimate transactions, and exploiting buffer-related software bugs ๐Ÿ’ฅ. Flooding can aim at the SCTP node itself or at resources in the intervening IP Access Links or the Internet ๐ŸŒ. When those in-between entities are the target, the flooding shows up as loss of network services, possibly including a breach of any firewalls in place ๐Ÿงฑ๐Ÿ’”.

Generally, protection against flooding starts at the equipment design level ๐Ÿ› ๏ธ. That includes things like:

  • avoiding committing limited resources before deciding the request for service is legitimate ๐Ÿ™…โ€โ™€๏ธ
  • giving priority to finishing work already in progress over accepting new work ๐Ÿ
  • identifying and removing duplicate or stale queued requests for service ๐Ÿ—‘๏ธ
  • not responding to unexpected packets sent to non-unicast addresses ๐Ÿšซ

Network equipment is expected to be able to generate an alarm and a log when there's a suspicious jump in traffic ๐Ÿšจ. The log shares info like the identity of the incoming link and the source address(es) used, which helps the network or SCTP system operator take protective measures ๐Ÿ“. Procedures are expected to be in place so the operator can act on those alarms if a clear pattern of abuse shows up ๐Ÿ‘€.

The design of SCTP is resistant to flooding attacks, and it's honestly iconic for that ๐Ÿ‘‘. It uses a four-way startup handshake, a cookie to delay committing resources at the responding SCTP node until the handshake is done, and a Verification Tag to stop extraneous packets from getting into the flow of an established association ๐Ÿ’….

ESP might be useful for reducing the risk of certain kinds of denial-of-service attacks ๐Ÿฆ‹.

Support for the Host Name Address parameter has been removed from the protocol ๐Ÿ—‘๏ธ. Endpoints that receive INIT or INIT ACK chunks containing the Host Name Address parameter MUST send an ABORT chunk in response ๐Ÿ›‘ and MAY include an "Unresolvable Address" error cause ๐Ÿ™ƒ.

12.2.4.2. Blind Masquerade

Masquerade can be used to deny service in a few different ways ๐ŸŽญ:

  • by tying up resources at the target SCTP node that the impersonated node has limited access to ๐Ÿงถ. For example, the target node can, by policy, allow a maximum of one SCTP association with the impersonated SCTP node ๐Ÿšช. The masquerading attacker can try to set up an association pretending to come from the impersonated node, so the real node can't do it when it actually needs to ๐Ÿ˜ฉ.
  • by deliberately letting the impersonation get detected, which provokes countermeasures that lock the impersonated node out of the target SCTP node ๐Ÿ”’.
  • by interfering with an established association by inserting extraneous content, like a SHUTDOWN chunk ๐Ÿ›‘.

SCTP cuts down the risk of blind masquerade attacks through IP spoofing by using the four-way startup handshake ๐Ÿค. The initial exchange is memoryless, so no lockout mechanism gets triggered by blind masquerade attacks ๐Ÿ™Œ. Also, the packet carrying the INIT ACK chunk with the State Cookie goes back to the IP address that sent the packet with the INIT chunk ๐Ÿ“ฌ. So the attacker never receives the INIT ACK chunk with the State Cookie ๐Ÿ™…โ€โ™€๏ธ. SCTP protects against extraneous packets getting into an established association by using the Verification Tag ๐Ÿท๏ธ.

Logging received INIT chunks and abnormalities, like unexpected INIT ACK chunks, might be considered as a way to spot patterns of hostile activity ๐Ÿ”. But the usefulness of that logging has to be weighed against the extra SCTP startup processing it adds, which makes the SCTP node more vulnerable to flooding attacks ๐Ÿ˜ฌ. Logging is pointless unless there are operating procedures to review and analyze the logs on a routine basis ๐Ÿ“‹.

12.2.4.3. Improper Monopolization of Services

These attacks are done openly and legitimately by the attacker ๐Ÿ˜‡. They target fellow users of the target SCTP node, or shared resources between the attacker and the target node ๐Ÿ˜๏ธ. Possible attacks include opening a large number of associations between the attacker's node and the target, or transferring large volumes of information within a legitimately established association ๐Ÿ“ฆ.

Policy limits are expected to be placed on the number of associations per adjoining SCTP node ๐Ÿšง. SCTP user applications are expected to be able to detect large volumes of illegitimate or "no-op" messages within a given association, and then either log it or terminate the association, based on local policy ๐Ÿงน.

12.3. SCTP Interactions with Firewalls

Some firewalls really benefit if they can inspect just the first fragment of a fragmented SCTP packet and clearly tell whether it's an INIT chunk (for more, check out [RFC1858]) ๐Ÿ”ฅ. So we're stressing the requirements from Section 3.1: (1) an INIT chunk MUST NOT be bundled with any other chunk in a packet, and (2) a packet containing an INIT chunk MUST have a zero Verification Tag ๐Ÿ’–. The receiver of an INIT chunk MUST silently discard the INIT chunk and all further chunks if the INIT chunk is bundled with other chunks or if the packet has a non-zero Verification Tag ๐Ÿ™…โ€โ™€๏ธ.

12.4. Protection of Non-SCTP-capable Hosts

To give a non-SCTP-capable host the same level of protection against attacks as SCTP-capable ones, all SCTP implementations MUST implement the ICMP handling described in Section 10 ๐Ÿซถ.

When an SCTP implementation gets a packet with multiple control or DATA chunks, and processing that packet would mean sending multiple chunks back, the sender of the response chunk(s) MUST NOT send more than one packet containing chunks other than DATA chunks ๐Ÿšฆ. This keeps the network from getting hit with a packet burst in response to a single packet ๐ŸŒŠ. If bundling is supported, multiple response chunks that fit into a single packet MAY be bundled together into one response packet ๐ŸŽ. If bundling isn't supported, the sender MUST NOT send more than one response chunk and MUST discard all other responses ๐Ÿ—‘๏ธ. Note that this rule doesn't apply to a SACK chunk, since a SACK chunk is itself a response to DATA chunks, and it doesn't need a response of more DATA chunks ๐Ÿ’œ.

An SCTP implementation MUST abort the association if it receives a SACK chunk acknowledging a TSN that hasn't been sent ๐Ÿ›‘.

If an SCTP implementation gets an INIT chunk that would need a large packet in response, because it includes multiple "Unrecognized Parameter" parameters, it MAY (at its discretion) choose to omit some or all of the "Unrecognized Parameter" parameters to shrink the INIT ACK chunk ๐Ÿช„. Because of the size of the State Cookie parameter combined with the number of addresses a receiver of an INIT chunk tells a peer about, the INIT ACK chunk can always end up bigger than the original INIT chunk ๐Ÿ“. An SCTP implementation SHOULD try to make the INIT ACK chunk as small as possible to reduce the chance of byte amplification attacks ๐ŸงŠ.


13. Network Management Considerations

The MIB module for SCTP defined in [RFC3873] applies to the version of the protocol specified in this document ๐Ÿ“Š.

This section details a set of parameters that are expected to be in the TCB for an implementation ๐Ÿง . It's just for illustration ๐ŸŽจ and isn't considered requirements on an implementation, nor is it an exhaustive list of all parameters inside an SCTP TCB ๐Ÿ™ƒ. Each implementation might need its own extra parameters for optimization โšก.

14.1. Parameters Necessary for the SCTP Instance โœจ

  • Associations: okay so this is your list of current associations plus the mapping to the data consumers for each one ๐Ÿ“‹ It can be a hash table or some other implementation-dependent structure, whatever fits. The data consumers might be process identification info, like file descriptors, a named pipe pointer, or table pointers, depending on how SCTP is implemented ๐Ÿงš
  • Secret Key: the secret key this endpoint uses to compute the MAC ๐Ÿ” This SHOULD be a cryptographic quality random number with a sufficient length. [RFC4086] can be super helpful when picking the key ๐Ÿ’œ
  • Address List: the list of IP addresses this instance has bound. It gets passed to your peer(s) in INIT and INIT ACK chunks ๐Ÿฆ‹
  • SCTP Port: the local SCTP port number the endpoint is bound to ๐ŸŽ€

14.2. Parameters Necessary per Association (i.e., the TCB) ๐Ÿ’…

  • Peer Verification Tag: the tag value that gets sent in every packet, and it's received in the INIT or INIT ACK chunk ๐ŸŒธ
  • My Verification Tag: the tag expected in every inbound packet, and it's sent in the INIT or INIT ACK chunk ๐ŸŒธ
  • State: COOKIE-WAIT, COOKIE-ECHOED, ESTABLISHED, SHUTDOWN-PENDING, SHUTDOWN-SENT, SHUTDOWN-RECEIVED, SHUTDOWN-ACK-SENT ๐Ÿ‘‘
Note: there's no "CLOSED" state in this list, because if an association is "CLOSED", its TCB SHOULD be removed. Bye bye, TCB ๐Ÿ‘‹
  • Peer Transport Address List: a list of SCTP transport addresses the peer is bound to. This comes from the INIT or INIT ACK chunk and it's how an inbound packet gets matched to its association. Normally this is hashed or keyed so the TCB is quick to look up and access ๐Ÿ”
  • Primary Path: the current primary destination transport address of the peer endpoint. It might also specify a source transport address on this endpoint ๐Ÿ›ค๏ธ
  • Overall Error Count: the overall association error count ๐Ÿ“‰
  • Overall Error Threshold: the threshold for this association. If the Overall Error Count reaches it, the association gets torn down ๐Ÿ’€
  • Peer Rwnd: the current calculated value of the peer's rwnd ๐ŸชŸ
  • Next TSN: the next TSN to be assigned to a new DATA chunk. It's sent to the peer in the INIT or INIT ACK chunk, and it goes up each time a DATA chunk gets assigned a TSN (usually right before transmit or during fragmentation) ๐Ÿ”ข
  • Last Rcvd TSN: the last TSN received in sequence. It starts out by taking the peer's Initial TSN (received in the INIT or INIT ACK chunk) and subtracting one from it ๐ŸŽฏ
  • Mapping Array: an array of bits or bytes showing which out-of-order TSNs have been received, relative to the Last Rcvd TSN. If there are no gaps (no out-of-order packets have arrived), this array is all zeros. It might be a circular buffer or a bit array ๐Ÿงฉ
  • Ack State: a flag that says whether the next received packet gets answered with a SACK chunk. It starts at 0, goes up with each packet received, and once it hits 2 or more, a SACK chunk goes out and the value resets to 0. Note: this is only used when no DATA chunks arrive out of order. When they do, SACK chunks are not delayed (see Section 6) ๐Ÿ™ˆ
  • Inbound Streams: an array of structures tracking the inbound streams. It normally includes the next sequence number expected and possibly the stream number ๐Ÿ“ฅ
  • Outbound Streams: an array of structures tracking the outbound streams. It normally includes the next sequence number to be sent on the stream ๐Ÿ“ค
  • Reasm Queue: a reassembly queue ๐Ÿงต
  • Receive Buffer: a buffer that stores received user data that hasn't been delivered to the upper layer yet ๐Ÿ“ฆ
  • Local Transport Address List: the list of local IP addresses bound into this association ๐Ÿ 
  • Association Maximum DATA Chunk Size: the smallest Path Maximum DATA Chunk Size across all destination addresses ๐Ÿ“

14.3. Per Transport Address Data ๐Ÿฆ‹

For each destination transport address in the peer's address list (the one derived from the INIT or INIT ACK chunk), a bunch of data elements need to be maintained, including:

  • Error Count: the current error count for this destination ๐Ÿ“Š
  • Error Threshold: the current error threshold for this destination. In other words, the value that marks the destination down if the error count reaches it โฌ‡๏ธ
  • cwnd: the current congestion window ๐ŸชŸ
  • ssthresh: the current ssthresh value ๐ŸŽš๏ธ
  • RTO: the current retransmission timeout value โฑ๏ธ
  • SRTT: the current smoothed round-trip time ๐ŸŒŠ
  • RTTVAR: the current RTT variation ๐Ÿ“ˆ
  • partial bytes acked: the tracking method for increasing cwnd when you're in congestion avoidance mode (see Section 7.2.2) ๐Ÿงฎ
  • state: the current state of this destination, i.e., DOWN, UP, ALLOW-HEARTBEAT, NO-HEARTBEAT, etc. ๐Ÿšฆ
  • PMTU: the current known PMTU ๐Ÿ“
  • PMDCS: the current known PMDCS ๐Ÿ“
  • Per Destination Timer: a timer used by each destination โฐ
  • RTO-Pending: a flag that tracks whether one of the DATA chunks sent to this address is currently being used to compute an RTT. If this flag is 0, the next DATA chunk sent to this destination is expected to be used for the RTT computation, and this flag is expected to be set. Every time the RTT calculation completes (aka the DATA chunk gets acknowledged), clear this flag ๐Ÿ
  • last-time: the time this destination was last sent to. It can be used to figure out whether a HEARTBEAT chunk needs to be sent ๐Ÿ’“

14.4. General Parameters Needed ๐ŸŒˆ

  • Out Queue: a queue of outbound DATA chunks ๐Ÿ“ฎ
  • In Queue: a queue of inbound DATA chunks ๐Ÿ“ฌ

15. IANA Considerations โœจ

okay bestie, IANA is the one keeping track of all the registries ๐Ÿ—‚๏ธ, and this document defines five of them ๐Ÿ’…. Registries can grow in these ways:

  • through definition of additional chunk types,
  • through definition of additional chunk flags,
  • through definition of additional parameter types,
  • through definition of additional cause codes within ERROR chunks, or
  • through definition of additional payload protocol identifiers.

IANA has already done a bunch of updates for those five registries, and it's giving organized ๐ŸŽ€:

  • In the Chunk Types registry, IANA swapped the registry reference to [RFC4960] and [RFC6096] for a reference to this document ๐Ÿฆ‹.

In the Notes section too, the reference to Section 3.2 of [RFC6096] got updated to point at Section 15.2 of this document โœจ.

And every reference to [RFC4960] got replaced with this document for these chunk types:

  • Payload Data (DATA)
  • Initiation (INIT)
  • Initiation Acknowledgement (INIT ACK)
  • Selective Acknowledgement (SACK)
  • Heartbeat Request (HEARTBEAT)
  • Heartbeat Acknowledgement (HEARTBEAT ACK)
  • Abort (ABORT)
  • Shutdown (SHUTDOWN)
  • Shutdown Acknowledgement (SHUTDOWN ACK)
  • Operation Error (ERROR)
  • State Cookie (COOKIE ECHO)
  • Cookie Acknowledgement (COOKIE ACK)
  • Reserved for Explicit Congestion Notification Echo (ECNE)
  • Reserved for Congestion Window Reduced (CWR)
  • Shutdown Complete (SHUTDOWN COMPLETE)
  • Reserved for IETF-defined Chunk Extensions
  • In the Chunk Parameter Types registry, IANA swapped the registry reference to [RFC4960] for a reference to this document ๐ŸŒธ.

IANA also renamed the Unrecognized Parameters chunk parameter type to Unrecognized Parameter in that registry, so the name is now a bit tidier ๐Ÿซถ.

Every reference to [RFC4960] got replaced with this document for these chunk parameter types:

  • Heartbeat Info
  • IPv4 Address
  • IPv6 Address
  • State Cookie
  • Unrecognized Parameter
  • Cookie Preservative
  • Host Name Address
  • Supported Address Types

IANA added a reference to this document for this one, which is new to the list:

  • Reserved for ECN Capable (0x8000)

And the value 65535 is now reserved for IETF-defined extensions ๐Ÿงš.

  • In the Chunk Flags registry, IANA replaced the registry reference to [RFC6096] with a reference to this document ๐Ÿ’œ.

Every reference to [RFC4960] got replaced with this document for these DATA chunk flags:

  • E bit
  • B bit
  • U bit

The reference to [RFC7053] got replaced with this document for this DATA chunk flag:

  • I bit

The reference to [RFC4960] got replaced with this document for this ABORT chunk flag:

  • T bit

And the reference to [RFC4960] got replaced with this document for this SHUTDOWN COMPLETE chunk flag:

  • T bit
  • In the Error Cause Codes registry, IANA swapped the registry reference to [RFC4960] for a reference to this document ๐Ÿ“.

IANA also renamed two causes, which is a whole glow-up ๐Ÿ’–: User Initiated Abort is now User-Initiated Abort, and Stale Cookie Error is now Stale Cookie.

Every reference to [RFC4960] got replaced with this document for these cause codes:

  • Invalid Stream Identifier
  • Missing Mandatory Parameter
  • Stale Cookie
  • Out of Resource
  • Unresolvable Address
  • Unrecognized Chunk Type
  • Invalid Mandatory Parameter
  • Unrecognized Parameters
  • No User Data
  • Cookie Received While Shutting Down
  • Restart of an Association with New Addresses

IANA also replaced each reference to [RFC4460] with a reference to this document for these cause codes:

  • User-Initiated Abort
  • Protocol Violation
  • In the SCTP Payload Protocol Identifiers registry, IANA swapped the registry reference to [RFC4960] for a reference to this document ๐ŸŒˆ.

The reference to [RFC4960] got replaced with this document for this SCTP payload protocol identifier:

  • Reserved by SCTP

SCTP needs the IANA Port Numbers registry to be opened up for SCTP port registrations, and Section 15.6 explains how ๐Ÿ”ฅ. An IESG-appointed Expert Reviewer helps IANA evaluate SCTP port allocation requests ๐Ÿ‘‘.

In the Service Name and Transport Protocol Port Number Registry, IANA replaced each reference to [RFC4960] with a reference to this document for these SCTP port numbers:

  • 9 (discard)
  • 20 (ftp-data)
  • 21 (ftp)
  • 22 (ssh)
  • 80 (http)
  • 179 (bgp)
  • 443 (https)

Over in the Hypertext Transfer Protocol (HTTP) Digest Algorithm Values registry, IANA swapped the reference to Appendix B of [RFC4960] for a reference to Appendix A of this document ๐Ÿฆ‹.

In the ONC RPC Netids (Standards Action) registry, IANA replaced each reference to [RFC4960] with a reference to this document for these netids:

  • sctp
  • sctp6

In the IPFIX Information Elements registry, IANA replaced each reference to [RFC4960] with a reference to this document for these elements with the name:

  • sourceTransportPort
  • destinationTransportPort
  • collectorTransportPort
  • exporterTransportPort
  • postNAPTSourceTransportPort
  • postNAPTDestinationTransportPort

15.1. IETF-Defined Chunk Extension

Okay, so new chunk type codes are assigned through an IETF Review action, as defined in [RFC8126] ๐Ÿ“œ. Documentation for a new chunk MUST contain the following information:

  • a) A long and short name for the new chunk type.
  • b) A detailed description of the structure of the chunk, which MUST conform to the basic structure defined in Section 3.2.
  • c) A detailed definition and description of intended use of each field within the chunk, including the chunk flags if any. Defined chunk flags will be used as initial entries in the chunk flags table for the new chunk type.
  • d) A detailed procedural description of the use of the new chunk type within the operation of the protocol.

The last chunk type (255) is reserved for future extension if necessary ๐ŸŒŸ.

For each new chunk type, IANA creates a registration table for the chunk flags of that type ๐Ÿ’œ. The procedure for registering particular chunk flags is described in Section 15.2.

15.2. IETF-Defined Chunk Flags Registration

New chunk flags are assigned through an RFC Required action, as defined in [RFC8126] ๐ŸŽ€. Documentation for the chunk flags MUST contain the following information:

  • a) A name for the new chunk flag.
  • b) A detailed procedural description of the use of the new chunk flag within the operation of the protocol. It MUST be considered that implementations not supporting the flag will send 0 on transmit and just ignore it on receipt.

IANA selects a chunk flags value ๐Ÿ”ฅ. This MUST be one of 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, or 0x80, which MUST be unique within the chunk flag values for the specific chunk type.

15.3. IETF-Defined Chunk Parameter Extension

New chunk parameter type codes are assigned through an IETF Review action, as defined in [RFC8126] ๐Ÿงš. Documentation of the chunk parameter MUST contain the following information:

  • a) Name of the parameter type.
  • b) Detailed description of the structure of the parameter field. This structure MUST conform to the general Type-Length-Value format described in Section 3.2.1.
  • c) Detailed definition of each component of the parameter value.
  • d) Detailed description of the intended use of this parameter type and an indication of whether and under what circumstances multiple instances of this parameter type can be found within the same chunk.
  • e) Each parameter type MUST be unique across all chunks.

15.4. IETF-Defined Additional Error Causes

Additional cause codes can be allocated through a Specification Required action, as defined in [RFC8126] ๐Ÿ’…. Provided documentation MUST include the following information:

  • a) Name of the error condition.
  • b) Detailed description of the conditions under which an SCTP endpoint issues an ERROR (or ABORT) chunk with this cause code.
  • c) Expected action by the SCTP endpoint that receives an ERROR (or ABORT) chunk containing this cause code.
  • d) Detailed description of the structure and content of data fields that accompany this cause code.

The initial word (32 bits) of a cause code parameter MUST conform to the format shown in Section 3.3.10, which means:

  • first 2 bytes contain the cause code value
  • last 2 bytes contain the length of the error cause

15.5. Payload Protocol Identifiers

Payload protocol identifiers are assigned using the First Come First Served policy, as defined in [RFC8126] ๐ŸŒธ.

Except for value 0, which is reserved to indicate an unspecified payload protocol identifier in a DATA chunk, an SCTP implementation will not be responsible for standardizing or verifying any payload protocol identifiers ๐Ÿซถ. An SCTP implementation simply receives the identifier from the upper layer and carries it with the corresponding payload data.

The upper layer, which is the SCTP user, SHOULD standardize any specific protocol identifier with IANA if it is so desired ๐ŸŒˆ. The use of any specific payload protocol identifier is out of the scope of this specification.

15.6. Port Numbers Registry

SCTP services can use contact port numbers to provide service to unknown callers, just like TCP and UDP do ๐Ÿฆ‹. An IESG-appointed Expert Reviewer supports IANA in evaluating SCTP port allocation requests, following the procedure defined in [RFC8126]. The details of this process are defined in [RFC6335] โœจ.

16. Suggested SCTP Protocol Parameter Values

The following protocol parameters are RECOMMENDED ๐Ÿ’–:

RTO.Initial:  1 second
RTO.Min:  1 second
RTO.Max:  60 seconds
Max.Burst:  4
RTO.Alpha:  1/8
RTO.Beta:  1/4
Valid.Cookie.Life:  60 seconds
Association.Max.Retrans:  10 attempts
Path.Max.Retrans:  5 attempts (per destination address)
Max.Init.Retransmits:  8 attempts
HB.interval:  30 seconds
HB.Max.Burst:  1
SACK.Delay:  200 milliseconds

Implementation Note: The SCTP implementation can let the ULP customize some of these protocol parameters, see Section 11 ๐ŸŽ€.

RTO.Min SHOULD be set as described above in this section ๐Ÿ”ฅ.

17. References

17.1. Normative References โœจ

Okay bestie, these are the docs SCTP straight up needs you to read first ๐Ÿ“š no cap, every single one is listed below exactly as it appears in the spec:

[ITU.V42.1994]
           International Telecommunications Union, "Error-correcting
           Procedures for DCEs Using Asynchronous-to-Synchronous
           Conversion", ITU-T Recommendation V.42, 1994.

[RFC1122]  Braden, R., Ed., "Requirements for Internet Hosts -
           Communication Layers", STD 3, RFC 1122,
           DOI 10.17487/RFC1122, October 1989,
           <https://www.rfc-editor.org/info/rfc1122>.

[RFC1123]  Braden, R., Ed., "Requirements for Internet Hosts -
           Application and Support", STD 3, RFC 1123,
           DOI 10.17487/RFC1123, October 1989,
           <https://www.rfc-editor.org/info/rfc1123>.

[RFC1191]  Mogul, J. and S. Deering, "Path MTU discovery", RFC 1191,
           DOI 10.17487/RFC1191, November 1990,
           <https://www.rfc-editor.org/info/rfc1191>.

[RFC1982]  Elz, R. and R. Bush, "Serial Number Arithmetic", RFC 1982,
           DOI 10.17487/RFC1982, August 1996,
           <https://www.rfc-editor.org/info/rfc1982>.

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119,
           DOI 10.17487/RFC2119, March 1997,
           <https://www.rfc-editor.org/info/rfc2119>.

[RFC4291]  Hinden, R. and S. Deering, "IP Version 6 Addressing
           Architecture", RFC 4291, DOI 10.17487/RFC4291, February
           2006, <https://www.rfc-editor.org/info/rfc4291>.

[RFC4303]  Kent, S., "IP Encapsulating Security Payload (ESP)",
           RFC 4303, DOI 10.17487/RFC4303, December 2005,
           <https://www.rfc-editor.org/info/rfc4303>.

[RFC4895]  Tuexen, M., Stewart, R., Lei, P., and E. Rescorla,
           "Authenticated Chunks for the Stream Control Transmission
           Protocol (SCTP)", RFC 4895, DOI 10.17487/RFC4895, August
           2007, <https://www.rfc-editor.org/info/rfc4895>.

[RFC5681]  Allman, M., Paxson, V., and E. Blanton, "TCP Congestion
           Control", RFC 5681, DOI 10.17487/RFC5681, September 2009,
           <https://www.rfc-editor.org/info/rfc5681>.

[RFC6335]  Cotton, M., Eggert, L., Touch, J., Westerlund, M., and S.
           Cheshire, "Internet Assigned Numbers Authority (IANA)
           Procedures for the Management of the Service Name and
           Transport Protocol Port Number Registry", BCP 165,
           RFC 6335, DOI 10.17487/RFC6335, August 2011,
           <https://www.rfc-editor.org/info/rfc6335>.

[RFC6083]  Tuexen, M., Seggelmann, R., and E. Rescorla, "Datagram
           Transport Layer Security (DTLS) for Stream Control
           Transmission Protocol (SCTP)", RFC 6083,
           DOI 10.17487/RFC6083, January 2011,
           <https://www.rfc-editor.org/info/rfc6083>.

[RFC7296]  Kaufman, C., Hoffman, P., Nir, Y., Eronen, P., and T.
           Kivinen, "Internet Key Exchange Protocol Version 2
           (IKEv2)", STD 79, RFC 7296, DOI 10.17487/RFC7296, October
           2014, <https://www.rfc-editor.org/info/rfc7296>.

[RFC8126]  Cotton, M., Leiba, B., and T. Narten, "Guidelines for
           Writing an IANA Considerations Section in RFCs", BCP 26,
           RFC 8126, DOI 10.17487/RFC8126, June 2017,
           <https://www.rfc-editor.org/info/rfc8126>.

[RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
           2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
           May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8200]  Deering, S. and R. Hinden, "Internet Protocol, Version 6
           (IPv6) Specification", STD 86, RFC 8200,
           DOI 10.17487/RFC8200, July 2017,
           <https://www.rfc-editor.org/info/rfc8200>.

[RFC8201]  McCann, J., Deering, S., Mogul, J., and R. Hinden, Ed.,
           "Path MTU Discovery for IP version 6", STD 87, RFC 8201,
           DOI 10.17487/RFC8201, July 2017,
           <https://www.rfc-editor.org/info/rfc8201>.

[RFC8899]  Fairhurst, G., Jones, T., Tรผxen, M., Rรผngeler, I., and T.
           Vรถlker, "Packetization Layer Path MTU Discovery for
           Datagram Transports", RFC 8899, DOI 10.17487/RFC8899,
           September 2020, <https://www.rfc-editor.org/info/rfc8899>.

17.2. Informative References ๐Ÿ“–

These ones are the background reading ๐ŸŒˆ the spec points to for extra context, all listed exactly as written:

[FALL96]   Fall, K. and S. Floyd, "Simulation-based Comparisons of
           Tahoe, Reno, and SACK TCP", SIGCOM 99, V. 26, N. 3, pp
           5-21, July 1996.

[SAVAGE99] Savage, S., Cardwell, N., Wetherall, D., and T. Anderson,
           "TCP Congestion Control with a Misbehaving Receiver", ACM
           Computer Communications Review 29(5), October 1999.

[ALLMAN99] Allman, M. and V. Paxson, "On Estimating End-to-End
           Network Path Properties", SIGCOM 99, October 1999.

[WILLIAMS93]
           Williams, R., "A PAINLESS GUIDE TO CRC ERROR DETECTION
           ALGORITHMS", SIGCOM 99, August 1993,
           <https://archive.org/stream/PainlessCRC/crc_v3.txt>.

[RFC0768]  Postel, J., "User Datagram Protocol", STD 6, RFC 768,
           DOI 10.17487/RFC0768, August 1980,
           <https://www.rfc-editor.org/info/rfc768>.

[RFC0793]  Postel, J., "Transmission Control Protocol", STD 7,
           RFC 793, DOI 10.17487/RFC0793, September 1981,
           <https://www.rfc-editor.org/info/rfc793>.

[RFC1858]  Ziemba, G., Reed, D., and P. Traina, "Security
           Considerations for IP Fragment Filtering", RFC 1858,
           DOI 10.17487/RFC1858, October 1995,
           <https://www.rfc-editor.org/info/rfc1858>.

[RFC2104]  Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
           Hashing for Message Authentication", RFC 2104,
           DOI 10.17487/RFC2104, February 1997,
           <https://www.rfc-editor.org/info/rfc2104>.

[RFC2196]  Fraser, B., "Site Security Handbook", FYI 8, RFC 2196,
           DOI 10.17487/RFC2196, September 1997,
           <https://www.rfc-editor.org/info/rfc2196>.

[RFC2522]  Karn, P. and W. Simpson, "Photuris: Session-Key Management
           Protocol", RFC 2522, DOI 10.17487/RFC2522, March 1999,
           <https://www.rfc-editor.org/info/rfc2522>.

[RFC2960]  Stewart, R., Xie, Q., Morneault, K., Sharp, C.,
           Schwarzbauer, H., Taylor, T., Rytina, I., Kalla, M.,
           Zhang, L., and V. Paxson, "Stream Control Transmission
           Protocol", RFC 2960, DOI 10.17487/RFC2960, October 2000,
           <https://www.rfc-editor.org/info/rfc2960>.

[RFC3465]  Allman, M., "TCP Congestion Control with Appropriate Byte
           Counting (ABC)", RFC 3465, DOI 10.17487/RFC3465, February
           2003, <https://www.rfc-editor.org/info/rfc3465>.

[RFC3873]  Pastor, J. and M. Belinchon, "Stream Control Transmission
           Protocol (SCTP) Management Information Base (MIB)",
           RFC 3873, DOI 10.17487/RFC3873, September 2004,
           <https://www.rfc-editor.org/info/rfc3873>.

[RFC4086]  Eastlake 3rd, D., Schiller, J., and S. Crocker,
           "Randomness Requirements for Security", BCP 106, RFC 4086,
           DOI 10.17487/RFC4086, June 2005,
           <https://www.rfc-editor.org/info/rfc4086>.

[RFC4301]  Kent, S. and K. Seo, "Security Architecture for the
           Internet Protocol", RFC 4301, DOI 10.17487/RFC4301,
           December 2005, <https://www.rfc-editor.org/info/rfc4301>.

[RFC4460]  Stewart, R., Arias-Rodriguez, I., Poon, K., Caro, A., and
           M. Tuexen, "Stream Control Transmission Protocol (SCTP)
           Specification Errata and Issues", RFC 4460,
           DOI 10.17487/RFC4460, April 2006,
           <https://www.rfc-editor.org/info/rfc4460>.

[RFC4960]  Stewart, R., Ed., "Stream Control Transmission Protocol",
           RFC 4960, DOI 10.17487/RFC4960, September 2007,
           <https://www.rfc-editor.org/info/rfc4960>.

[RFC6096]  Tuexen, M. and R. Stewart, "Stream Control Transmission
           Protocol (SCTP) Chunk Flags Registration", RFC 6096,
           DOI 10.17487/RFC6096, January 2011,
           <https://www.rfc-editor.org/info/rfc6096>.

[RFC6458]  Stewart, R., Tuexen, M., Poon, K., Lei, P., and V.
           Yasevich, "Sockets API Extensions for the Stream Control
           Transmission Protocol (SCTP)", RFC 6458,
           DOI 10.17487/RFC6458, December 2011,
           <https://www.rfc-editor.org/info/rfc6458>.

[RFC6951]  Tuexen, M. and R. Stewart, "UDP Encapsulation of Stream
           Control Transmission Protocol (SCTP) Packets for End-Host
           to End-Host Communication", RFC 6951,
           DOI 10.17487/RFC6951, May 2013,
           <https://www.rfc-editor.org/info/rfc6951>.

[RFC7053]  Tuexen, M., Ruengeler, I., and R. Stewart, "SACK-
           IMMEDIATELY Extension for the Stream Control Transmission
           Protocol", RFC 7053, DOI 10.17487/RFC7053, November 2013,
           <https://www.rfc-editor.org/info/rfc7053>.

[RFC8260]  Stewart, R., Tuexen, M., Loreto, S., and R. Seggelmann,
           "Stream Schedulers and User Message Interleaving for the
           Stream Control Transmission Protocol", RFC 8260,
           DOI 10.17487/RFC8260, November 2017,
           <https://www.rfc-editor.org/info/rfc8260>.

[RFC8261]  Tuexen, M., Stewart, R., Jesup, R., and S. Loreto,
           "Datagram Transport Layer Security (DTLS) Encapsulation of
           SCTP Packets", RFC 8261, DOI 10.17487/RFC8261, November
           2017, <https://www.rfc-editor.org/info/rfc8261>.

[RFC8540]  Stewart, R., Tuexen, M., and M. Proshin, "Stream Control
           Transmission Protocol: Errata and Issues in RFC 4960",
           RFC 8540, DOI 10.17487/RFC8540, February 2019,
           <https://www.rfc-editor.org/info/rfc8540>.

Appendix A. CRC32c Checksum Calculation ๐Ÿ”ฅ

Okay so a "reflected value" is one that's flipped from the normal bit order of the machine ๐Ÿ™ƒ. The 32-bit CRC (Cyclic Redundancy Check) gets calculated the way CRC32c describes, using the polynomial code 0x11EDC6F41 (Castagnoli93), or x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+x^11+x^10+x^9+x^8+x^6+x^0. It's kinda like the ETHERNET CRC [ITU.V42.1994] but tweaked for transport-level usage ๐Ÿ’….

CRC computation is all about polynomial division ๐Ÿงฎ. A message bit-string M gets turned into a polynomial, M(X), and then the CRC is worked out from M(X) using polynomial arithmetic ๐Ÿฆ‹.

When CRCs are used at the link layer, the polynomial is derived from on-the-wire bit ordering: the first bit on the wire is the high-order coefficient. But SCTP is a transport-level protocol, so it can't know the actual serial-media bit ordering ๐Ÿ™ˆ. Plus, different links in the path between SCTP endpoints can use different link-level bit orders ๐Ÿ˜ญ.

So a convention gets established for mapping SCTP transport messages to polynomials for CRC computation ๐ŸŽ€. The bit-ordering is that bytes are taken most-significant first, but within each byte, bits are taken least-significant first. The first byte of the message provides the eight highest coefficients. Within each byte, the least-significant SCTP bit gives the most-significant polynomial coefficient in that byte, and the most-significant SCTP bit is the least-significant polynomial coefficient in that byte. (This bit ordering is sometimes called 'mirrored' or 'reflected' [WILLIAMS93].) CRC polynomials are then transformed back into SCTP transport-level byte values, using a consistent mapping ๐ŸŒธ.

Heads up, bestie: that bit-ordering trick is the whole game here ๐Ÿ‘‘

The SCTP transport-level CRC value can be calculated as follows:

  • okay so first things first ๐Ÿ’… the CRC input data gets assigned to a byte stream, numbered from 0 to N-1 ๐Ÿงš
  • the transport-level byte stream gets mapped to a polynomial value ๐Ÿฆ‹ an N-byte PDU with j bytes numbered 0 to N-1 is treated as the coefficients of a polynomial M(x) of order 8*N-1. bit 0 of byte j is the coefficient of x^(8*(N-j)-8), and bit 7 of byte j is the coefficient of x^(8*(N-j)-1) ๐ŸŒธ
  • the CRC remainder register starts with all 1s, and the CRC gets computed with an algorithm that multiplies by x^32 and divides by the CRC polynomial simultaneously โœจ so efficient, ngl
  • the polynomial gets multiplied by x^32 and divided by G(x), the generator polynomial, which leaves a remainder R(x) of degree less than or equal to 31 ๐Ÿ”ฅ
  • the coefficients of R(x) get treated as a 32-bit sequence ๐Ÿ’œ
  • the bit sequence gets complemented, and the result is the CRC polynomial ๐Ÿ’€ plot twist
  • the CRC polynomial gets mapped back into SCTP transport-level bytes ๐Ÿ“ the coefficient of x^31 gives bit 7 of SCTP byte 0, and the coefficient of x^24 gives bit 0 of byte 0. the coefficient of x^7 gives bit 7 of byte 3, and the coefficient of x^0 gives bit 0 of byte 3. that resulting 4-byte transport-level sequence is the 32-bit SCTP checksum value ๐Ÿ‘‘

Implementation Note: this part is a whole vibe ๐ŸŒˆ standards docs, textbooks, and vendor literature on CRCs often follow an alternative formulation, where the register used to hold the remainder of the long-division algorithm starts at zero instead of all ones, and the first 32 bits of the message get complemented instead. the long-division algorithm in our formulation is specified so that the initial multiplication by 2^32 and the long-division get combined into one simultaneous operation. for those algorithms, and for messages longer than 64 bits, the two specifications are precisely equivalent ๐Ÿซถ that equivalence is the whole intent of this doc

implementors of SCTP, listen up ๐Ÿšจ both specifications show up in the literature, sometimes with no restriction on the long-division algorithm. the reason this doc picked its formulation is to allow non-SCTP usage, where the same CRC algorithm can be used to protect messages shorter than 64 bits ๐ŸŽ€

there can be a computational advantage in validating the association against the Verification Tag before doing a checksum ๐Ÿง  since invalid tags will result in the same action as a bad checksum in most cases. the exceptions for this trick are packets containing INIT chunks and some SHUTDOWN-COMPLETE chunks, as well as stale COOKIE ECHO chunks. these special-case exchanges are small packets, so they'll minimize the effect of the checksum calculation ๐Ÿ™ˆ

the following non-normative sample code is taken from an open-source CRC generator [WILLIAMS93] ๐Ÿ’– it uses the "mirroring" technique and yields a lookup table for SCTP CRC32c with 256 entries, each 32 bits wide. it's neither especially slow nor especially fast, as software table-lookup CRCs go, but it has the advantage of working on both big-endian and little-endian CPUs, using the same (host-order) lookup tables, and using only the predefined ntohl() and htonl() operations ๐Ÿฆ‹ the code is somewhat modified from [WILLIAMS93] to ensure portability between big-endian and little-endian architectures, use fixed-sized types to allow portability between 32-bit and 64-bit platforms, and use general C code improvements. (psst, if the byte endian-ness of the target architecture is known to be little endian, the final bit-reversal and byte-reversal steps can be folded into a single operation ๐Ÿงš)

<CODE BEGINS>
/****************************************************************/
/* Note: The definitions for Ross Williams's table generator    */
/* would be TB_WIDTH=4, TB_POLY=0x1EDC6F41, TB_REVER=TRUE.      */
/* For Mr. Williams's direct calculation code, use the settings */
/* cm_width=32, cm_poly=0x1EDC6F41, cm_init=0xFFFFFFFF,         */
/* cm_refin=TRUE, cm_refot=TRUE, cm_xorot=0x00000000.           */
/****************************************************************/

/* Example of the crc table file */
#ifndef __crc32cr_h__
#define __crc32cr_h__

#define CRC32C_POLY 0x1EDC6F41UL
#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF])

uint32_t crc_c[256] = {
  0x00000000UL, 0xF26B8303UL, 0xE13B70F7UL, 0x1350F3F4UL,
  0xC79A971FUL, 0x35F1141CUL, 0x26A1E7E8UL, 0xD4CA64EBUL,
  0x8AD958CFUL, 0x78B2DBCCUL, 0x6BE22838UL, 0x9989AB3BUL,
  0x4D43CFD0UL, 0xBF284CD3UL, 0xAC78BF27UL, 0x5E133C24UL,
  0x105EC76FUL, 0xE235446CUL, 0xF165B798UL, 0x030E349BUL,
  0xD7C45070UL, 0x25AFD373UL, 0x36FF2087UL, 0xC494A384UL,
  0x9A879FA0UL, 0x68EC1CA3UL, 0x7BBCEF57UL, 0x89D76C54UL,
  0x5D1D08BFUL, 0xAF768BBCUL, 0xBC267848UL, 0x4E4DFB4BUL,
  0x20BD8EDEUL, 0xD2D60DDDUL, 0xC186FE29UL, 0x33ED7D2AUL,
  0xE72719C1UL, 0x154C9AC2UL, 0x061C6936UL, 0xF477EA35UL,
  0xAA64D611UL, 0x580F5512UL, 0x4B5FA6E6UL, 0xB93425E5UL,
  0x6DFE410EUL, 0x9F95C20DUL, 0x8CC531F9UL, 0x7EAEB2FAUL,
  0x30E349B1UL, 0xC288CAB2UL, 0xD1D83946UL, 0x23B3BA45UL,
  0xF779DEAEUL, 0x05125DADUL, 0x1642AE59UL, 0xE4292D5AUL,
  0xBA3A117EUL, 0x4851927DUL, 0x5B016189UL, 0xA96AE28AUL,
  0x7DA08661UL, 0x8FCB0562UL, 0x9C9BF696UL, 0x6EF07595UL,
  0x417B1DBCUL, 0xB3109EBFUL, 0xA0406D4BUL, 0x522BEE48UL,
  0x86E18AA3UL, 0x748A09A0UL, 0x67DAFA54UL, 0x95B17957UL,
  0xCBA24573UL, 0x39C9C670UL, 0x2A993584UL, 0xD8F2B687UL,
  0x0C38D26CUL, 0xFE53516FUL, 0xED03A29BUL, 0x1F682198UL,
  0x5125DAD3UL, 0xA34E59D0UL, 0xB01EAA24UL, 0x42752927UL,
  0x96BF4DCCUL, 0x64D4CECFUL, 0x77843D3BUL, 0x85EFBE38UL,
  0xDBFC821CUL, 0x2997011FUL, 0x3AC7F2EBUL, 0xC8AC71E8UL,
  0x1C661503UL, 0xEE0D9600UL, 0xFD5D65F4UL, 0x0F36E6F7UL,
  0x61C69362UL, 0x93AD1061UL, 0x80FDE395UL, 0x72966096UL,
  0xA65C047DUL, 0x5437877EUL, 0x4767748AUL, 0xB50CF789UL,
  0xEB1FCBADUL, 0x197448AEUL, 0x0A24BB5AUL, 0xF84F3859UL,
  0x2C855CB2UL, 0xDEEEDFB1UL, 0xCDBE2C45UL, 0x3FD5AF46UL,
  0x7198540DUL, 0x83F3D70EUL, 0x90A324FAUL, 0x62C8A7F9UL,
  0xB602C312UL, 0x44694011UL, 0x5739B3E5UL, 0xA55230E6UL,
  0xFB410CC2UL, 0x092A8FC1UL, 0x1A7A7C35UL, 0xE811FF36UL,
  0x3CDB9BDDUL, 0xCEB018DEUL, 0xDDE0EB2AUL, 0x2F8B6829UL,
  0x82F63B78UL, 0x709DB87BUL, 0x63CD4B8FUL, 0x91A6C88CUL,
  0x456CAC67UL, 0xB7072F64UL, 0xA457DC90UL, 0x563C5F93UL,
  0x082F63B7UL, 0xFA44E0B4UL, 0xE9141340UL, 0x1B7F9043UL,
  0xCFB5F4A8UL, 0x3DDE77ABUL, 0x2E8E845FUL, 0xDCE5075CUL,
  0x92A8FC17UL, 0x60C37F14UL, 0x73938CE0UL, 0x81F80FE3UL,
  0x55326B08UL, 0xA759E80BUL, 0xB4091BFFUL, 0x466298FCUL,
  0x1871A4D8UL, 0xEA1A27DBUL, 0xF94AD42FUL, 0x0B21572CUL,
  0xDFEB33C7UL, 0x2D80B0C4UL, 0x3ED04330UL, 0xCCBBC033UL,
  0xA24BB5A6UL, 0x502036A5UL, 0x4370C551UL, 0xB11B4652UL,
  0x65D122B9UL, 0x97BAA1BAUL, 0x84EA524EUL, 0x7681D14DUL,
  0x2892ED69UL, 0xDAF96E6AUL, 0xC9A99D9EUL, 0x3BC21E9DUL,
  0xEF087A76UL, 0x1D63F975UL, 0x0E330A81UL, 0xFC588982UL,
  0xB21572C9UL, 0x407EF1CAUL, 0x532E023EUL, 0xA145813DUL,
  0x758FE5D6UL, 0x87E466D5UL, 0x94B49521UL, 0x66DF1622UL,
  0x38CC2A06UL, 0xCAA7A905UL, 0xD9F75AF1UL, 0x2B9CD9F2UL,
  0xFF56BD19UL, 0x0D3D3E1AUL, 0x1E6DCDEEUL, 0xEC064EEDUL,
  0xC38D26C4UL, 0x31E6A5C7UL, 0x22B65633UL, 0xD0DDD530UL,
  0x0417B1DBUL, 0xF67C32D8UL, 0xE52CC12CUL, 0x1747422FUL,
  0x49547E0BUL, 0xBB3FFD08UL, 0xA86F0EFCUL, 0x5A048DFFUL,
  0x8ECEE914UL, 0x7CA56A17UL, 0x6FF599E3UL, 0x9D9E1AE0UL,
  0xD3D3E1ABUL, 0x21B862A8UL, 0x32E8915CUL, 0xC083125FUL,
  0x144976B4UL, 0xE622F5B7UL, 0xF5720643UL, 0x07198540UL,
  0x590AB964UL, 0xAB613A67UL, 0xB831C993UL, 0x4A5A4A90UL,
  0x9E902E7BUL, 0x6CFBAD78UL, 0x7FAB5E8CUL, 0x8DC0DD8FUL,
  0xE330A81AUL, 0x115B2B19UL, 0x020BD8EDUL, 0xF0605BEEUL,
  0x24AA3F05UL, 0xD6C1BC06UL, 0xC5914FF2UL, 0x37FACCF1UL,
  0x69E9F0D5UL, 0x9B8273D6UL, 0x88D28022UL, 0x7AB90321UL,
  0xAE7367CAUL, 0x5C18E4C9UL, 0x4F48173DUL, 0xBD23943EUL,
  0xF36E6F75UL, 0x0105EC76UL, 0x12551F82UL, 0xE03E9C81UL,
  0x34F4F86AUL, 0xC69F7B69UL, 0xD5CF889DUL, 0x27A40B9EUL,
  0x79B737BAUL, 0x8BDCB4B9UL, 0x988C474DUL, 0x6AE7C44EUL,
  0xBE2DA0A5UL, 0x4C4623A6UL, 0x5F16D052UL, 0xAD7D5351UL,
};

#endif


/* Example of table build routine */

#include <stdio.h>
#include <stdlib.h>

#define OUTPUT_FILE   "crc32cr.h"
#define CRC32C_POLY    0x1EDC6F41UL

static FILE *tf;

static uint32_t
reflect_32(uint32_t b)
{
  int i;
  uint32_t rw = 0UL;

  for (i = 0; i < 32; i++) {
    if (b & 1)
      rw |= 1UL << (31 - i);
    b >>= 1;
  }
  return (rw);
}

static uint32_t
build_crc_table (int index)
{
  int i;
  uint32_t rb;

  rb = reflect_32(index);

  for (i = 0; i < 8; i++) {
    if (rb & 0x80000000UL)
      rb = (rb << 1) ^ (uint32_t)CRC32C_POLY;
    else
      rb <<= 1;
  }
  return (reflect_32(rb));
}

int
main (void)
{
  int i;

  printf("\nGenerating CRC32c table file <%s>.\n",
  OUTPUT_FILE);
  if ((tf = fopen(OUTPUT_FILE, "w")) == NULL) {
    printf("Unable to open %s.\n", OUTPUT_FILE);
    exit (1);
  }
  fprintf(tf, "#ifndef __crc32cr_h__\n");
  fprintf(tf, "#define __crc32cr_h__\n\n");
  fprintf(tf, "#define CRC32C_POLY 0x%08XUL\n",
    (uint32_t)CRC32C_POLY);
  fprintf(tf,
    "#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF])\n");
  fprintf(tf, "\nuint32_t crc_c[256] =\n{\n");
  for (i = 0; i < 256; i++) {
    fprintf(tf, "0x%08XUL,", build_crc_table (i));
    if ((i & 3) == 3)
      fprintf(tf, "\n");
    else
      fprintf(tf, " ");
  }
  fprintf(tf, "};\n\n#endif\n");

  if (fclose(tf) != 0)
    printf("Unable to close <%s>.\n", OUTPUT_FILE);
  else
    printf("\nThe CRC32c table has been written to <%s>.\n",
      OUTPUT_FILE);
  return (0);
}

/* Example of crc insertion */

#include "crc32cr.h"

uint32_t
generate_crc32c(unsigned char *buffer, unsigned int length)
{
  unsigned int i;
  uint32_t crc32 = 0xffffffffUL;
  uint32_t result;
  uint32_t byte0, byte1, byte2, byte3;

  for (i = 0; i < length; i++) {
    CRC32C(crc32, buffer[i]);
  }

  result = ~crc32;

  /*  result now holds the negated polynomial remainder,
   *  since the table and algorithm are "reflected" [williams95].
   *  That is, result has the same value as if we mapped the message
   *  to a polynomial, computed the host-bit-order polynomial
   *  remainder, performed final negation, and then did an
   *  end-for-end bit-reversal.
   *  Note that a 32-bit bit-reversal is identical to four in-place
   *  8-bit bit-reversals followed by an end-for-end byteswap.
   *  In other words, the bits of each byte are in the right order,
   *  but the bytes have been byteswapped.  So, we now do an explicit
   *  byteswap.  On a little-endian machine, this byteswap and
   *  the final ntohl cancel out and could be elided.
   */

  byte0 = result & 0xff;
  byte1 = (result>>8) & 0xff;
  byte2 = (result>>16) & 0xff;
  byte3 = (result>>24) & 0xff;
  crc32 = ((byte0 << 24) |
           (byte1 << 16) |
           (byte2 << 8)  |
           byte3);
  return (crc32);
}

int
insert_crc32(unsigned char *buffer, unsigned int length)
{
  SCTP_message *message;
  uint32_t crc32;

  message = (SCTP_message *)buffer;
  message->common_header.checksum = 0UL;
  crc32 = generate_crc32c(buffer,length);
  /* and insert it into the message */
  message->common_header.checksum = htonl(crc32);
  return (1);
}
int
validate_crc32(unsigned char *buffer, unsigned int length)
{
  SCTP_message *message;
  unsigned int i;
  uint32_t original_crc32;
  uint32_t crc32;

  /* save and zero checksum */
  message = (SCTP_message *)buffer;
  original_crc32 = ntohl(message->common_header.checksum);
  message->common_header.checksum = 0L;
  crc32 = generate_crc32c(buffer, length);
  return ((original_crc32 == crc32) ? 1 : -1);
}

Acknowledgements ๐Ÿซถ

okay bestie, this updated doc is not a small feat ๐Ÿ˜ญโœจ it's the whole summation of the original coauthors of [RFC2960]: Q. Xie, K. Morneault, C. Sharp, H. Schwarzbauer, T. Taylor, I. Rytina, M. Kalla, L. Zhang, and V. Paxson ๐ŸŽ€

then add everyone who gave comments on [RFC2960]: Mark Allman, R. J. Atkinson, Richard Band, Scott Bradner, Steve Bellovin, Peter Butler, Ram Dantu, R. Ezhirpavai, Mike Fisk, Sally Floyd, Atsushi Fukumoto, Matt Holdrege, Henry Houh, Christian Huitema, Gary Lehecka, Jonathan Lee, David Lehmann, John Loughney, Daniel Luan, Barry Nagelberg, Thomas Narten, Erik Nordmark, Lyndon Ong, Shyamal Prasad, Kelvin Porter, Heinz Prantner, Jarno Rajahalme, Raymond E. Reeves, Renee Revis, Ivan Arias Rodriguez, A. Sankar, Greg Sidebottom, Brian Wyld, La Monte Yarroll, and many others for their invaluable comments ๐Ÿ’–

next up, the coauthors of [RFC4460]: I. Arias-Rodriguez, K. Poon, and A. Caro ๐ŸŒธ

then add the efforts of all the subsequent seven SCTP interoperability tests and everyone who commented on [RFC4460], as shown in its acknowledgements: Barry Zuckerman, La Monte Yarroll, Qiaobing Xie, Wang Xiaopeng, Jonathan Wood, Jeff Waskow, Mike Turner, John Townsend, Sabina Torrente, Cliff Thomas, Yuji Suzuki, Manoj Solanki, Sverre Slotte, Keyur Shah, Jan Rovins, Ben Robinson, Renee Revis, Ian Periam, RC Monee, Sanjay Rao, Sujith Radhakrishnan, Heinz Prantner, Biren Patel, Nathalie Mouellic, Mitch Miers, Bernward Meyknecht, Stan McClellan, Oliver Mayor, Tomas Orti Martin, Sandeep Mahajan, David Lehmann, Jonathan Lee, Philippe Langlois, Karl Knutson, Joe Keller, Gareth Keily, Andreas Jungmaier, Janardhan Iyengar, Mutsuya Irie, John Hebert, Kausar Hassan, Fred Hasle, Dan Harrison, Jon Grim, Laurent Glaude, Steven Furniss, Atsushi Fukumoto, Ken Fujita, Steve Dimig, Thomas Curran, Serkan Cil, Melissa Campbell, Peter Butler, Rob Brennan, Harsh Bhondwe, Brian Bidulock, Caitlin Bestler, Jon Berger, Robby Benedyk, Stephen Baucke, Sandeep Balani, and Ronnie Sellar ๐Ÿฆ‹

a special shoutout to Mark Allman, who really should have been a coauthor of [RFC4460] for his work on the max-burst but somehow wiggled out on a technicality ๐Ÿ’…๐Ÿ˜‚

also, we see you Lyndon Ong and Phil Conrad, thank you for your valuable input and many contributions ๐Ÿงš

furthermore, there's [RFC4960] and everyone who commented on it, including Alfred Hรถnes and Ronnie Sellars ๐ŸŒˆ

then add the coauthor of [RFC8540]: Maksim Proshin ๐Ÿ‘‘

and the people who commented on [RFC8540]: Pontus Andersson, Eric W. Biederman, Cedric Bonnet, Spencer Dawkins, Gorry Fairhurst, Benjamin Kaduk, Mirja Kรผhlewind, Peter Lei, Gyula Marosi, Lionel Morand, Jeff Morriss, Tom Petch, Kacheong Poon, Julien Pourtet, Irene Rรผngeler, Michael Welzl, and Qiaobing Xie ๐Ÿ“

and last but definitely not least, the people who commented on this document: Gorry Fairhurst, Martin Duke, Benjamin Kaduk, Tero Kivinen, Eliot Lear, Marcelo Ricardo Leitner, David Mandelberg, John PreuรŸ Mattsson, Claudio Porfiri, Maksim Proshin, Ines Robles, Timo Vรถlker, Magnus Westerlund, and Zhouming ๐Ÿ’œ

honestly, our thanks can't be fully expressed to everyone who joined in on the coding, testing, and updating of this document. all we can say is, Thank You! ๐Ÿฅนโœจ๐Ÿซถโœจ


Authors' Addresses ๐ŸŽ€

Randall R. Stewart Netflix, Inc. 2455 Heritage Green Ave Davenport, FL 33837 United States of America Email: randall@lakerest.net

Michael Tรผxen Mรผnster University of Applied Sciences Stegerwaldstrasse 39 48565 Steinfurt Germany Email: tuexen@fh-muenster.de

Karen E. E. Nielsen Kamstrup A/S Industrivej 28 DK-8660 Skanderborg Denmark Email: kee@kamstrup.com

๐Ÿ“ฆ where this page comes from