RFC 6458

Sockets API Extensions for the Stream Control Transmission Protocol (SCTP)

๐Ÿ“… published December 2011, from ietf

RFC 6458: Sockets API Extensions for the Stream Control Transmission Protocol (SCTP) ๐Ÿฆ‹

Part 1 of 22 ๐Ÿ’…

Okay bestie, this is RFC 6458, and it's all about Sockets API Extensions for SCTP (the Stream Control Transmission Protocol) ๐ŸŒธ. It was published in December 2011 by the IETF (Internet Engineering Task Force) as RFC 6458, with ISSN 2070-1721 โœจ. Its category is Informational, so it's published for information rather than as a standards track spec ๐ŸŽ€.

The author list is giving main character energy ๐Ÿ‘‘: R. Stewart (Adara Networks), M. Tuexen (Muenster Univ. of Appl. Sciences), K. Poon (Oracle Corporation), P. Lei (Cisco Systems, Inc.), and V. Yasevich (HP) ๐Ÿซถ. The header doesn't list anything this RFC obsoletes or updates, so it isn't replacing or changing any other RFC ๐ŸŒˆ.


Abstract

Okay so the abstract is the quick pitch ๐Ÿ’–. This document describes a mapping of SCTP into a sockets API โœจ. The perks of this mapping are compatibility for TCP applications, access to new SCTP features, and one consolidated error and event notification scheme ๐Ÿงš. Literally iconic ๐Ÿ˜ญ.

Status of This Memo

Real talk, this part is the fine print ๐Ÿ™ˆ. This document is not an Internet Standards Track specification. It's published for informational purposes ๐Ÿ“.

It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community ๐Ÿ’œ. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG) ๐Ÿ”ฅ. Not every document the IESG approves is a candidate for any level of Internet Standard, so check Section 2 of RFC 5741 if you're curious ๐Ÿ’€.

For the current status of this document, any errata, and how to send feedback, head to http://www.rfc-editor.org/info/rfc6458 ๐ŸŒธ.

Copyright (c) 2011 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 (http://trustee.ietf.org/license-info) in effect on the date of publication ๐Ÿฆ‹. Please review those carefully, because they describe your rights and restrictions with respect to this document ๐Ÿ’…. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they're provided without warranty as described in the Simplified BSD License ๐ŸŽ€.

This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008 ๐Ÿงš. The people controlling the copyright in some of that material may not have granted the IETF Trust the right 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 the IETF Standards Process ๐Ÿ’€. The exceptions are formatting it for publication as an RFC or translating it into languages other than English ๐ŸŒˆ.

Table of Contents โœจ

Okay bestie, this table of contents is giving full main character energy ๐Ÿ’… Every section is listed in order so you can find your way around the whole spec ๐Ÿฆ‹

   1. Introduction ....................................................6
   2. Data Types ......................................................8
   3. One-to-Many Style Interface .....................................8
      3.1. Basic Operation ............................................8
           3.1.1. socket() ............................................9
           3.1.2. bind() .............................................10
           3.1.3. listen() ...........................................11
           3.1.4. sendmsg() and recvmsg() ............................12
           3.1.5. close() ............................................14
           3.1.6. connect() ..........................................14
      3.2. Non-Blocking Mode .........................................15
      3.3. Special Considerations ....................................16
   4. One-to-One Style Interface .....................................18
      4.1. Basic Operation ...........................................18
           4.1.1. socket() ...........................................19
           4.1.2. bind() .............................................19
           4.1.3. listen() ...........................................21
           4.1.4. accept() ...........................................21
           4.1.5. connect() ..........................................22
           4.1.6. close() ............................................23
           4.1.7. shutdown() .........................................23
           4.1.8. sendmsg() and recvmsg() ............................24
           4.1.9. getpeername() ......................................24
   5. Data Structures ................................................25
      5.1. The msghdr and cmsghdr Structures .........................25
      5.2. Ancillary Data Considerations and Semantics ...............26
           5.2.1. Multiple Items and Ordering ........................27
           5.2.2. Accessing and Manipulating Ancillary Data ..........27
           5.2.3. Control Message Buffer Sizing ......................28
      5.3. SCTP msg_control Structures ...............................28
           5.3.1. SCTP Initiation Structure (SCTP_INIT) ..............29
           5.3.2. SCTP Header Information Structure
                  (SCTP_SNDRCV) - DEPRECATED .........................30
           5.3.3. Extended SCTP Header Information Structure
                  (SCTP_EXTRCV) - DEPRECATED .........................33
           5.3.4. SCTP Send Information Structure (SCTP_SNDINFO) .....35
           5.3.5. SCTP Receive Information Structure (SCTP_RCVINFO) ..37
           5.3.6. SCTP Next Receive Information Structure
                  (SCTP_NXTINFO) .....................................38
           5.3.7. SCTP PR-SCTP Information Structure (SCTP_PRINFO) ...39
           5.3.8. SCTP AUTH Information Structure (SCTP_AUTHINFO) ....40
           5.3.9. SCTP Destination IPv4 Address Structure
                  (SCTP_DSTADDRV4) ...................................41
           5.3.10. SCTP Destination IPv6 Address Structure
                   (SCTP_DSTADDRV6) ..................................41

   6. SCTP Events and Notifications ..................................41
      6.1. SCTP Notification Structure ...............................42
           6.1.1. SCTP_ASSOC_CHANGE ..................................43
           6.1.2. SCTP_PEER_ADDR_CHANGE ..............................45
           6.1.3. SCTP_REMOTE_ERROR ..................................46
           6.1.4. SCTP_SEND_FAILED - DEPRECATED ......................47
           6.1.5. SCTP_SHUTDOWN_EVENT ................................48
           6.1.6. SCTP_ADAPTATION_INDICATION .........................49
           6.1.7. SCTP_PARTIAL_DELIVERY_EVENT ........................49
           6.1.8. SCTP_AUTHENTICATION_EVENT ..........................50
           6.1.9. SCTP_SENDER_DRY_EVENT ..............................51
           6.1.10. SCTP_NOTIFICATIONS_STOPPED_EVENT ..................52
           6.1.11. SCTP_SEND_FAILED_EVENT ............................52
      6.2. Notification Interest Options .............................54
           6.2.1. SCTP_EVENTS Option - DEPRECATED ....................54
           6.2.2. SCTP_EVENT Option ..................................56
   7. Common Operations for Both Styles ..............................57
      7.1. send(), recv(), sendto(), and recvfrom() ..................57
      7.2. setsockopt() and getsockopt() .............................59
      7.3. read() and write() ........................................60
      7.4. getsockname() .............................................60
      7.5. Implicit Association Setup ................................61
   8. Socket Options .................................................61
      8.1. Read/Write Options ........................................63
           8.1.1. Retransmission Timeout Parameters (SCTP_RTOINFO) ...63
           8.1.2. Association Parameters (SCTP_ASSOCINFO) ............64
           8.1.3. Initialization Parameters (SCTP_INITMSG) ...........66
           8.1.4. SO_LINGER ..........................................66
           8.1.5. SCTP_NODELAY .......................................66
           8.1.6. SO_RCVBUF ..........................................67
           8.1.7. SO_SNDBUF ..........................................67
           8.1.8. Automatic Close of Associations (SCTP_AUTOCLOSE) ...67
           8.1.9. Set Primary Address (SCTP_PRIMARY_ADDR) ............68
           8.1.10. Set Adaptation Layer Indicator
                   (SCTP_ADAPTATION_LAYER) ...........................68
           8.1.11. Enable/Disable Message Fragmentation
                   (SCTP_DISABLE_FRAGMENTS) ..........................68
           8.1.12. Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) ...69
           8.1.13. Set Default Send Parameters
                   (SCTP_DEFAULT_SEND_PARAM) - DEPRECATED ............71
           8.1.14. Set Notification and Ancillary Events
                   (SCTP_EVENTS) - DEPRECATED ........................72
           8.1.15. Set/Clear IPv4 Mapped Addresses
                   (SCTP_I_WANT_MAPPED_V4_ADDR) ......................72
           8.1.16. Get or Set the Maximum Fragmentation Size
                   (SCTP_MAXSEG) .....................................72
           8.1.17. Get or Set the List of Supported HMAC
                   Identifiers (SCTP_HMAC_IDENT) .....................73

           8.1.18. Get or Set the Active Shared Key
                   (SCTP_AUTH_ACTIVE_KEY) ............................74
           8.1.19. Get or Set Delayed SACK Timer
                   (SCTP_DELAYED_SACK) ...............................74
           8.1.20. Get or Set Fragmented Interleave
                   (SCTP_FRAGMENT_INTERLEAVE) ........................75
           8.1.21. Set or Get the SCTP Partial Delivery Point
                   (SCTP_PARTIAL_DELIVERY_POINT) .....................77
           8.1.22. Set or Get the Use of Extended Receive Info
                   (SCTP_USE_EXT_RCVINFO) - DEPRECATED ...............77
           8.1.23. Set or Get the Auto ASCONF Flag
                   (SCTP_AUTO_ASCONF) ................................77
           8.1.24. Set or Get the Maximum Burst (SCTP_MAX_BURST) .....78
           8.1.25. Set or Get the Default Context (SCTP_CONTEXT) .....78
           8.1.26. Enable or Disable Explicit EOR Marking
                   (SCTP_EXPLICIT_EOR) ...............................79
           8.1.27. Enable SCTP Port Reusage (SCTP_REUSE_PORT) ........79
           8.1.28. Set Notification Event (SCTP_EVENT) ...............79
           8.1.29. Enable or Disable the Delivery of SCTP_RCVINFO
                   as Ancillary Data (SCTP_RECVRCVINFO) ..............79
           8.1.30. Enable or Disable the Delivery of SCTP_NXTINFO
                   as Ancillary Data (SCTP_RECVNXTINFO) ..............80
           8.1.31. Set Default Send Parameters
                   (SCTP_DEFAULT_SNDINFO) ............................80
           8.1.32. Set Default PR-SCTP Parameters
                   (SCTP_DEFAULT_PRINFO) .............................80
      8.2. Read-Only Options .........................................81
           8.2.1. Association Status (SCTP_STATUS) ...................81
           8.2.2. Peer Address Information
                  (SCTP_GET_PEER_ADDR_INFO) ..........................82
           8.2.3. Get the List of Chunks the Peer Requires to
                  Be Authenticated (SCTP_PEER_AUTH_CHUNKS) ...........84
           8.2.4. Get the List of Chunks the Local Endpoint Requires
                  to Be Authenticated (SCTP_LOCAL_AUTH_CHUNKS) .......84
           8.2.5. Get the Current Number of Associations
                  (SCTP_GET_ASSOC_NUMBER) ............................85
           8.2.6. Get the Current Identifiers of Associations
                  (SCTP_GET_ASSOC_ID_LIST) ...........................85
      8.3. Write-Only Options ........................................85
           8.3.1. Set Peer Primary Address
                  (SCTP_SET_PEER_PRIMARY_ADDR) .......................86
           8.3.2. Add a Chunk That Must Be Authenticated
                  (SCTP_AUTH_CHUNK) ..................................86
           8.3.3. Set a Shared Key (SCTP_AUTH_KEY) ...................86
           8.3.4. Deactivate a Shared Key
                  (SCTP_AUTH_DEACTIVATE_KEY) .........................87
           8.3.5. Delete a Shared Key (SCTP_AUTH_DELETE_KEY) .........88

   9. New Functions ..................................................88
      9.1. sctp_bindx() ..............................................88
      9.2. sctp_peeloff() ............................................90
      9.3. sctp_getpaddrs() ..........................................91
      9.4. sctp_freepaddrs() .........................................92
      9.5. sctp_getladdrs() ..........................................92
      9.6. sctp_freeladdrs() .........................................93
      9.7. sctp_sendmsg() - DEPRECATED ...............................93
      9.8. sctp_recvmsg() - DEPRECATED ...............................94
      9.9. sctp_connectx() ...........................................95
      9.10. sctp_send() - DEPRECATED .................................96
      9.11. sctp_sendx() - DEPRECATED ................................97
      9.12. sctp_sendv() .............................................98
      9.13. sctp_recvv() ............................................101
   10. Security Considerations ......................................103
   11. Acknowledgments ..............................................103
   12. References ...................................................104
      12.1. Normative References ....................................104
      12.2. Informative References ..................................104
   Appendix A. Example Using One-to-One Style Sockets ...............106
   Appendix B. Example Using One-to-Many Style Sockets ..............109

1. Introduction โœจ

okay so the sockets API has been giving us a standard way to plug the Internet Protocol suite into basically every operating system ๐ŸŒ and TCP [RFC0793] and UDP [RFC0768] have both benefited from that shared setup on tons of different platforms. SCTP is the new kid ๐Ÿฆ‹ and it has a lot of TCP vibes but also some UDP-style semantics. This document is about mapping the existing sockets API onto SCTP so you get a base for all the new features, AND most existing TCP apps can move over with few (if any) changes. Literally the best of both worlds ๐Ÿ’–

There are three basic design objectives ๐ŸŽ€

  1. Maintain consistency with existing sockets APIs: We define a sockets mapping for SCTP that lines up with the other sockets API protocol mappings (for instance UDP, TCP, IPv4, and IPv6). Consistency is everything ๐Ÿ’…
  2. Support a one-to-many style interface: This set of semantics is similar to what's defined for connectionless protocols, like UDP. A one-to-many style SCTP socket should be able to control multiple SCTP associations. It's basically like a UDP socket that can chat with lots of peer endpoints ๐Ÿซถ Each association gets its own association identifier so the app can tell them apart. Note that SCTP is connection-oriented in nature, and it does not support broadcast or multicast communications, as UDP does. So no group-chat broadcasting here, bestie ๐Ÿ™ˆ
  3. Support a one-to-one style interface: This interface supports semantics similar to sockets for connection-oriented protocols, like TCP. A one-to-one style SCTP socket should only control one SCTP association. The whole point is that existing apps built on other connection-oriented protocols can be ported to SCTP with very little effort, and developers who already know those semantics can adapt easily ๐Ÿงš It also makes sure the mechanisms most OSes have for sockets, like select(), keep working with this style of socket. New features get added to this mapping later so you can exploit what's new in SCTP โœจ

Goals 2 and 3 are not compatible, so this document defines two modes of mapping: the one-to-many style mapping and the one-to-one style mapping. They share some common data structures and operations, but they need two different application programming styles ๐ŸŒธ Also, all new SCTP features can be used with both styles of socket. Which one you pick mainly depends on the nature of your application, so it's really a vibe check ๐Ÿ‘‘

A mechanism is defined to extract an SCTP association from a one-to-many style socket into a one-to-one style socket ๐Ÿฆ‹

Some SCTP mechanisms can't be adequately mapped to an existing socket interface. Sometimes it's just better to have a new interface instead of forcing existing socket calls to do the job. Section 9 of this document describes these new interfaces ๐Ÿ’œ

Please note that some elements of the SCTP sockets API are declared as deprecated ๐Ÿ˜ญ Over time, elements of the API were introduced, implemented, and later replaced by other elements. The replaced ones are marked deprecated because they still show up in some implementations while the replacement functions don't. This especially applies to older versions of operating systems that support SCTP. New SCTP socket implementations must implement at least the non-deprecated elements. Implementations that want to interoperate with older versions of the API should also include the deprecated functions ๐Ÿ“

2. Data Types ๐ŸŽ€

Whenever possible, the Portable Operating System Interface (POSIX) data types defined in [IEEE-1003.1-2008] are used. uintN_t means an unsigned integer of exactly N bits (e.g., uint16_t). This document also assumes the argument data types from POSIX when possible (e.g., the final argument to setsockopt() is a socklen_t value). And whenever buffer sizes are specified, the POSIX size_t data type is used ๐Ÿ’–

3. One-to-Many Style Interface ๐Ÿฆ‹

In the one-to-many style interface, there's a one-to-many relationship between sockets and associations ๐ŸŒˆ

3.1. Basic Operation

A typical server in this style uses the following socket calls, in sequence, to prep an endpoint for servicing requests:

  • socket()
  • bind()
  • listen()
  • recvmsg()
  • sendmsg()
  • close()

A typical client uses the following calls, in sequence, to set up an association with a server so it can request services:

  • socket()
  • sendmsg()
  • recvmsg()
  • close()

In this style, by default, all of the associations connected to the endpoint are represented by a single socket ๐Ÿงš Each association is assigned an association identifier (the type is sctp_assoc_t) so the application can use it to tell them apart. In some implementations, the peer endpoints' addresses can also be used for this purpose, but this is not required for performance reasons. If an implementation does not support using addresses to differentiate between associations, the sendto() call can only be used to set up an association implicitly. It can't be used to send data to an established association, because the association identifier can't be specified ๐Ÿ™ˆ

Once an association identifier is assigned to an SCTP association, that identifier will not be reused until the application explicitly terminates use of the association. The resources belonging to that association won't be freed until that happens. This is similar to the close() operation on a normal socket. The only exception is when the SCTP_AUTOCLOSE option (Section 8.1.8) is set. In that case, after the association is terminated gracefully and automatically, its association identifier can be reused. Everyone using this option should be aware of this, to avoid the possible problem of sending data to an incorrect peer endpoint ๐Ÿ˜ฌ

If the server or client wants to branch an existing association off to a separate socket, it is required to call sctp_peeloff() and specify the association identifier. The sctp_peeloff() call returns a new one-to-one style socket that can then be used with recv() and send() functions for message passing. See Section 9.2 for more on branched-off associations ๐Ÿฆ‹

Once an association is branched off to a separate socket, it becomes completely separated from the original socket. All subsequent control and data operations on that association must be done through the new socket. For example, a close() on the original socket will not terminate any associations that have been branched off to a different socket ๐Ÿ’…

One-to-many style socket calls are discussed in more detail in the following subsections โœจ

3.1.1. socket()

Applications use socket() to create a socket descriptor that represents an SCTP endpoint ๐ŸŒธ

The function prototype is

int socket(int domain,
           int type,
           int protocol);

and one uses PF_INET or PF_INET6 as the domain, SOCK_SEQPACKET as the type, and IPPROTO_SCTP as the protocol.

Here, SOCK_SEQPACKET indicates the creation of a one-to-many style socket.

The function returns a socket descriptor, or -1 in case of an error.

Using the PF_INET domain indicates the creation of an endpoint that can use only IPv4 addresses, while PF_INET6 creates an endpoint that can use both IPv6 and IPv4 addresses ๐Ÿ‘‘

3.1.2. bind()

Applications use bind() to specify which local address and port the SCTP endpoint should associate itself with ๐ŸŽ€

An SCTP endpoint can be associated with multiple addresses. To do this, sctp_bindx() is introduced in Section 9.1 to help applications do the job of associating multiple addresses. But note that an endpoint can only be associated with one local port ๐Ÿ™ˆ

These addresses associated with a socket are the eligible transport addresses for the endpoint to send and receive data. The endpoint will also present these addresses to its peers during the association initialization process; see [RFC4960] ๐Ÿ’œ

After calling bind(), if the endpoint wishes to accept new associations on the socket, it must call listen() (see Section 3.1.3) ๐Ÿซถ

The function prototype of bind() is

int bind(int sd,
         struct sockaddr *addr,
         socklen_t addrlen);

and the arguments are

  • sd: The socket descriptor returned by socket().
  • addr: The address structure (struct sockaddr_in for an IPv4 address or struct sockaddr_in6 for an IPv6 address; see [RFC3493]).
  • addrlen: The size of the address structure.

bind() returns 0 on success and -1 in case of an error.

If sd is an IPv4 socket, the address passed must be an IPv4 address. If sd is an IPv6 socket, the address passed can be either an IPv4 or an IPv6 address ๐ŸŒˆ

Applications cannot call bind() multiple times to associate multiple addresses with an endpoint. After the first call to bind(), all subsequent calls will return an error ๐Ÿ˜ญ

If the IP address part of addr is specified as a wildcard (INADDR_ANY for an IPv4 address, or IN6ADDR_ANY_INIT or in6addr_any for an IPv6 address), the operating system will associate the endpoint with an optimal address set of the available interfaces. If the IPv4 sin_port or IPv6 sin6_port is set to 0, the operating system will choose an ephemeral port for the endpoint ๐Ÿงš

If bind() is not called prior to a sendmsg() call that initiates a new association, the system picks an ephemeral port and chooses an address set equivalent to binding with a wildcard address. One of those addresses becomes the primary address for the association. This automatically enables the multi-homing capability of SCTP ๐Ÿฆ‹

The completion of this bind() process does not allow the SCTP endpoint to accept inbound SCTP association requests. Until a listen() system call, described below, is performed on the socket, the SCTP endpoint will promptly reject an inbound SCTP INIT request with an SCTP ABORT ๐Ÿšซ

3.1.3. listen() โœจ

By default, a one-to-many style socket is like a bouncer with the door shut ๐Ÿšช๐Ÿ’…, so it does not accept new association requests. An application calls listen() to mark a socket as able to accept new associations ๐Ÿ”“

The function prototype is

int listen(int sd,
           int backlog);

and the arguments are

  • sd: The socket descriptor of the endpoint.
  • backlog: If backlog is non-zero, enable listening, else disable listening.

listen() returns 0 on success and -1 in case of an error.

Okay bestie, one-to-many style socket consumers do not need to call accept() to grab new associations ๐Ÿงš Calling accept() on a one-to-many style socket should return EOPNOTSUPP. Instead, new associations are accepted automatically, and you get notified about them through recvmsg() with the SCTP_ASSOC_CHANGE event (if these notifications are enabled) ๐Ÿ”” Clients will typically not call listen(), so they can be assured that only actively initiated associations are possible on the socket. Server or peer-to-peer sockets, on the other hand, will always accept new associations, so a well-written application using server one-to-many style sockets must be prepared to handle new associations from unwanted peers. Not me being dramatic about it, but that's a real vibe check ๐Ÿ˜ญ

Also, the SCTP_ASSOC_CHANGE event provides the association identifier for a new association. So if applications wish to use the association identifier as a parameter to other socket calls, they should make sure the SCTP_ASSOC_CHANGE event is enabled ๐Ÿซถ


3.1.4. sendmsg() and recvmsg() ๐Ÿ’Œ

An application uses the sendmsg() and recvmsg() calls to send data to and receive data from its peer. Iconic behavior ๐ŸŒˆ

The function prototypes are

ssize_t sendmsg(int sd,
                const struct msghdr *message,
                int flags);

and

ssize_t recvmsg(int sd,
                struct msghdr *message,
                int flags);

using the following arguments:

  • sd: The socket descriptor of the endpoint.
  • message: Pointer to the msghdr structure that contains a single user message and possibly some ancillary data. See Section 5 for a complete description of the data structures.
  • flags: No new flags are defined for SCTP at this level. See Section 5 for SCTP-specific flags used in the msghdr structure.

sendmsg() returns the number of bytes accepted by the kernel or -1 in case of an error. recvmsg() returns the number of bytes received or -1 in case of an error.

As described in Section 5, different types of ancillary data can be sent and received along with user data. When sending, the ancillary data is used to specify the sent behavior, such as the SCTP stream number to use. When receiving, the ancillary data is used to describe the received data, such as the SCTP stream sequence number of the message ๐Ÿงพ

When sending user data with sendmsg(), the msg_name field in the msghdr structure will be filled with one of the transport addresses of the intended receiver. If there is no existing association between the sender and the intended receiver, the sender's SCTP stack will set up a new association and then send the user data (see Section 7.5 for more on implicit association setup). If sendmsg() is called with no data and there is no existing association, a new one will be established. The SCTP_INIT type ancillary data can be used to change some of the parameters used to set up a new association. If sendmsg() is called with NULL data, and there is no existing association but the SCTP_ABORT or SCTP_EOF flags are set as described in Section 5.3.4, then -1 is returned and errno is set to EINVAL ๐Ÿ™…โ€โ™€๏ธ Sending a message using sendmsg() is atomic unless explicit end of record (EOR) marking is enabled on the socket specified by sd (see Section 8.1.26).

If a peer sends a SHUTDOWN, an SCTP_SHUTDOWN_EVENT notification will be delivered if that notification has been enabled, and no more data can be sent to that association. Any attempt to send more data will cause sendmsg() to return with an ESHUTDOWN error. The socket is still open for reading at this point, so it is possible to retrieve notifications ๐Ÿ“ฌ

When receiving a user message with recvmsg(), the msg_name field in the msghdr structure will be populated with the source transport address of the user data. The caller of recvmsg() can use this address information to determine which association the received user message belongs to ๐Ÿ•ต๏ธโ€โ™€๏ธ Note that if SCTP_ASSOC_CHANGE events are disabled, applications must use the peer transport address provided in the msg_name field by recvmsg() to correlate to an association, since they will not have the association identifier.

If all data in a single message has been delivered, MSG_EOR will be set in the msg_flags field of the msghdr structure (see Section 5.1) โœ…

If the application does not provide enough buffer space to completely receive a data message, MSG_EOR will not be set in msg_flags. Successive reads will consume more of the same message until the entire message has been delivered, and MSG_EOR will be set.

If the SCTP stack is running low on buffers, it may partially deliver a message. In this case, MSG_EOR will not be set, and more calls to recvmsg() will be necessary to completely consume the message. Only one message at a time can be partially delivered in any stream. The socket option SCTP_FRAGMENT_INTERLEAVE controls various aspects of what interlacing of messages occurs for both the one-to-one and the one-to-many style sockets. Please consult Section 8.1.20 for further details on message delivery options ๐Ÿงฉ


3.1.5. close() ๐Ÿšช

Applications use close() to perform graceful shutdown (as described in Section 10.1 of [RFC4960]) on all of the associations currently represented by a one-to-many style socket ๐Ÿ‘‹

The function prototype is

int close(int sd);

and the argument is

  • sd: The socket descriptor of the associations to be closed.

0 is returned on success and -1 in case of an error.

To gracefully shut down a specific association represented by the one-to-many style socket, an application should use the sendmsg() call and include the SCTP_EOF flag. A user may optionally terminate an association non-gracefully by using sendmsg() with the SCTP_ABORT flag set and possibly passing a user-specified abort code in the data field. Both flags SCTP_EOF and SCTP_ABORT are passed with ancillary data (see Section 5.3.4) in the sendmsg() call. That's the difference between a soft goodbye and a dramatic exit ๐Ÿ’€

If sd in the close() call is a branched-off socket representing only one association, the shutdown is performed on that association only ๐ŸŽฏ


3.1.6. connect() ๐Ÿ”Œ

An application may use the connect() call in the one-to-many style to initiate an association without sending data.

The function prototype is

int connect(int sd,
            const struct sockaddr *nam,
            socklen_t len);

and the arguments are

  • sd: The socket descriptor to which a new association is added.
  • nam: The address structure (struct sockaddr_in for an IPv4 address or struct sockaddr_in6 for an IPv6 address; see [RFC3493]).
  • len: The size of the address.

0 is returned on success and -1 in case of an error.

Multiple connect() calls can be made on the same socket to create multiple associations. This is different from the semantics of connect() on a UDP socket ๐ŸŒธ

Note that SCTP allows data exchange, similar to T/TCP [RFC1644] (made Historic by [RFC6247]), during the association setup phase. If an application wants to do this, it cannot use the connect() call. Instead, it should use sendto() or sendmsg() to initiate an association. If it uses sendto() and it wants to change the initialization behavior, it needs to use the SCTP_INITMSG socket option before calling sendto(). Or it can use sendmsg() with SCTP_INIT type ancillary data to initiate an association without calling setsockopt(). Note that the implicit setup is supported for the one-to-many style sockets โœจ

SCTP does not support half close semantics. This means that unlike T/TCP, MSG_EOF should not be set in the flags parameter when calling sendto() or sendmsg() when the call is used to initiate a connection. MSG_EOF is not an acceptable flag with an SCTP socket ๐Ÿšซ


3.2. Non-Blocking Mode ๐ŸŒŠ

Some SCTP applications may wish to avoid being blocked when calling a socket interface function.

Once a bind() call and/or subsequent sctp_bindx() calls are complete on a one-to-many style socket, an application may set the non-blocking option via a fcntl() (such as O_NONBLOCK). After setting the socket to non-blocking mode, the sendmsg() function returns immediately. The success or failure of sending the data message (with possible SCTP_INITMSG ancillary data) will be signaled by the SCTP_ASSOC_CHANGE event with SCTP_COMM_UP or SCTP_CANT_START_ASSOC. If user data could not be sent (due to an SCTP_CANT_START_ASSOC), the sender will also receive an SCTP_SEND_FAILED_EVENT event. Events can be received by the user calling recvmsg(). A server (having called listen()) is also notified of an association-up event via the reception of an SCTP_ASSOC_CHANGE with SCTP_COMM_UP via the calling of recvmsg() and possibly the reception of the first data message ๐ŸŽ‰

To shut down the association gracefully, the user must call sendmsg() with no data and with the SCTP_EOF flag set as described in Section 5.3.4. The function returns immediately, and completion of the graceful shutdown is indicated by an SCTP_ASSOC_CHANGE notification of type SCTP_SHUTDOWN_COMP (see Section 6.1.1). Note that this can also be done using the sctp_sendv() call described in Section 9.12.

It is recommended that an application use caution when using select() (or poll()) for writing on a one-to-many style socket, because the interpretation of select() on write is implementation specific. Generally, a positive return on a select() on write would only indicate that one of the associations represented by the one-to-many style socket is writable. An application that writes after the select() returns may still block, since the association that was writable is not the destination association of the write call ๐Ÿ™ˆ Likewise, select() (or poll()) for reading from a one-to-many style socket will only return an indication that one of the associations represented by the socket has data to be read.

An application that wishes to know that a particular association is ready for reading or writing should either use the one-to-one style or use the sctp_peeloff() function (see Section 9.2) to separate the association of interest from the one-to-many style socket ๐Ÿง

Note that some implementations may have an extended select call, such as epoll or kqueue, that may escape this limitation and allow a select on a specific association of a one-to-many style socket, but this is an implementation-specific detail that a portable application cannot depend on.

3.3. Special Considerations ๐Ÿง โœจ

okay bestie, this part is lowkey the most important bit of section 3 ๐Ÿซถ The fact that a one-to-many style socket can reach tons of SCTP associations through a single socket descriptor is a whole vibe ๐Ÿฆ‹ It has big implications for both application programmers and system programmers who implement this API. The big question is how buffer space inside the sockets layer gets managed ๐Ÿ’… Since this implementation detail directly affects how app devs write their code to keep things working correctly and portable, this section gives some guidance to both implementers and application programmers ๐ŸŽ€

An important feature SCTP shares with TCP is flow control ๐ŸŒธ Basically, a sender may not send data faster than the receiver can consume it ๐Ÿ’œ

For TCP, flow control usually shows up in the sockets API like this ๐Ÿ“ If the reader stops reading, the sender keeps queuing messages in the socket layer until the send socket buffer is completely full. That's a "stalled connection" ๐Ÿ’€ Any further attempts to write to the socket will block, or return the error EAGAIN or EWOULDBLOCK if the socket is non-blocking ๐Ÿ™ˆ Eventually either the connection gets closed, or the receiver starts reading again, which frees up space in the output queue โœจ

For one-to-one style SCTP sockets (this includes socket descriptors that were separated from a one-to-many style socket with sctp_peeloff()), the behavior is identical ๐Ÿ‘‘ For one-to-many style SCTP sockets, there are multiple associations on a single socket, which makes things way more complicated ๐Ÿ˜ญ If the implementation uses a single buffer space allocation shared by all associations, then a single stalled association can stop data from being sent on all associations active on that one-to-many style socket ๐Ÿฅบ

For a blocking socket, it's pretty clear that a single stalled association can block the entire socket ๐Ÿ”’ Because of this, application programmers may want to use non-blocking one-to-many style sockets ๐Ÿงš The application should at least be able to send messages to the non-stalled associations โœจ

But a non-blocking socket isn't enough if the API implementer chose a single shared buffer allocation for the socket ๐Ÿฅฒ A single stalled association would eventually fill up the shared allocation, and then it would be impossible to send even to the non-stalled associations ๐Ÿ’€

The API implementer can fix this by giving each association its own allocation of outbound buffer space ๐Ÿ’– Conceptually, each association should have as much buffer space as it would have if it had its own socket ๐Ÿซง As a bonus, this also simplifies the implementation of sctp_peeloff() ๐ŸŽ€

To make sure one stalled association doesn't stop the other non-stalled associations from being writable, application programmers should either:

  • ask the underlying implementation to dedicate independent buffer space reservation to each association (as suggested above), or
  • verify that their application-layer protocol does not permit large amounts of unread data at the receiver (this is true of some request-response protocols, for example), or
  • use one-to-one style sockets for association, which may potentially stall (either from the beginning, or by using sctp_peeloff() before sending large amounts of data that may cause a stalled condition).

4. One-to-One Style Interface ๐ŸŽ€

The goal of this style is to follow the current practice of using the sockets interface for a connection-oriented protocol such as TCP as closely as possible ๐Ÿ’… That means existing apps that use connection-oriented protocols can be ported to SCTP with very little effort ๐ŸŒˆ

One-to-one style sockets can be connected (explicitly or implicitly) at most once, similar to TCP sockets ๐Ÿ”—

Note that some new SCTP features and some new SCTP socket options can only be used through sendmsg() and recvmsg() calls. See Section 4.1.8 ๐Ÿ“ฌ

4.1. Basic Operation ๐ŸŒธ

A typical one-to-one style server uses this system call sequence to get an SCTP endpoint ready for servicing requests ๐Ÿง

  • socket()
  • bind()
  • listen()
  • accept()

The accept() call blocks until a new association is set up ๐Ÿ™ˆ It returns with a new socket descriptor. The server then uses that new socket descriptor to talk to the client, using recv() and send() calls to get requests and send back responses ๐Ÿ’ฌ

Then it calls close() to terminate the association ๐Ÿ‘‹

A typical client uses this system call sequence to set up an association with a server to request services ๐Ÿ“ฎ

  • socket()
  • connect()

After returning from the connect() call, the client uses send()/sendmsg() and recv()/recvmsg() calls to send out requests and receive responses from the server ๐Ÿ“ฉ

The client calls close() to terminate this association when it's done ๐Ÿซถ

4.1.1. socket() โœจ

Applications call socket() to create a socket descriptor to represent an SCTP endpoint ๐Ÿฆ‹

The function prototype is:

int socket(int domain,
           int type,
           int protocol);

and one uses PF_INET or PF_INET6 as the domain, SOCK_STREAM as the type, and IPPROTO_SCTP as the protocol ๐Ÿ’–

Here, SOCK_STREAM indicates the creation of a one-to-one style socket ๐ŸŒท

Using the PF_INET domain indicates the creation of an endpoint that can use only IPv4 addresses, while PF_INET6 creates an endpoint that can use both IPv6 and IPv4 addresses ๐ŸŒˆ

4.1.2. bind() ๐Ÿ’…

Applications use bind() to specify with which local address and port the SCTP endpoint should associate itself ๐Ÿ“

An SCTP endpoint can be associated with multiple addresses ๐ŸŒ To do this, sctp_bindx() is introduced in Section 9.1 to help applications do the job of associating multiple addresses ๐Ÿงฉ But note that an endpoint can only be associated with one local port ๐Ÿšช

These addresses associated with a socket are the eligible transport addresses for the endpoint to send and receive data ๐Ÿ“ฆ The endpoint will also present these addresses to its peers during the association initialization process. See [RFC4960] ๐Ÿค

The function prototype of bind() is:

int bind(int sd,
         struct sockaddr *addr,
         socklen_t addrlen);

and the arguments are:

  • sd: The socket descriptor returned by socket().
  • addr: The address structure (struct sockaddr_in for an IPv4 address or struct sockaddr_in6 for an IPv6 address; see [RFC3493]).
  • addrlen: The size of the address structure.

If sd is an IPv4 socket, the address passed must be an IPv4 address. If sd is an IPv6 socket, the address passed can be either an IPv4 or an IPv6 address ๐ŸŒŠ

Applications cannot call bind() multiple times to associate multiple addresses to the endpoint. After the first call to bind(), all subsequent calls will return an error ๐Ÿšซ

If the IP address part of addr is specified as a wildcard (INADDR_ANY for an IPv4 address, or as IN6ADDR_ANY_INIT or in6addr_any for an IPv6 address), the operating system will associate the endpoint with an optimal address set of the available interfaces ๐ŸŒ If the IPv4 sin_port or IPv6 sin6_port is set to 0, the operating system will choose an ephemeral port for the endpoint ๐ŸŽฒ

If bind() is not called prior to the connect() call, the system picks an ephemeral port and will choose an address set equivalent to binding with a wildcard address ๐ŸŽฐ One of these addresses will be the primary address for the association. This automatically enables the multi-homing capability of SCTP ๐Ÿ 

The completion of this bind() process does not allow the SCTP endpoint to accept inbound SCTP association requests ๐Ÿ™… Until a listen() system call, described below, is performed on the socket, the SCTP endpoint will promptly reject an inbound SCTP INIT request with an SCTP ABORT ๐Ÿ’จ

4.1.3. listen() ๐Ÿ‘‚

Applications use listen() to allow the SCTP endpoint to accept inbound associations ๐Ÿ›Ž๏ธ

The function prototype is:

int listen(int sd,
           int backlog);

and the arguments are:

  • sd: The socket descriptor of the SCTP endpoint.
  • backlog: Specifies the max number of outstanding associations allowed in the socket's accept queue. These are the associations that have finished the four-way initiation handshake (see Section 5 of [RFC4960]) and are in the ESTABLISHED state. Note that a backlog of '0' indicates that the caller no longer wishes to receive new associations.

listen() returns 0 on success and -1 in case of an error ๐ŸŽฏ

4.1.4. accept() ๐Ÿคฉ

Applications use the accept() call to remove an established SCTP association from the accept queue of the endpoint ๐Ÿšถ A new socket descriptor will be returned from accept() to represent the newly formed association ๐Ÿ†•

The function prototype is:

int accept(int sd,
           struct sockaddr *addr,
           socklen_t *addrlen);

and the arguments are:

  • sd: The listening socket descriptor.
  • addr: On return, addr (struct sockaddr_in for an IPv4 address or struct sockaddr_in6 for an IPv6 address; see [RFC3493]) will contain the primary address of the peer endpoint.
  • addrlen: On return, addrlen will contain the size of addr.

The function returns the socket descriptor for the newly formed association on success and -1 in case of an error ๐Ÿงพ

4.1.5. connect() ๐Ÿ”Œ

Applications use connect() to initiate an association to a peer ๐Ÿค

The function prototype is:

int connect(int sd,
            const struct sockaddr *addr,
            socklen_t addrlen);

and the arguments are:

  • sd: The socket descriptor of the endpoint.
  • addr: The peer's (struct sockaddr_in for an IPv4 address or struct sockaddr_in6 for an IPv6 address; see [RFC3493]) address.
  • addrlen: The size of the address.

connect() returns 0 on success and -1 on error ๐Ÿ“ฌ

This operation corresponds to the ASSOCIATE primitive described in Section 10.1 of [RFC4960] ๐Ÿ’ซ

The number of outbound streams the new association has is stack dependent. Before connecting, applications can use the SCTP_INITMSG option described in Section 8.1.3 to change the number of outbound streams ๐ŸŒŠ

If bind() is not called prior to the connect() call, the system picks an ephemeral port and will choose an address set equivalent to binding with INADDR_ANY and IN6ADDR_ANY_INIT for IPv4 and IPv6 sockets, respectively. One of the addresses will be the primary address for the association. This automatically enables the multi-homing capability of SCTP ๐Ÿก

Note that SCTP allows data exchange, similar to T/TCP [RFC1644] (made Historic by [RFC6247]), during the association setup phase ๐ŸŽ If an application wants to do this, it cannot use the connect() call. Instead, it should use sendto() or sendmsg() to initiate an association. If it uses sendto() and wants to change the initialization behavior, it needs to use the SCTP_INITMSG socket option before calling sendto(). Or it can use sendmsg() with SCTP_INIT type ancillary data to initiate an association without calling setsockopt(). Note that the implicit setup is supported for the one-to-one style sockets ๐Ÿง 

SCTP does not support half close semantics. This means that unlike T/TCP, MSG_EOF should not be set in the flags parameter when calling sendto() or sendmsg() when the call is used to initiate a connection. MSG_EOF is not an acceptable flag with an SCTP socket ๐Ÿšซ๐Ÿ’…

4.1.6. close() ๐Ÿ’…

okay so close() is how you gracefully wrap up an association ๐ŸŽ€ like a clean exit from the group chat, no drama ๐Ÿซถ

The function prototype is

int close(int sd);

and the argument is

  • sd: The socket descriptor of the association to be closed.

close() returns 0 on success and -1 in case of an error. โœจ

After an application calls close() on a socket descriptor, no further socket operations will succeed on that descriptor. ๐Ÿ”’ it's giving "we're done here, bestie" energy ๐Ÿ’€

4.1.7. shutdown() ๐Ÿฆ‹

okay this one is lowkey the biggest plot twist ๐Ÿ˜ญ SCTP does NOT have half close semantics, unlike TCP. so the shutdown() call in SCTP is more of an approximation of the TCP one, and it solves some different problems ๐ŸŒธ full TCP compatibility is not provided, so if you're porting a TCP app to SCTP, you may need to recode the parts that use shutdown() ๐Ÿ™ˆ (ngl that's the kind of thing that ruins your whole afternoon). also, fun fact, it IS possible to get the same results as half close in SCTP by using SCTP streams ๐Ÿงš

The function prototype is

int shutdown(int sd,
             int how);

and the arguments are

  • sd: The socket descriptor of the association to be closed.
  • how: Specifies the type of shutdown. The values are as follows:
    • SHUT_RD: Disables further receive operations. No SCTP protocol action is taken.
    • SHUT_WR: Disables further send operations, and initiates the SCTP shutdown sequence.
    • SHUT_RDWR: Disables further send and receive operations, and initiates the SCTP shutdown sequence.

shutdown() returns 0 on success and -1 in case of an error. ๐Ÿ“

the major difference between SCTP and TCP shutdown() is that SCTP's SHUT_WR initiates an immediate and full protocol shutdown ๐Ÿ˜ณ whereas TCP's SHUT_WR causes TCP to go into the half close state. SHUT_RD behaves the same for SCTP as it does for TCP โœจ the whole point of SCTP's SHUT_WR is to close the SCTP association while still leaving the socket descriptor open. this is so the caller can receive back any data that SCTP is unable to deliver (see Section 6.1.4 for more information) and receive event notifications. main character energy, the socket stays alive to catch the leftovers ๐Ÿ‘‘

to perform the ABORT operation described in Section 10.1 of [RFC4960], an application can use the socket option SO_LINGER. SO_LINGER is described in Section 8.1.4. ๐Ÿ’œ

4.1.8. sendmsg() and recvmsg() ๐ŸŒˆ

with a one-to-one style socket, the application can also use sendmsg() and recvmsg() to send data to and receive data from its peer. the semantics is similar to what you get in the one-to-many style (see Section 3.1.4), with the following differences:

  1. When sending, the msg_name field in the msghdr is not used to specify the intended receiver. instead, it is used to indicate a preferred peer address, if the sender wants to discourage the stack from sending the message to the receiver's primary address. if the socket is connected and the transport address given is not part of the current association, the data will not be sent, and an SCTP_SEND_FAILED_EVENT event will be delivered to the application if send failure events are enabled. ๐Ÿ™ƒ
  2. Using sendmsg() on a non-connected one-to-one style socket for implicit connection setup may or may not work, depending on the SCTP implementation. ๐Ÿคทโ€โ™€๏ธ

4.1.9. getpeername() ๐ŸŽ€

applications use getpeername() to retrieve the primary socket address of the peer. this one is for TCP compatibility and is not multi-homed ๐ŸŒธ it may not work with one-to-many style sockets, depending on the implementation. see Section 9.3 for a multi-homed style version of the call. โœจ

The function prototype is

int getpeername(int sd,
                struct sockaddr *address,
                socklen_t *len);

and the arguments are

  • sd: The socket descriptor to be queried.
  • address: On return, the peer primary address is stored in this buffer. If the socket is an IPv4 socket, the address will be IPv4. If the socket is an IPv6 socket, the address will be either an IPv6 or IPv4 address.
  • len: The caller should set the length of address here. On return, this is set to the length of the returned address.

getpeername() returns 0 on success and -1 in case of an error. ๐Ÿ’…

if the actual length of the address is greater than the length of the supplied sockaddr structure, the stored address will be truncated. oof ๐Ÿ˜ญ so make your buffer big enough, bestie.


5. Data Structures ๐Ÿงš

this section is all about the important data structures that are specific to SCTP. they're used with sendmsg() and recvmsg() calls to control SCTP endpoint operations and to access ancillary information and notifications. it's giving control panel ๐ŸŽ›๏ธ

5.1. The msghdr and cmsghdr Structures ๐Ÿ’–

the msghdr structure used in the sendmsg() and recvmsg() calls, along with the ancillary data it carries, is the key for the application to set and get various control information from the SCTP endpoint. literally the main way to talk to the stack ๐Ÿซถ

the msghdr and the related cmsghdr structures are defined and discussed in detail in [RFC3542]. they are defined as

struct msghdr {
  void *msg_name;           /* ptr to socket address structure */
  socklen_t msg_namelen;    /* size of socket address structure */
  struct iovec *msg_iov;    /* scatter/gather array */
  int msg_iovlen;           /* # elements in msg_iov */
  void *msg_control;        /* ancillary data */
  socklen_t msg_controllen; /* ancillary data buffer length */
  int msg_flags;            /* flags on received message */
};

struct cmsghdr {
  socklen_t cmsg_len; /* # bytes, including this header */
  int cmsg_level;     /* originating protocol */
  int cmsg_type;      /* protocol-specific type */
                      /* followed by unsigned char cmsg_data[]; */
};

in the msghdr structure, the usage of msg_name has been discussed in previous sections (see Sections 3.1.4 and 4.1.8). โœจ

the scatter/gather buffers, or I/O vectors (pointed to by the msg_iov field), are treated by SCTP as a single user message for both sendmsg() and recvmsg(). ๐Ÿฆ‹

the SCTP stack uses the ancillary data (msg_control field) to communicate the attributes, such as SCTP_RCVINFO, of the message stored in msg_iov to the socket endpoint. the different ancillary data types are described in Section 5.3. ๐Ÿ’œ

the msg_flags are not used when sending a message with sendmsg(). ๐Ÿ™ˆ

if a notification has arrived, recvmsg() will return the notification in the msg_iov field and set the MSG_NOTIFICATION flag in msg_flags. if the MSG_NOTIFICATION flag is not set, recvmsg() will return data. see Section 6 for more information about notifications. ๐Ÿ””

if all portions of a data frame or notification have been read, recvmsg() will return with MSG_EOR set in msg_flags. ๐Ÿ

5.2. Ancillary Data Considerations and Semantics ๐ŸŒธ

programming with ancillary socket data (msg_control) has some subtleties and pitfalls, which are discussed below. ngl this part is where people get messy ๐Ÿ˜ญ so pay attention ๐Ÿ’…

5.2.1. Multiple Items and Ordering ๐ŸŽ€

multiple ancillary data items may be included in any call to sendmsg() or recvmsg(). these may include multiple SCTP items, non-SCTP items (such as IP-level items), or both. ๐Ÿ“

the ordering of ancillary data items (whether they come from SCTP or another protocol) is not significant and is implementation dependent, so applications must not depend on any ordering. ๐Ÿ™…โ€โ™€๏ธ don't build your whole life on the order, fr fr.

SCTP_SNDRCV/SCTP_SNDINFO/SCTP_RCVINFO type ancillary data always corresponds to the data in the msghdr's msg_iov member. there can be only one such type of ancillary data for each sendmsg() or recvmsg() call. โœจ

5.2.2. Accessing and Manipulating Ancillary Data ๐Ÿงš

applications can infer the presence of data or ancillary data by examining the msg_iovlen and msg_controllen msghdr members, respectively. ๐Ÿ‘€

implementations may have different padding requirements for ancillary data, so portable applications should make use of the macros CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_SPACE, and CMSG_LEN. see [RFC3542] and the SCTP implementation's documentation for more information. the following is an example, from [RFC3542], demonstrating how to use these macros to access ancillary data: ๐Ÿซถ

struct msghdr msg;
struct cmsghdr *cmsgptr;

/* fill in msg */

/* call recvmsg() */

for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
     cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
  if (cmsgptr->cmsg_len == 0) {
     /* Error handling */
     break;
  }
  if (cmsgptr->cmsg_level == ... && cmsgptr->cmsg_type == ... ) {
    u_char  *ptr;

    ptr = CMSG_DATA(cmsgptr);
    /* process data pointed to by ptr */
  }
}

5.2.3. Control Message Buffer Sizing ๐Ÿ’œ

the information conveyed via SCTP_SNDRCV/SCTP_SNDINFO/SCTP_RCVINFO ancillary data will often be fundamental to the correct and sane operation of the sockets application. this is especially true for one-to-many style sockets, but it also applies to one-to-one style sockets. for example, if an application needs to send and receive data on different SCTP streams, SCTP_SNDRCV/SCTP_SNDINFO/SCTP_RCVINFO ancillary data is indispensable. ๐ŸŒˆ

given that some ancillary data is critical, and that multiple ancillary data items may appear in any order, applications should be carefully written to always provide a large enough buffer to contain all possible ancillary data that can be presented by recvmsg(). if the buffer is too small and crucial data gets truncated, it may pose a fatal error condition. ๐Ÿ˜ฑ

thus, it is essential that applications be able to deterministically calculate the maximum required buffer size to pass to recvmsg(). one constraint imposed on this specification that makes this possible is that all ancillary data definitions are of a fixed length. ๐Ÿ”ข one way to calculate the maximum required buffer size might be to take the sum of the sizes of all enabled ancillary data item structures, as calculated by CMSG_SPACE. for example, if we enabled SCTP_SNDRCV_INFO and IPV6_RECVPKTINFO [RFC3542], we would calculate and allocate the buffer size as follows: ๐Ÿงฎ

size_t total;
void *buf;

total = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo)) +
        CMSG_SPACE(sizeof(struct in6_pktinfo));

buf = malloc(total);

we could then use this buffer (buf) for msg_control on each call to recvmsg() and be assured that we would not lose any ancillary data to truncation. ๐ŸŽ‰ bestie, that's the whole dream โœจ

5.3. SCTP msg_control Structures ๐Ÿ’…

Okay bestie, every SCTP-specific socket extension runs on ancillary data ๐ŸŽ€ That's how you tell it what you want and read back what you got. It rides in the msg_control member of the msghdr structure, which gets used in sendmsg() and recvmsg(). Fine-grained control over initialization and sending parameters all happens through ancillary data โœจ

Each ancillary data item starts with a struct cmsghdr (see Section 5.1). That header is what tells you the function and purpose of whatever is in the cmsg_data[] member ๐Ÿฆ‹

By default, on either style of socket, SCTP passes no ancillary data ๐Ÿ™ˆ If you want specific items, you turn them on with socket options defined for SCTP. See Section 6.2.

Heads up: every ancillary type is of fixed length, so see Section 5.2 for more on that ๐Ÿ”ฅ These data structures use struct sockaddr_storage (defined in [RFC3493]) as a portable, fixed-length address format ๐Ÿ’œ

Other protocols can also hand ancillary data up to the socket layer ๐ŸŒˆ Those items can mix right in with SCTP's. For example, the IPv6 sockets API definitions ([RFC3542] and [RFC3493]) define a whole bunch of ancillary data items. If a sockets API consumer turns on delivery of both SCTP and IPv6 ancillary data, they can both show up in the same msg_control buffer, in any order. So an app may have to handle ancillary data types that aren't from SCTP at all ๐Ÿ’€

The sockets application must provide a buffer big enough to fit all the ancillary data recvmsg() hands back. If the buffer isn't big enough, the ancillary data gets truncated and the msghdr's msg_flags will include MSG_CTRUNC ๐Ÿ˜ญ


5.3.1. SCTP Initiation Structure (SCTP_INIT)

This cmsghdr structure carries the info for setting up new SCTP associations with sendmsg(). The SCTP_INITMSG socket option uses this exact same data structure ๐Ÿ“ It is not used for recvmsg(), so skip it there โœจ

+--------------+-----------+---------------------+
| cmsg_level   | cmsg_type | cmsg_data[]         |
+--------------+-----------+---------------------+
| IPPROTO_SCTP | SCTP_INIT | struct sctp_initmsg |
+--------------+-----------+---------------------+

Here's the sctp_initmsg structure:

struct sctp_initmsg {
  uint16_t sinit_num_ostreams;
  uint16_t sinit_max_instreams;
  uint16_t sinit_max_attempts;
  uint16_t sinit_max_init_timeo;
};
  • sinit_num_ostreams: This is an integer for how many streams the app wants to be able to send on. The SCTP_COMM_UP notification confirms this number, and you must verify it, because it's negotiated with the remote endpoint ๐Ÿค A default value of 0 means you get the endpoint's default value.
  • sinit_max_instreams: This is the max number of inbound streams the app is ready to support. The actual implementation sets the limit, so the user might be able to support more streams than the operating system allows ๐Ÿ™ƒ If that happens, the operating-system limit wins over whatever the user asked for. A default value of 0 means you get the endpoint's default value.
  • sinit_max_attempts: This integer says how many times the SCTP endpoint should try resending the INIT. It overrides the system SCTP Max.Init.Retransmits value ๐Ÿ’… A default value of 0 means the endpoint's default value gets used. This is normally set to the system's default Max.Init.Retransmit value.
  • sinit_max_init_timeo: This is the largest timeout, or retransmission timeout (RTO), in milliseconds, to use when attempting an INIT โฐ Normally, RTO.Max caps how much the RTO can double on each timeout. For the INIT message, this value may override RTO.Max. Still, it must not influence RTO.Max during data transmission, since it's only there to bound the initial setup time ๐ŸŒธ A default value of 0 means the endpoint's default value. This is normally set to the system's RTO.Max value (60 seconds).

5.3.2. SCTP Header Information Structure (SCTP_SNDRCV) - DEPRECATED

Deprecated alert ๐Ÿšจ This one's been replaced, so use the newer structures when you can.

This cmsghdr structure sets SCTP options for sendmsg() and describes the SCTP header info of a received message through recvmsg(). It mixes the send and receive paths together ๐ŸŒ€ SCTP_SNDINFO (described in Section 5.3.4) and SCTP_RCVINFO (described in Section 5.3.5) split all of this up. Since SCTP_SNDRCV is deprecated, you should use those structures when possible ๐Ÿ‘‘

+--------------+-------------+------------------------+
| cmsg_level   | cmsg_type   | cmsg_data[]            |
+--------------+-------------+------------------------+
| IPPROTO_SCTP | SCTP_SNDRCV | struct sctp_sndrcvinfo |
+--------------+-------------+------------------------+

Here's the sctp_sndrcvinfo structure:

struct sctp_sndrcvinfo {
  uint16_t sinfo_stream;
  uint16_t sinfo_ssn;
  uint16_t sinfo_flags;
  uint32_t sinfo_ppid;
  uint32_t sinfo_context;
  uint32_t sinfo_timetolive;
  uint32_t sinfo_tsn;
  uint32_t sinfo_cumtsn;
  sctp_assoc_t sinfo_assoc_id;
};
  • sinfo_stream: For recvmsg(), the SCTP stack puts the message's stream number in here. For sendmsg(), this holds the stream number the app wants to send the message on. If the sender picks an invalid stream number, an error indication comes back and the call fails ๐Ÿ™…โ€โ™€๏ธ
  • sinfo_ssn: For recvmsg(), this has the stream sequence number the remote endpoint put in the DATA chunk. For fragmented messages, it's the same number for every delivery of the message (if you need more than one recvmsg() to read it all) ๐Ÿงฉ sendmsg() ignores this parameter.
  • sinfo_flags: This field can hold any of the following flags, combined with a bitwise OR ๐Ÿ’–
    • recvmsg() flags:
      • SCTP_UNORDERED: This flag shows up when the message was sent unordered.
    • sendmsg() flags:
      • SCTP_UNORDERED: This flag requests unordered delivery of the message. If this flag is clear, the datagram counts as an ordered send.
      • SCTP_ADDR_OVER: For a one-to-many style socket, this flag asks the SCTP stack to override the primary destination address with the address from the sendto/sendmsg call ๐Ÿฆ‹
      • SCTP_ABORT: Setting this flag makes the specified association abort by sending an ABORT message to the peer ๐Ÿ’€ The ABORT chunk carries an error cause of User Initiated Abort with cause code 12. The cause-specific info for this error cause is provided in msg_iov.
      • SCTP_EOF: Setting this flag kicks off the SCTP graceful shutdown procedure on the specified association ๐ŸŒ™ Graceful shutdown makes sure all data queued by both endpoints gets successfully transmitted before the association closes.
      • SCTP_SENDALL: If this flag is set, a one-to-many style socket sends the message to every association currently established on that socket ๐ŸŒˆ For the one-to-one style socket, this flag has no effect.
  • sinfo_ppid: In sendmsg(), this is an unsigned integer that gets passed to the remote end in each user message ๐ŸŽ€ In recvmsg(), it's the same info the upper layer passed in from the peer application. Heads up: the SCTP stack does no byte order modification on this field ๐Ÿ™ˆ So if the DATA chunk has to carry a given value in network byte order, the SCTP user has to do the htonl() computation.
  • sinfo_context: This is an opaque 32-bit context datum used in the sendmsg() function. If an error happens while sending a message, this value gets passed back to the upper layer, and it's retrieved with each undelivered message ๐Ÿ“ฆ
  • sinfo_timetolive: On the sending side, this is the message's time to live, in milliseconds โณ The sending side expires the message within that time period if it hasn't been sent to the peer yet. This value overrides any default set with a socket option. Also, a value of 0 is special, since it means no timeout should happen on this message โ™พ๏ธ
  • sinfo_tsn: On the receiving side, this holds a Transmission Sequence Number (TSN) that was assigned to one of the SCTP DATA chunks ๐Ÿ”ข On the sending side, it's ignored.
  • sinfo_cumtsn: This holds the current cumulative TSN as the underlying SCTP layer knows it. It's ignored when sending ๐Ÿงฎ
  • sinfo_assoc_id: The association handle, sinfo_assoc_id, holds the identifier for the association announced in the SCTP_COMM_UP notification ๐Ÿฆ‹ Every notification for a given association has the same identifier. This field is ignored for one-to-one style sockets.

An sctp_sndrcvinfo item always corresponds to the data in msg_iov. ๐Ÿซถ

5.3.3. Extended SCTP Header Information Structure (SCTP_EXTRCV) - DEPRECATED ๐Ÿ’…

okay so this one is the upgraded version of SCTP_SNDRCV (see Section 5.3.2) ๐Ÿฆ‹ it tells you about a received message through recvmsg(), but you ONLY get it if you've set the socket option SCTP_USE_EXT_RCVINFO (see Section 8.1.22) to true, AND you've subscribed to whatever events you need to get ancillary data in the first place ๐Ÿซถ also, heads up bestie: next-message info isn't valid until the current message is fully read, aka until MSG_EOR is set. so if you still have stuff left to read from the current message, no next-message info for you yet ๐Ÿ™ˆ

SCTP_NXTINFO (described in Section 5.3.6) should be used when possible, since SCTP_EXTRCV is considered deprecated. the new girl is better, periodt โœจ

+--------------+-------------+------------------------+
| cmsg_level   | cmsg_type   | cmsg_data[]            |
+--------------+-------------+------------------------+
| IPPROTO_SCTP | SCTP_EXTRCV | struct sctp_extrcvinfo |
+--------------+-------------+------------------------+

the sctp_extrcvinfo structure looks like this ๐Ÿ‘‘

struct sctp_extrcvinfo {
  uint16_t sinfo_stream;
  uint16_t sinfo_ssn;
  uint16_t sinfo_flags;
  uint32_t sinfo_ppid;
  uint32_t sinfo_context;
  uint32_t sinfo_pr_value;
  uint32_t sinfo_tsn;
  uint32_t sinfo_cumtsn;
  uint16_t serinfo_next_flags;
  uint16_t serinfo_next_stream;
  uint32_t serinfo_next_aid;
  uint32_t serinfo_next_length;
  uint32_t serinfo_next_ppid;
  sctp_assoc_t sinfo_assoc_id;
};
  • sinfo_*: the sinfo fields are the same ones you already know from Section 5.3.2, so go check those out for the details ๐Ÿ“
  • serinfo_next_flags: this bitmask can hold one or more of the following values ๐ŸŒธ
    • SCTP_NEXT_MSG_AVAIL: when this bit is 1, next-message info is available, so serinfo_next_stream, serinfo_next_aid, serinfo_next_length, and serinfo_next_ppid all have valid values. if it's 0, those fields aren't valid and you should ignore them ๐Ÿ™…โ€โ™€๏ธ
    • SCTP_NEXT_MSG_ISCOMPLETE: when this bit is set, the next message is fully sitting in the receive buffer, so serinfo_next_length has the entire message size. if it's 0, then serinfo_next_length only has part of the size, because the message is still coming in (it's being partially delivered) ๐Ÿ“ฆ
    • SCTP_NEXT_MSG_IS_UNORDERED: when this bit is set, the next message was sent by the peer as unordered. if it's not set (aka the bit is 0), the next message you read is an ordered message in the stream you're looking at ๐Ÿ”ฅ
    • SCTP_NEXT_MSG_IS_NOTIFICATION: when this bit is set, the next message is NOT from the peer. it's a MSG_NOTIFICATION from your own local SCTP stack ๐Ÿงš
  • serinfo_next_stream: when this is valid (check serinfo_next_flags), it holds the next stream number that'll come through on a later call to one of the receive message functions ๐Ÿ’œ
  • serinfo_next_aid: when this is valid (check serinfo_next_flags), it holds the next association identifier that'll be received on a later call to one of the receive message functions ๐ŸŒˆ
  • serinfo_next_length: when this is valid (check serinfo_next_flags), it's the length of the next message that'll be received on a later call to one of the receive message functions. heads up, this length might be partial, depending on the settings in next_flags ๐ŸŽ€
  • serinfo_next_ppid: when this is valid (check serinfo_next_flags), it's the ppid of the next message that'll be received on a later call to one of the receive message functions ๐Ÿฆ‹

5.3.4. SCTP Send Information Structure (SCTP_SNDINFO) ๐ŸŒธ

this cmsghdr structure is how you specify SCTP options for sendmsg(). it's giving "tell the stack exactly how to send my stuff" ๐Ÿ’…

+--------------+--------------+---------------------+
| cmsg_level   | cmsg_type    | cmsg_data[]         |
+--------------+--------------+---------------------+
| IPPROTO_SCTP | SCTP_SNDINFO | struct sctp_sndinfo |
+--------------+--------------+---------------------+

the sctp_sndinfo structure is defined like so โœจ

struct sctp_sndinfo {
  uint16_t snd_sid;
  uint16_t snd_flags;
  uint32_t snd_ppid;
  uint32_t snd_context;
  sctp_assoc_t snd_assoc_id;
};
  • snd_sid: this is the stream number you want to send this message on. if you hand over an invalid stream number, you get an error indication back and the call fails. bestie, don't do that ๐Ÿ˜ญ
  • snd_flags: this field can hold any of the following flags, combined with a bitwise OR ๐Ÿงš
    • SCTP_UNORDERED: requests unordered delivery of the message. if this flag is clear, the datagram is treated as an ordered send ๐Ÿ”ฅ
    • SCTP_ADDR_OVER: for a one-to-many style socket, this asks the SCTP stack to override the primary destination address with the address you gave to the sendto()/sendmsg call ๐Ÿฆ‹
    • SCTP_ABORT: setting this makes the specified association abort by sending an ABORT message to the peer. the ABORT chunk carries an error cause of 'User Initiated Abort' with cause code 12. the cause-specific info for that error cause comes from msg_iov ๐Ÿ’€
    • SCTP_EOF: setting this kicks off the SCTP graceful shutdown procedures on the specified association. graceful shutdown makes sure all data queued by both endpoints gets successfully sent before the association closes ๐Ÿซถ
    • SCTP_SENDALL: if set, a one-to-many style socket sends the message to every association that's currently established on that socket. for the one-to-one style socket, this flag does nothing ๐Ÿ‘‘
  • snd_ppid: in sendmsg(), this is an unsigned integer that gets passed to the remote end in each user message. just a heads up, the SCTP stack does NOT touch the byte order of this field. so if your DATA chunk needs a given value in network byte order, you've gotta do the htonl() computation yourself ๐ŸŒˆ
  • snd_context: an opaque 32-bit context datum used in the sendmsg() function. if there's an error sending a message, this value gets passed back to the upper layer, and it's retrieved with each undelivered message ๐Ÿ“
  • snd_assoc_id: the association handle field, sinfo_assoc_id, holds the identifier for the association announced in the SCTP_COMM_UP notification. every notification for a given association has the same identifier. this field gets ignored for one-to-one style sockets ๐ŸŽ€

an sctp_sndinfo item always corresponds to the data in msg_iov ๐Ÿ’–


5.3.5. SCTP Receive Information Structure (SCTP_RCVINFO) ๐Ÿงš

this cmsghdr structure describes SCTP receive info about a message you got through recvmsg(). it's basically the receiving side's receipt ๐Ÿ’…

to actually get this info, you need to use the SCTP_RECVRCVINFO socket option (see Section 8.1.29). no option, no info, no cap ๐Ÿ™ˆ

+--------------+--------------+---------------------+
| cmsg_level   | cmsg_type    | cmsg_data[]         |
+--------------+--------------+---------------------+
| IPPROTO_SCTP | SCTP_RCVINFO | struct sctp_rcvinfo |
+--------------+--------------+---------------------+

the sctp_rcvinfo structure is defined below ๐Ÿฆ‹

struct sctp_rcvinfo {
  uint16_t rcv_sid;
  uint16_t rcv_ssn;
  uint16_t rcv_flags;
  uint32_t rcv_ppid;
  uint32_t rcv_tsn;
  uint32_t rcv_cumtsn;
  uint32_t rcv_context;
  sctp_assoc_t rcv_assoc_id;
};
  • rcv_sid: the SCTP stack puts the message's stream number in this value ๐Ÿ’œ
  • rcv_ssn: this holds the stream sequence number the remote endpoint put in the DATA chunk. for fragmented messages, it's the same number for every delivery of that message (if you need more than one recvmsg() to read the whole thing) ๐Ÿ“ฆ
  • rcv_flags: this field can hold any of the following flags, combined with a bitwise OR ๐ŸŒธ
    • SCTP_UNORDERED: this flag shows up when the message was sent unordered ๐Ÿ”ฅ
  • rcv_ppid: this is the same info the upper layer passed in on the peer application's side. again, the SCTP stack does NOT change the byte order here, so if the DATA chunk has a value in network byte order, you need to do the ntohl() computation yourself ๐ŸŒˆ
  • rcv_tsn: this field holds a TSN that got assigned to one of the SCTP DATA chunks ๐ŸŽ€
  • rcv_cumtsn: this field holds the current cumulative TSN as the underlying SCTP layer knows it ๐Ÿ‘‘
  • rcv_context: an opaque 32-bit context datum that you set with the SCTP_CONTEXT socket option. if there's an error sending a message, this value gets passed back to the upper layer, and it's retrieved with each undelivered message ๐Ÿ“
  • rcv_assoc_id: the association handle field, sinfo_assoc_id, holds the identifier for the association announced in the SCTP_COMM_UP notification. every notification for a given association has the same identifier. this field gets ignored for one-to-one style sockets ๐Ÿฆ‹

an sctp_rcvinfo item always corresponds to the data in msg_iov โœจ

5.3.6. SCTP Next Receive Information Structure (SCTP_NXTINFO) ๐Ÿ”ฎ

okay so this cmsghdr structure tells you about the SCTP receive info of the next message, the one recvmsg() will hand you after this one, if that info is already sitting there while you're handling the current message. it's giving sneak peek ๐Ÿ‘€โœจ

to turn this on, your app must use the SCTP_RECVNXTINFO socket option (see Section 8.1.30) ๐Ÿ’…

        +--------------+--------------+---------------------+
        | cmsg_level   | cmsg_type    | cmsg_data[]         |
        +--------------+--------------+---------------------+
        | IPPROTO_SCTP | SCTP_NXTINFO | struct sctp_nxtinfo |
        +--------------+--------------+---------------------+

here's the sctp_nxtinfo struct ๐ŸŽ€

struct sctp_nxtinfo {
  uint16_t nxt_sid;
  uint16_t nxt_flags;
  uint32_t nxt_ppid;
  uint32_t nxt_length;
  sctp_assoc_t nxt_assoc_id;
};
  • nxt_sid: the SCTP stack puts the next message's stream number in here ๐ŸŒธ
  • nxt_flags: this one can hold any of the flags below, combined with a bitwise OR ๐Ÿงฉ
    • SCTP_UNORDERED: shows up when the next message was sent unordered ๐ŸŒ€
    • SCTP_COMPLETE: means the whole message has arrived and is in the socket buffer. this one is lowkey important for nxt_length, so read the nxt_length bit below ๐Ÿ‘‡
    • SCTP_NOTIFICATION: shows up when the next message is a notification and not a user message ๐Ÿ””
  • nxt_ppid: this is the same info the upper layer on the peer app passed along for the next message. the SCTP stack does NOT touch the byte order of this field, so if your DATA chunk needs a certain value in network byte order, you have to do the ntohl() conversion yourself ๐Ÿ™ˆ
  • nxt_length: the length of the message currently sitting in the socket buffer. this might NOT be the entire length of the message, since a partial delivery could be in progress. it only counts as the full size of the next message when SCTP_COMPLETE is set in nxt_flags ๐Ÿ“
  • nxt_assoc_id: the association handle of the next message. it holds the identifier for the association that was announced in the SCTP_COMM_UP notification, and every notification for a given association has that same identifier ๐Ÿซถ this one gets ignored for one-to-one style sockets

5.3.7. SCTP PR-SCTP Information Structure (SCTP_PRINFO) ๐Ÿง 

this cmsghdr structure sets SCTP options for sendmsg() ๐Ÿ“ค

         +--------------+-------------+--------------------+
         | cmsg_level   | cmsg_type   | cmsg_data[]        |
         +--------------+-------------+--------------------+
         | IPPROTO_SCTP | SCTP_PRINFO | struct sctp_prinfo |
         +--------------+-------------+--------------------+

here's the sctp_prinfo struct โœจ

struct sctp_prinfo {
  uint16_t pr_policy;
  uint32_t pr_value;
};
  • pr_policy: this picks which Partially Reliable SCTP (PR-SCTP) policy gets used. using SCTP_PR_SCTP_NONE gives you a reliable transmission. when SCTP_PR_SCTP_TTL is used, the PR-SCTP policy "timed reliability" from [RFC3758] kicks in, and the lifetime goes in pr_value โณ
  • pr_value: what this means depends on the policy named in pr_policy. it gets ignored when SCTP_PR_SCTP_NONE is specified. with SCTP_PR_SCTP_TTL, the lifetime is given in milliseconds ๐Ÿ’–

an sctp_prinfo item always corresponds to the data in msg_iov ๐ŸŽ€


5.3.8. SCTP AUTH Information Structure (SCTP_AUTHINFO) ๐Ÿ”

this cmsghdr structure sets SCTP options for sendmsg() ๐Ÿ’Œ

        +--------------+---------------+----------------------+
        | cmsg_level   | cmsg_type     | cmsg_data[]          |
        +--------------+---------------+----------------------+
        | IPPROTO_SCTP | SCTP_AUTHINFO | struct sctp_authinfo |
        +--------------+---------------+----------------------+

here's the sctp_authinfo struct ๐Ÿฆ‹

struct sctp_authinfo {
  uint16_t auth_keynumber;
};
  • auth_keynumber: this specifies the shared key identifier used for sending the user message ๐Ÿ—๏ธ

an sctp_authinfo item always corresponds to the data in msg_iov. also, bestie, please note that the SCTP implementation must not bundle user messages that need to be authenticated using different shared key identifiers ๐Ÿšซ๐Ÿ“ฆ


5.3.9. SCTP Destination IPv4 Address Structure (SCTP_DSTADDRV4) ๐ŸŒ

this cmsghdr structure sets SCTP options for sendmsg() ๐Ÿš€

         +--------------+----------------+----------------+
         | cmsg_level   | cmsg_type      | cmsg_data[]    |
         +--------------+----------------+----------------+
         | IPPROTO_SCTP | SCTP_DSTADDRV4 | struct in_addr |
         +--------------+----------------+----------------+

this ancillary data lets you hand sendmsg() more than one destination address ๐ŸŽฏ it can be used to implement sctp_sendv() using sendmsg(), which is so iconic ๐Ÿ‘‘


5.3.10. SCTP Destination IPv6 Address Structure (SCTP_DSTADDRV6) ๐ŸŒ

this cmsghdr structure sets SCTP options for sendmsg() ๐Ÿ’œ

         +--------------+----------------+-----------------+
         | cmsg_level   | cmsg_type      | cmsg_data[]     |
         +--------------+----------------+-----------------+
         | IPPROTO_SCTP | SCTP_DSTADDRV6 | struct in6_addr |
         +--------------+----------------+-----------------+

same energy as the IPv4 one: this ancillary data lets you give sendmsg() more than one destination address ๐ŸŒˆ and yes, it can be used to implement sctp_sendv() using sendmsg() ๐Ÿ’…


6. SCTP Events and Notifications โœจ

okay so an SCTP app needs to understand and process events and errors that happen on the SCTP stack ๐Ÿ‘€ that includes network status changes, association startups, remote operational errors, and undeliverable messages ๐Ÿ“ก all of these can be essential for the app, no cap ๐Ÿซถ

when an SCTP app calls recvmsg(), the message it reads is normally a data message from a peer endpoint ๐Ÿ’ฌ if the app wants the SCTP stack to deliver notifications of non-data events, it sets the right socket option for the notifications it wants. see Section 6.2 for those socket options ๐Ÿ”ง when a notification shows up, recvmsg() returns it in the app-supplied data buffer via msg_iov and sets MSG_NOTIFICATION in msg_flags ๐Ÿ””

this section breaks down the notification structures. every notification structure carries some common fields that give general info ๐Ÿงš

a recvmsg() call returns only one notification at a time. just like with normal data, it may return part of a notification if the msg_iov buffer isn't big enough ๐Ÿ“ฆ if one read isn't enough, msg_flags will have MSG_EOR clear. the user must finish reading the notification before any subsequent data can arrive ๐Ÿ™…โ€โ™€๏ธ

6.1. SCTP Notification Structure ๐ŸŽ€

the notification structure is defined as the union of all notification types ๐ŸŒธ

union sctp_notification {
  struct sctp_tlv {
    uint16_t sn_type; /* Notification type. */
    uint16_t sn_flags;
    uint32_t sn_length;
  } sn_header;
  struct sctp_assoc_change sn_assoc_change;
  struct sctp_paddr_change sn_paddr_change;
  struct sctp_remote_error sn_remote_error;
  struct sctp_send_failed sn_send_failed;
  struct sctp_shutdown_event sn_shutdown_event;
  struct sctp_adaptation_event sn_adaptation_event;
  struct sctp_pdapi_event sn_pdapi_event;
  struct sctp_authkey_event sn_auth_event;
  struct sctp_sender_dry_event sn_sender_dry_event;
  struct sctp_send_failed_event sn_send_failed_event;
};
  • sn_type: this list describes the SCTP notification and event types for the field sn_type ๐Ÿท๏ธ
    • SCTP_ASSOC_CHANGE: means an association has either been opened or closed. see Section 6.1.1 for the deets ๐Ÿšช
    • SCTP_PEER_ADDR_CHANGE: means an address that's part of an existing association has had a change of state, like a failure or a return to service of an endpoint's reachability via a specific transport address ๐Ÿ“ถ check Section 6.1.2 for the data structure details
    • SCTP_REMOTE_ERROR: the attached error message is an Operation Error message received from the remote peer. it includes the complete TLV sent by the remote endpoint ๐Ÿ’Œ see Section 6.1.3 for the detailed format
    • SCTP_SEND_FAILED_EVENT: the attached datagram couldn't be sent to the remote endpoint ๐Ÿ’€ this structure includes the original SCTP_SNDINFO that was used to send this message, so it uses the sctp_sndinfo per Section 6.1.11
    • SCTP_SHUTDOWN_EVENT: the peer has sent a SHUTDOWN. no further data should be sent on this socket ๐Ÿ›‘
    • SCTP_ADAPTATION_INDICATION: this notification holds the peer's indicated adaptation layer. see Section 6.1.6 ๐Ÿงฉ
    • SCTP_PARTIAL_DELIVERY_EVENT: tells a receiver that the partial delivery has been aborted. this may mean the association is about to be aborted ๐Ÿ˜ญ see Section 6.1.7
    • SCTP_AUTHENTICATION_EVENT: tells a receiver that either an authentication error happened, or a new key was made active ๐Ÿ”‘ see Section 6.1.8
    • SCTP_SENDER_DRY_EVENT: lets the app know the sender has no more user data queued for transmission or retransmission โ˜€๏ธ see Section 6.1.9
  • sn_flags: these are notification-specific flags ๐Ÿšฉ
  • sn_length: the length of the whole sctp_notification structure, including the sn_type, sn_flags, and sn_length fields ๐Ÿ“

6.1.1. SCTP_ASSOC_CHANGE ๐Ÿ’Œ

Okay bestie, communication notifications tell your app when an SCTP association has either begun or ended ๐Ÿฆ‹. The identifier for a brand-new association comes through this notification too, so it's the one to watch โœจ. Here's the format:

struct sctp_assoc_change {
  uint16_t sac_type;
  uint16_t sac_flags;
  uint32_t sac_length;
  uint16_t sac_state;
  uint16_t sac_error;
  uint16_t sac_outbound_streams;
  uint16_t sac_inbound_streams;
  sctp_assoc_t sac_assoc_id;
  uint8_t  sac_info[];
};
  • sac_type: This field should be set to SCTP_ASSOC_CHANGE. ๐ŸŽ€
  • sac_flags: This field is currently unused. ๐Ÿ’ค
  • sac_length: This field is the total length of the notification data, including the notification header. ๐Ÿ“
  • sac_state: This field holds one of a number of values that tell you what happened to the association ๐Ÿ‘‘. The values include:
    • SCTP_COMM_UP: A new association is now ready, and data may be exchanged with this peer. When an association has been established successfully, this notification should be the first one ๐ŸŒˆ.
    • SCTP_COMM_LOST: The association has failed, and it's now in the closed state ๐Ÿ’€. If SEND_FAILED notifications are turned on, an SCTP_COMM_LOST comes with a series of SCTP_SEND_FAILED_EVENT events, one for each outstanding message ๐Ÿ˜ญ.
    • SCTP_RESTART: SCTP has detected that the peer has restarted ๐Ÿ”„.
    • SCTP_SHUTDOWN_COMP: The association has gracefully closed ๐Ÿซถ.
    • SCTP_CANT_STR_ASSOC: The association setup failed. If non-blocking mode is set and data was sent (on a one-to-many style socket), an SCTP_CANT_STR_ASSOC comes with a series of SCTP_SEND_FAILED_EVENT events, one for each outstanding message ๐Ÿ™ˆ.
  • sac_error: If the state was reached because of an error condition (for example, SCTP_COMM_LOST), any relevant error information shows up in this field ๐Ÿšจ. It lines up with the protocol error codes defined in [RFC4960] ๐Ÿ“š.
  • sac_outbound_streams and sac_inbound_streams: The maximum number of streams allowed in each direction is available in sac_outbound_streams and sac_inbound streams ๐ŸŒŠ.
  • sac_assoc_id: This field holds the identifier for the association. Every notification for a given association has the same association identifier ๐Ÿ’œ. For a one-to-one style socket, this field is ignored ๐Ÿ™ƒ.
  • sac_info: If sac_state is SCTP_COMM_LOST and an ABORT chunk was received for this association, sac_info[] contains the complete ABORT chunk as defined in Section 3.3.7 of the SCTP specification [RFC4960] ๐Ÿงพ. If sac_state is SCTP_COMM_UP or SCTP_RESTART, sac_info may contain an array of uint8_t describing the features that the current association supports โœจ. Features may include:
    • SCTP_ASSOC_SUPPORTS_PR: Both endpoints support the protocol extension described in [RFC3758] ๐Ÿง.
    • SCTP_ASSOC_SUPPORTS_AUTH: Both endpoints support the protocol extension described in [RFC4895] ๐Ÿ”.
    • SCTP_ASSOC_SUPPORTS_ASCONF: Both endpoints support the protocol extension described in [RFC5061] ๐ŸŒธ.
    • SCTP_ASSOC_SUPPORTS_MULTIBUF: For a one-to-many style socket, the local endpoints use separate send and/or receive buffers for each SCTP association ๐Ÿ“.

6.1.2. SCTP_PEER_ADDR_CHANGE ๐Ÿ—บ๏ธ

When a destination address of a multi-homed peer goes through a state change, a peer address change event gets sent ๐Ÿ›ฃ๏ธ. Here's the notification format:

struct sctp_paddr_change {
  uint16_t spc_type;
  uint16_t spc_flags;
  uint32_t spc_length;
  struct sockaddr_storage spc_aaddr;
  uint32_t spc_state;
  uint32_t spc_error;
  sctp_assoc_t spc_assoc_id;
}
  • spc_type: This field should be set to SCTP_PEER_ADDR_CHANGE. ๐ŸŽ€
  • spc_flags: This field is currently unused. ๐Ÿ’ค
  • spc_length: This field is the total length of the notification data, including the notification header. ๐Ÿ“
  • spc_aaddr: The affected address field holds the remote peer's address that is going through the change of state ๐Ÿ“.
  • spc_state: This field holds one of a number of values that tell you what happened to the address ๐ŸŽฏ. They include:
    • SCTP_ADDR_AVAILABLE: This address is now reachable. This notification is provided whenever an address becomes reachable ๐ŸŸข.
    • SCTP_ADDR_UNREACHABLE: The address specified can no longer be reached. Any data sent to this address is rerouted to an alternate until this address becomes reachable again ๐Ÿšง. This notification is provided whenever an address becomes unreachable ๐Ÿ˜ต.
    • SCTP_ADDR_REMOVED: The address is no longer part of the association ๐Ÿ‘‹.
    • SCTP_ADDR_ADDED: The address is now part of the association ๐Ÿค.
    • SCTP_ADDR_MADE_PRIM: This address has now been made the primary destination address ๐Ÿ‘‘. This notification is provided whenever an address is made primary.
  • spc_error: If the state was reached because of any error condition (for example, SCTP_ADDR_UNREACHABLE), any relevant error information shows up in this field ๐Ÿงฏ.
  • spc_assoc_id: This field holds the identifier for the association. Every notification for a given association has the same association identifier ๐Ÿ’œ. For a one-to-one style socket, this field is ignored ๐Ÿ™ƒ.

6.1.3. SCTP_REMOTE_ERROR ๐Ÿ™…โ€โ™€๏ธ

A remote peer may send an Operation Error message to its peer ๐Ÿ“จ. This message flags a variety of error conditions on an association, and the entire ERROR chunk, exactly as it appears on the wire, gets included in an SCTP_REMOTE_ERROR event ๐ŸŽ. For a list of possible error formats, check the SCTP specification [RFC4960] and any extensions ๐Ÿ”. An SCTP error notification has this format:

struct sctp_remote_error {
  uint16_t sre_type;
  uint16_t sre_flags;
  uint32_t sre_length;
  uint16_t sre_error;
  sctp_assoc_t sre_assoc_id;
  uint8_t  sre_data[];
};
  • sre_type: This field should be set to SCTP_REMOTE_ERROR. ๐ŸŽ€
  • sre_flags: This field is currently unused. ๐Ÿ’ค
  • sre_length: This field is the total length of the notification data, including the notification header and the contents of sre_data ๐Ÿ“.
  • sre_error: This value represents one of the Operation Error causes defined in the SCTP specification [RFC4960], in network byte order ๐Ÿ”ข.
  • sre_assoc_id: This field holds the identifier for the association. Every notification for a given association has the same association identifier ๐Ÿ’œ. For a one-to-one style socket, this field is ignored ๐Ÿ™ƒ.
  • sre_data: This contains the ERROR chunk as defined in Section 3.3.10 of the SCTP specification [RFC4960] ๐Ÿ“ฆ.

6.1.4. SCTP_SEND_FAILED - DEPRECATED ๐Ÿชฆ

Heads up: this notification is deprecated. Use SCTP_SEND_FAILED_EVENT instead โš ๏ธ.

If SCTP can't deliver a message, it can hand the message back as a notification, as long as the SCTP_SEND_FAILED event is enabled ๐Ÿ“ฌ. The notification has this format:

struct sctp_send_failed {
  uint16_t ssf_type;
  uint16_t ssf_flags;
  uint32_t ssf_length;
  uint32_t ssf_error;
  struct sctp_sndrcvinfo ssf_info;
  sctp_assoc_t ssf_assoc_id;
  uint8_t  ssf_data[];
};
  • ssf_type: This field should be set to SCTP_SEND_FAILED. ๐ŸŽ€
  • ssf_flags: The flag value will take one of the following values:
    • SCTP_DATA_UNSENT: This value indicates that the data was never put on the wire ๐Ÿšซ.
    • SCTP_DATA_SENT: This value indicates that the data was put on the wire. This does not necessarily mean that the data was (or was not) successfully delivered ๐Ÿคทโ€โ™€๏ธ.
  • ssf_length: This field is the total length of the notification data, including the notification header and the payload in ssf_data ๐Ÿ“.
  • ssf_error: This value represents the reason why the send failed. If it's set, it will be an SCTP protocol error code as defined in Section 3.3.10 of [RFC4960] ๐Ÿงฉ.
  • ssf_info: This field includes the ancillary data (struct sctp_sndrcvinfo) used to send the undelivered message ๐Ÿงณ. Whether or not ancillary data is used, the ssf_info.sinfo_flags field indicates whether the complete message or only part of the message is returned in ssf_data ๐Ÿ”Ž. If only part of the message is returned, that means the part that isn't present has been sent successfully to the peer โœ….

If the complete message can't be sent, the SCTP_DATA_NOT_FRAG flag is set in ssf_info.sinfo_flags ๐Ÿงฑ. If the first part of the message is sent successfully, SCTP_DATA_LAST_FRAG is set. This means the tail end of the message is returned in ssf_data ๐Ÿชก.

  • ssf_assoc_id: The ssf_assoc_id field, ssf_assoc_id, holds the identifier for the association. Every notification for a given association has the same association identifier ๐Ÿ’œ. For a one-to-one style socket, this field is ignored ๐Ÿ™ƒ.
  • ssf_data: The undelivered message, or part of it, will be present in the ssf_data field ๐Ÿ’Œ. Use the ssf_info.sinfo_flags field as noted above to figure out whether you've got a complete message or just a piece of one ๐Ÿง. Only user data is present in this field. Any chunk headers or SCTP common headers must be removed by the SCTP stack ๐Ÿงน.

6.1.5. SCTP_SHUTDOWN_EVENT ๐ŸŒ™

When a peer sends a SHUTDOWN, SCTP delivers this notification to tell the application that it should stop sending data ๐Ÿ›‘. The format looks like this:

    struct sctp_shutdown_event {
        uint16_t sse_type;
        uint16_t sse_flags;
        uint32_t sse_length;
        sctp_assoc_t sse_assoc_id;
    };
  • sse_type: This field should be set to SCTP_SHUTDOWN_EVENT. ๐ŸŽ€
  • sse_flags: This field is currently unused. ๐Ÿ’ค
  • sse_length: This field is the total length of the notification data, including the notification header. It will generally be sizeof(struct sctp_shutdown_event) ๐Ÿ“.
  • sse_assoc_id: This field holds the identifier for the association. Every notification for a given association has the same association identifier ๐Ÿ’œ. For a one-to-one style socket, this field is ignored ๐Ÿ™ƒ.

6.1.6. SCTP_ADAPTATION_INDICATION ๐Ÿงฉ

When a peer sends an Adaptation Layer Indication parameter, as described in [RFC5061], SCTP delivers this notification to tell the application about the peer's adaptation layer indication ๐Ÿช„. The format is:

struct sctp_adaptation_event {
  uint16_t sai_type;
  uint16_t sai_flags;
  uint32_t sai_length;
  uint32_t sai_adaptation_ind;
  sctp_assoc_t sai_assoc_id;
};
  • sai_type: This field should be set to SCTP_ADAPTATION_INDICATION. ๐ŸŽ€
  • sai_flags: This field is currently unused. ๐Ÿ’ค
  • sai_length: This field is the total length of the notification data, including the notification header. It will generally be sizeof(struct sctp_adaptation_event) ๐Ÿ“.
  • sai_adaptation_ind: This field holds the bit array sent by the peer in the Adaptation Layer Indication parameter ๐Ÿ”ข.
  • sai_assoc_id: This field holds the identifier for the association. Every notification for a given association has the same association identifier ๐Ÿ’œ. For a one-to-one style socket, this field is ignored ๐Ÿ™ƒ.

6.1.7. SCTP_PARTIAL_DELIVERY_EVENT ๐Ÿ’–

okay so when a receiver is in the middle of a partial delivery of a message, this notification is how it lets you know stuff is happening ๐Ÿฆ‹

struct sctp_pdapi_event {
  uint16_t pdapi_type;
  uint16_t pdapi_flags;
  uint32_t pdapi_length;
  uint32_t pdapi_indication;
  uint32_t pdapi_stream;
  uint32_t pdapi_seq;
  sctp_assoc_t pdapi_assoc_id;
};
  • pdapi_type: this should be set to SCTP_PARTIAL_DELIVERY_EVENT ๐ŸŽ€
  • pdapi_flags: this one is currently unused ๐Ÿ’ค
  • pdapi_length: this is the total length of the notification data, including the notification header. it'll generally be sizeof(struct sctp_pdapi_event) ๐Ÿ“
  • pdapi_indication: this is the indication being sent up to the application. right now there's only one defined value:
    • SCTP_PARTIAL_DELIVERY_ABORTED: this means the partial delivery of a user message got aborted ๐Ÿ˜ญ that can happen, for example, if the association gets aborted while a partial delivery is going on, or if the user message gets abandoned using PR-SCTP while that partial delivery is happening
  • pdapi_stream: this holds the stream where the partial delivery event happened ๐ŸŒŠ
  • pdapi_seq: this holds the stream sequence number that was being partially delivered ๐Ÿ”ข
  • pdapi_assoc_id: this holds the identifier for the association. every notification for a given association has the same association identifier, so you can group them together ๐Ÿซถ for a one-to-one style socket, this field is ignored

6.1.8. SCTP_AUTHENTICATION_EVENT ๐Ÿ”

ok so [RFC4895] defines an extension to authenticate SCTP messages, and this notification is how you hear about the different events that come with using that extension. main character energy for security ๐Ÿ’…

struct sctp_authkey_event {
  uint16_t auth_type;
  uint16_t auth_flags;
  uint32_t auth_length;
  uint16_t auth_keynumber;
  uint32_t auth_indication;
  sctp_assoc_t auth_assoc_id;
};
  • auth_type: this should be set to SCTP_AUTHENTICATION_EVENT ๐ŸŽ€
  • auth_flags: this one is currently unused ๐Ÿ’ค
  • auth_length: this is the total length of the notification data, including the notification header. it'll generally be sizeof(struct sctp_authkey_event) ๐Ÿ“
  • auth_keynumber: this holds the key number for the affected key in the event. what it means depends on auth_indication ๐Ÿ—๏ธ
  • auth_indication: this holds the error or indication being reported. these are the values currently defined:
    • SCTP_AUTH_NEW_KEY: this report means a new key has been made active (the peer used it for the first time) and it's now the active key โœจ the auth_keynumber field holds the user-specified key number
    • SCTP_AUTH_NO_AUTH: this report means the peer does not support SCTP authentication as defined in [RFC4895] ๐Ÿ™ˆ
    • SCTP_AUTH_FREE_KEY: this report means the SCTP implementation will no longer use the key identifier specified in auth_keynumber ๐Ÿ‘‹
  • auth_assoc_id: this holds the identifier for the association. all notifications for a given association have the same association identifier ๐Ÿซถ for a one-to-one style socket, this field is ignored

6.1.9. SCTP_SENDER_DRY_EVENT ๐Ÿœ๏ธ

when the SCTP stack has no more user data to send or retransmit, this notification gets handed to you. and here's the iconic part: if you subscribe to this event when there's already nothing to send or retransmit, the stack immediately sends this notification right back up ๐Ÿ’€

struct sctp_sender_dry_event {
  uint16_t sender_dry_type;
  uint16_t sender_dry_flags;
  uint32_t sender_dry_length;
  sctp_assoc_t sender_dry_assoc_id;
};
  • sender_dry_type: this should be set to SCTP_SENDER_DRY_EVENT ๐ŸŽ€
  • sender_dry_flags: this one is currently unused ๐Ÿ’ค
  • sender_dry_length: this is the total length of the notification data, including the notification header. it'll generally be sizeof(struct sctp_sender_dry_event) ๐Ÿ“
  • sender_dry_assoc_id: this holds the identifier for the association. all notifications for a given association have the same association identifier ๐Ÿซถ for a one-to-one style socket, this field is ignored

6.1.10. SCTP_NOTIFICATIONS_STOPPED_EVENT ๐Ÿ›‘

okay bestie, SCTP notifications are reliable when you've subscribed to them. they're always delivered as long as there's space in the socket receive buffer ๐Ÿซง BUT if an implementation hits a notification storm, it can run out of socket buffer space. when that happens, it may wish to disable notifications, and if it does, it appends one final notification, SCTP_NOTIFICATIONS_STOPPED_EVENT ๐Ÿ˜ญ

this one is a union sctp_notification, where only the sctp_tlv structure (see the union above) gets used. it only carries this type in the sn_type field, has the sn_length field set to the size of an sctp_tlv structure, and has sn_flags set to 0 ๐Ÿงฉ

if an application receives this notification, it will need to re-subscribe to any notifications it's interested in, except for the sctp_data_io_event (also, note that SCTP_EVENTS is deprecated ๐Ÿ™ƒ)

an endpoint is automatically subscribed to this event as soon as it subscribes to any event other than data io events โœจ


6.1.11. SCTP_SEND_FAILED_EVENT ๐Ÿ“ฆ

if SCTP can't deliver a message, it can hand the message right back to you as a notification, as long as the SCTP_SEND_FAILED_EVENT event is enabled. the notification looks like this:

struct sctp_send_failed_event {
  uint16_t ssfe_type;
  uint16_t ssfe_flags;
  uint32_t ssfe_length;
  uint32_t ssfe_error;
  struct sctp_sndinfo ssfe_info;
  sctp_assoc_t ssfe_assoc_id;
  uint8_t  ssfe_data[];
};
  • ssfe_type: this should be set to SCTP_SEND_FAILED_EVENT ๐ŸŽ€
  • ssfe_flags: the flag value takes one of these values:
    • SCTP_DATA_UNSENT: the data was never put on the wire ๐Ÿšซ
    • SCTP_DATA_SENT: the data was put on the wire. note that this does not necessarily mean it was (or wasn't) successfully delivered ๐Ÿคทโ€โ™€๏ธ
  • ssfe_length: this is the total length of the notification data, including the notification header and the payload in ssf_data ๐Ÿ“
  • ssfe_error: this is the reason the send failed. if it's set, it'll be an SCTP protocol error code as defined in Section 3.3.10 of [RFC4960] โŒ
  • ssfe_info: this includes the ancillary data (struct sctp_sndinfo) used to send the undelivered message. whether or not ancillary data was used, the ssfe_info.sinfo_flags field tells you whether the complete message or only part of it is returned in ssf_data. if only part comes back, that means the part that's missing was sent successfully to the peer ๐ŸŽ‰

if the complete message couldn't be sent, the SCTP_DATA_NOT_FRAG flag is set in ssfe_info.sinfo_flags. if the first part of the message did get sent successfully, SCTP_DATA_LAST_FRAG is set, which means the tail end of the message is what's returned in ssf_data ๐Ÿงต

  • ssfe_assoc_id: this is the ssfe_assoc_id field (ssf_assoc_id) and it holds the identifier for the association. all notifications for a given association have the same association identifier ๐Ÿซถ for a one-to-one style socket, this field is ignored
  • ssfe_data: the undelivered message, or part of it, shows up in the ssf_data field ๐Ÿ“ use ssf_info.sinfo_flags (as noted above) to figure out whether you're getting a complete message or just a piece. also, only user data is present in this field: any chunk headers or SCTP common headers have to be removed by the SCTP stack first ๐Ÿงน

6.2. Notification Interest Options ๐ŸŽ€

6.2.1. SCTP_EVENTS Option - DEPRECATED โœจ

๐Ÿšจ Heads up bestie: this option is deprecated. Use the SCTP_EVENT option described in Section 6.2.2 instead.

Okay so, to get SCTP event notifications, an application signs up for them by setting the SCTP_EVENTS socket option ๐Ÿ’…. After that, it uses recvmsg() to pull the notifications in. A notification gets stored in the data part (msg_iov) of the msghdr structure. The socket option takes this structure:

struct sctp_event_subscribe {
  uint8_t sctp_data_io_event;
  uint8_t sctp_association_event;
  uint8_t sctp_address_event;
  uint8_t sctp_send_failure_event;
  uint8_t sctp_peer_error_event;
  uint8_t sctp_shutdown_event;
  uint8_t sctp_partial_delivery_event;
  uint8_t sctp_adaptation_layer_event;
  uint8_t sctp_authentication_event;
  uint8_t sctp_sender_dry_event;
};

Let's go through each flag, because they're all kind of a vibe ๐Ÿฆ‹

  • sctp_data_io_event: Setting this flag to 1 causes the reception of SCTP_SNDRCV information on a per-message basis ๐ŸŽ€. The application will need to use the recvmsg() interface so it can receive the event information contained in the msg_control field. Setting the flag to 0 disables the reception of the message control information. Note that this flag is not really a notification, so it's stored in the ancillary data (msg_control), not in the data part (msg_iov) ๐Ÿ™ˆ.
  • sctp_association_event: Setting this flag to 1 enables the reception of association event notifications. Setting the flag to 0 disables association event notifications ๐ŸŒธ.
  • sctp_address_event: Setting this flag to 1 enables the reception of address event notifications. Setting the flag to 0 disables address event notifications ๐Ÿงš.
  • sctp_send_failure_event: Setting this flag to 1 enables the reception of send failure event notifications. Setting the flag to 0 disables send failure event notifications ๐Ÿ’€.
  • sctp_peer_error_event: Setting this flag to 1 enables the reception of peer error event notifications. Setting the flag to 0 disables peer error event notifications ๐Ÿ“.
  • sctp_shutdown_event: Setting this flag to 1 enables the reception of shutdown event notifications. Setting the flag to 0 disables shutdown event notifications โœจ.
  • sctp_partial_delivery_event: Setting this flag to 1 enables the reception of partial delivery event notifications. Setting the flag to 0 disables partial delivery event notifications ๐Ÿซถ.
  • sctp_adaptation_layer_event: Setting this flag to 1 enables the reception of adaptation layer event notifications. Setting the flag to 0 disables adaptation layer event notifications ๐Ÿ‘‘.
  • sctp_authentication_event: Setting this flag to 1 enables the reception of authentication layer event notifications. Setting the flag to 0 disables authentication layer event notifications ๐Ÿ”ฅ.
  • sctp_sender_dry_event: Setting this flag to 1 enables the reception of sender dry event notifications. Setting the flag to 0 disables sender dry event notifications ๐Ÿ’œ.

Here's an example where an application wants data_io_events and association_events but none of the others ๐Ÿซถ:

{
  struct sctp_event_subscribe events;

  memset(&events, 0, sizeof(events));

  events.sctp_data_io_event = 1;
  events.sctp_association_event = 1;

  setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events));
}

Also, for one-to-many style SCTP sockets, the caller of recvmsg() gets ancillary data and notifications for all associations bound to the file descriptor. For one-to-one style SCTP sockets, the caller only gets ancillary data and notifications for the single association bound to the file descriptor ๐Ÿ‘€.

By default, both the one-to-one style and the one-to-many style socket do not subscribe to any notification. Nothing comes in until you ask ๐Ÿ™…โ€โ™€๏ธ

6.2.2. SCTP_EVENT Option ๐Ÿฆ‹

The SCTP_EVENTS socket option has one issue for future compatibility ๐Ÿ˜ญ. As new features get added, the structure (sctp_event_subscribe) has to be expanded. That can cause an application binary interface (ABI) issue, unless an implementation added padding at the end of the structure. So to dodge that problem, SCTP_EVENTS got deprecated and a new socket option, SCTP_EVENT, took its place. This option uses the following structure:

struct sctp_event {
        sctp_assoc_t se_assoc_id;
        uint16_t     se_type;
        uint8_t      se_on;
};
  • se_assoc_id: The se_assoc_id field is ignored for one-to-one style sockets. For one-to-many style sockets, this field can be a particular association identifier or SCTP_{FUTURE|CURRENT|ALL}_ASSOC ๐ŸŒˆ.
  • se_type: The se_type field can be filled with any value that would show up in the respective sn_type field (in the sctp_tlv structure of the notification) ๐Ÿ’–.
  • se_on: The se_on field is set to 1 to turn an event on, and set to 0 to turn an event off โญ.

To use this option, you fill in this structure and then call setsockopt() to turn an individual event on or off. Here's an example:

{
  struct sctp_event event;

  memset(&event, 0, sizeof(event));

  event.se_assoc_id = SCTP_FUTURE_ASSOC;
  event.se_type = SCTP_SENDER_DRY_EVENT;
  event.se_on = 1;
  setsockopt(sd, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event));
}

And yes, same as before: by default, both the one-to-one style and the one-to-many style socket do not subscribe to any notification ๐Ÿ™ˆ


7. Common Operations for Both Styles ๐Ÿ‘‘

7.1. send(), recv(), sendto(), and recvfrom() ๐Ÿ’…

Applications can use send() and sendto() to transmit data to the peer of an SCTP endpoint. recv() and recvfrom() can be used to receive data from the peer ๐Ÿฆ‹.

The function prototypes are:

ssize_t send(int sd,
             const void *msg,
             size_t len,
             int flags);

ssize_t sendto(int sd,
               const void *msg,
               size_t len,
               int flags,
               const struct sockaddr *to,
               socklen_t tolen);

ssize_t recv(int sd,
             void *buf,
             size_t len,
             int flags);

ssize_t recvfrom(int sd,
                 void *buf,
                 size_t len,
                 int flags,
                 struct sockaddr *from,
                 socklen_t *fromlen);

And the arguments are:

  • sd: The socket descriptor of an SCTP endpoint.
  • msg: The message to be sent.
  • len: The size of the message or the size of the buffer.
  • to: One of the peer addresses of the association to be used to send the message.
  • tolen: The size of the address.
  • buf: The buffer to store a received message.
  • from: The buffer to store the peer address used to send the received message.
  • fromlen: The size of the from address.
  • flags: (described below).
โš ๏ธ These calls only give you access to the basic SCTP protocol features. If either peer in the association uses multiple streams, or sends unordered data, these calls will usually be inadequate and may deliver the data in unpredictable ways. Be careful, bestie ๐Ÿ’€

SCTP has the concept of multiple streams in one association ๐ŸŒธ. The calls above don't let you say which stream a message should go out on. The system uses stream 0 as the default stream for send() and sendto(). recv() and recvfrom() return data from any stream, but the caller can't tell the streams apart. That can make data seem to arrive out of order ๐Ÿ˜ต. Similarly, if a DATA chunk is sent unordered, recv() and recvfrom() give you no indication of it.

SCTP is message based. The msg buffer in send() and sendto() is treated as a single message. So if you want to send a message made of several buffers, you need to combine them before calling send() or sendto(). Or you can use sendmsg() to do it without combining them ๐Ÿงš. Sending a message with send() or sendto() is atomic, unless explicit EOR marking is enabled on the socket specified by sd. Using sendto() on a non-connected one-to-one style socket for implicit connection setup may or may not work, depending on the SCTP implementation. And recv() and recvfrom() can't tell message boundaries apart (i.e., there's no way to observe the MSG_EOR flag to detect partial delivery) ๐Ÿ™ˆ.

When receiving, if the buffer you supplied isn't large enough to hold a complete message, the receive call acts like a stream socket and returns as much data as will fit in the buffer ๐Ÿ“.

Note that the send() and recv() calls may not be used for a one-to-many style socket ๐Ÿ™…โ€โ™€๏ธ.

Also, if an application calls a send() or sendto() function with no user data, the SCTP implementation should reject the request with an appropriate error message. An implementation is not allowed to send a DATA chunk with no user data [RFC4960] โœจ.

7.2. setsockopt() and getsockopt() ๐ŸŽ€

Applications use setsockopt() and getsockopt() to set or retrieve socket options. Socket options are used to change the default behavior of socket calls. They're described in Section 8 ๐Ÿ’–.

The function prototypes are:

int getsockopt(int sd,
               int level,
               int optname,
               void *optval,
               socklen_t *optlen);

and

int setsockopt(int sd,
               int level,
               int optname,
               const void *optval,
               socklen_t optlen);

And the arguments are:

  • sd: The socket descriptor.
  • level: Set to IPPROTO_SCTP for all SCTP options.
  • optname: The option name.
  • optval: The buffer to store the value of the option.
  • optlen: The size of the buffer (or the length of the option returned).

These functions return 0 on success and -1 in case of an error ๐ŸŒˆ.

All socket options set on a one-to-one style listening socket also apply to all future accepted sockets ๐Ÿฆ‹. For one-to-many style sockets, a socket option will often pass a structure that includes an assoc_id field. This field can be filled with the association identifier of a particular association, and unless otherwise specified, it can be filled with one of the following constants:

  • SCTP_FUTURE_ASSOC: Specifies that only future associations created after this socket option will be affected by this call.
  • SCTP_CURRENT_ASSOC: Specifies that only currently existing associations will be affected by this call, and future associations will still receive the previous default value.
  • SCTP_ALL_ASSOC: Specifies that all current and future associations will be affected by this call.

7.3. read() and write() ๐Ÿงš

Applications can use read() and write() to receive and send data from and to a peer. They have the same semantics as recv() and send(), except that the flags parameter can't be used ๐Ÿ’….

7.4. getsockname() ๐Ÿ’…

okay bestie, getsockname() is how an app asks "hey, what local address did SCTP actually give me?" ๐Ÿฆ‹ It's especially useful when the caller let SCTP pick a local port, because otherwise you're just guessing ๐Ÿ™ˆ This call is for single-homed endpoints, so it does NOT vibe with multi-homed ones. For the multi-homed version, see Section 9.5 ๐Ÿซถ

The prototype is:

int getsockname(int sd,
                struct sockaddr *address,
                socklen_t *len);

And the arguments are:

  • sd: the socket descriptor you want to query ๐ŸŒธ
  • address: on return, one locally bound address (picked by the SCTP stack) gets stored in this buffer. If the socket is an IPv4 socket, the address will be IPv4. If it's an IPv6 socket, the address could be either IPv6 or IPv4 ๐ŸŒˆ
  • len: you set the length of the address here before the call. On return, it's set to the length of the returned address โœจ

getsockname() returns 0 on success and -1 if something goes wrong โญ

If the real address is longer than the sockaddr structure you passed in, the stored address gets truncated ๐Ÿ’€

If the socket hasn't been bound to a local name yet, the value stored in the object address points to is unspecified. so like, don't trust it ๐Ÿ™ˆ


7.5. Implicit Association Setup ๐Ÿงš

the app can start sending and receiving data with sendmsg()/recvmsg() or sendto()/recvfrom(), and it doesn't have to do any explicit association setup first. no connect() calls needed, the girlies are free โœจ

here's the vibe: whenever sendmsg() or sendto() gets called and the SCTP stack at the sender finds there's no association between the sender and the intended receiver (identified by the address passed in the msg_name field of the msghdr structure for sendmsg(), or the dest_addr field for sendto()), the SCTP stack will automatically set up an association to that receiver ๐Ÿฆ‹

once the association is up, an SCTP_COMM_UP notification gets dispatched to the socket on both the sender and receiver sides ๐ŸŽ€ You can read that notification with the recvmsg() system call (see Section 3.1.4) ๐Ÿ’–

also, if the sender's SCTP stack supports bundling, the first user message might get bundled with the COOKIE ECHO message [RFC4960] ๐Ÿซถ

when the SCTP stack sets up a new association implicitly, you can also pass along SCTP_INIT type ancillary data (see Section 5.3.1 for the data structures) to change some of the parameters used in setting up the new association ๐Ÿ’…

if you don't include that info in the sendmsg() call, or if the implicit setup was triggered by a sendto() call, the default association initialization parameters get used instead. those defaults can be set with the respective setsockopt() calls, or they can just be left at the system defaults ๐ŸŒธ

โš ๏ธ implicit association setup cannot be initiated by send() calls. just saying, bestie ๐Ÿ˜ญ

8. Socket Options ๐Ÿ’œ

this section covers the SCTP-level socket options that work the same for both styles ๐ŸŽ€ Remember that SCTP associations can be multi-homed! So some option parameters include a sockaddr_storage structure to pick which peer address the option should apply to ๐Ÿฆ‹

for one-to-many style sockets, you use an sctp_assoc_t (association identifier) parameter to say which association the operation affects. so it MUST be set when you're using this style ๐Ÿ‘‘

hmm wait, I'm not allowed to capitalise that, so: it must be set when you're using this style. (the original says it, so it's a must-be-set moment, periodt ๐Ÿ’…)

for one-to-one style sockets, and for branched-off one-to-many style sockets (see Section 9.2), this association ID parameter gets ignored ๐Ÿงš

also, socket- and IP-level options are set or retrieved per socket. so for one-to-many style sockets, the options get applied to ALL associations belonging to the socket (kinda like using SCTP_ALL_ASSOC as the association identifier) ๐ŸŒˆ For the one-to-one style, the options get applied to all peer addresses of the association the socket controls. apps should be careful with these, no cap ๐Ÿ˜ญ

for some IP stacks, getsockopt() is read-only, so a new interface is needed when info has to go both into and out of the SCTP stack โœจ The syntax for sctp_opt_info() is:

int sctp_opt_info(int sd,
                  sctp_assoc_t id,
                  int opt,
                  void *arg,
                  socklen_t *size);

sctp_opt_info() is a replacement for getsockopt() only. it will NOT set any options on the socket, so if you want to set a writable option, you still need setsockopt() ๐Ÿซถ

for one-to-many style sockets, id says which association to query. for one-to-one style sockets, id gets ignored. also, for one-to-many style sockets, any association identifier inside the structure passed as arg gets ignored, and id takes precedence ๐Ÿ‘‘

quick heads-up: SCTP_CURRENT_ASSOC and SCTP_ALL_ASSOC can't be used with sctp_opt_info() or in getsockopt() calls. using them gives an error (returns -1 with errno set to EINVAL) ๐Ÿ’€ SCTP_FUTURE_ASSOC is fine though, and you can use it to query info for future associations ๐ŸŒธ

the opt field says which SCTP socket option to get. it can grab any currently supported option that requests information (read/write or read-only options), like:

  • SCTP_RTOINFO
  • SCTP_ASSOCINFO
  • SCTP_PRIMARY_ADDR
  • SCTP_PEER_ADDR_PARAMS
  • SCTP_DEFAULT_SEND_PARAM
  • SCTP_MAX_SEG
  • SCTP_AUTH_ACTIVE_KEY
  • SCTP_DELAYED_SACK
  • SCTP_MAX_BURST
  • SCTP_CONTEXT
  • SCTP_EVENT
  • SCTP_DEFAULT_SNDINFO
  • SCTP_DEFAULT_PRINFO
  • SCTP_STATUS
  • SCTP_GET_PEER_ADDR_INFO
  • SCTP_PEER_AUTH_CHUNKS
  • SCTP_LOCAL_AUTH_CHUNKS

the arg field is an option-specific structure buffer that the caller provides. the rest of this section goes into more detail on these options and their structures ๐ŸŽ€

sctp_opt_info() returns 0 on success, or on failure returns -1 and sets errno to the appropriate error code โœจ


8.1. Read/Write Options ๐ŸŒˆ

8.1.1. Retransmission Timeout Parameters (SCTP_RTOINFO) โฑ๏ธ

the protocol parameters that set up and limit the retransmission timeout (RTO) are tunable ๐Ÿฆ‹ See [RFC4960] for more on how these parameters are used in the RTO calculation ๐Ÿซถ

to access or change these, use this structure:

struct sctp_rtoinfo {
  sctp_assoc_t srto_assoc_id;
  uint32_t srto_initial;
  uint32_t srto_max;
  uint32_t srto_min;
};
  • srto_assoc_id: ignored for one-to-one style sockets. for one-to-many style sockets, you can fill in an association identifier or SCTP_FUTURE_ASSOC. it's an error to use SCTP_{CURRENT|ALL}_ASSOC here ๐Ÿ’…
  • srto_initial: the initial RTO value ๐ŸŒธ
  • srto_max and srto_min: the max and min bounds for all RTOs ๐ŸŒˆ

all times are in milliseconds. a value of 0 when modifying means the current value should NOT be changed (ok, just lowercase "should" here, it's giving "keep it as is" ๐Ÿซถ). Actually the original says: a value of 0, when modifying the parameters, indicates that the current value should not be changed.

to access or modify these, call getsockopt() or setsockopt() with the option name SCTP_RTOINFO ๐Ÿ‘‘

8.1.2. Association Parameters (SCTP_ASSOCINFO) ๐ŸŽ€

this option is used to both examine and set various association and endpoint parameters. See [RFC4960] for more on how they're used ๐Ÿ’–

to access or modify these, use this structure:

struct sctp_assocparams {
  sctp_assoc_t sasoc_assoc_id;
  uint16_t sasoc_asocmaxrxt;
  uint16_t sasoc_number_peer_destinations;
  uint32_t sasoc_peer_rwnd;
  uint32_t sasoc_local_rwnd;
  uint32_t sasoc_cookie_life;
};
  • sasoc_assoc_id: ignored for one-to-one style sockets. for one-to-many style sockets, you can fill in an association identifier or SCTP_FUTURE_ASSOC. it's an error to use SCTP_{CURRENT|ALL}_ASSOC here ๐Ÿฆ‹
  • sasoc_asocmaxrxt: the max retransmission attempts for the association ๐ŸŒธ
  • sasoc_number_peer_destinations: how many destination addresses the peer has ๐Ÿงš
  • sasoc_peer_rwnd: the peer's current rwnd (as reported in the last selective acknowledgment (SACK)) minus any outstanding data (aka data in flight) ๐ŸŒˆ
  • sasoc_local_rwnd: the last reported rwnd that was sent to the peer โœจ
  • sasoc_cookie_life: the association's cookie life value, used when issuing cookies ๐Ÿ’…

the value of sasoc_peer_rwnd is meaningless when you're examining endpoint info. it's only valid when you're examining a specific association ๐Ÿ™ˆ

all time values are in milliseconds. a value of 0 when modifying means the current value should not be changed ๐Ÿซถ

the values of sasoc_asocmaxrxt and sasoc_cookie_life can be set on either an endpoint or association basis. the rwnd and destination counts (sasoc_number_peer_destinations, sasoc_peer_rwnd, sasoc_local_rwnd) aren't settable, so any value you put in them gets ignored ๐Ÿ’€

to access or modify these, call getsockopt() or setsockopt() with the option name SCTP_ASSOCINFO ๐Ÿ‘‘

the max number of retransmissions before an address counts as unreachable is also tunable, but it's address-specific, so it's covered in a separate option ๐ŸŒธ If an app tries to set the association's max retransmission parameter to more than the sum of all the max retransmission parameters, setsockopt() may return an error. the reason, from Section 8.2 of [RFC4960], is this:

Note: When configuring the SCTP endpoint, the user should avoid having the value of 'Association.Max.Retrans' (sasoc_maxrxt in this option) larger than the summation of the 'Path.Max.Retrans' (see spp_pathmaxrxt in Section 8.1.12) of all of the destination addresses for the remote endpoint. Otherwise, all of the destination addresses may become inactive while the endpoint still considers the peer endpoint reachable.

8.1.3. Initialization Parameters (SCTP_INITMSG) ๐Ÿงš

apps can specify protocol parameters for the default association initialization ๐ŸŽ€ The structure used to access and modify these is defined in Section 5.3.1. The option name argument to setsockopt() and getsockopt() is SCTP_INITMSG ๐Ÿ’–

setting initialization parameters only works on an unconnected socket. (for one-to-many style sockets, the change only affects future associations) ๐Ÿฆ‹

8.1.4. SO_LINGER ๐Ÿ’…

okay bestie, this option lets an app do the SCTP ABORT primitive ๐Ÿ’€. It affects all the associations tied to the socket, so choose wisely โœจ

The linger option structure is:

struct linger {
  int l_onoff;  /* option on/off */
  int l_linger; /* linger time   */
};

To turn it on, set l_onoff to 1 ๐ŸŽ€. If l_linger is 0, calling close() is the same as the ABORT primitive ๐Ÿ”ฅ. If it's negative, the setsockopt() call returns an error ๐Ÿ™…โ€โ™€๏ธ. If it's a positive value (linger_time), close() can get blocked for at most linger_time ๐Ÿฆ‹. Heads up: the time unit is seconds according to POSIX, but it might be different on specific platforms ๐ŸŒธ. If the graceful shutdown phase doesn't finish during that window, close() returns anyway, but the graceful shutdown keeps going in the system ๐Ÿซถ.

Note that this is a socket-level option, not an SCTP-level option ๐Ÿงš. When using it, an application must specify a level of SOL_SOCKET in the call. no skipping that part ๐Ÿ˜ญ

8.1.5. SCTP_NODELAY ๐ŸŒˆ

This option turns any Nagle-like algorithm on or off โœจ. The vibe here is "send it ASAP": packets generally go out as soon as possible, with no unnecessary delays, but you pay for it with more packets in the network ๐Ÿ’ธ. Not using a Nagle-like algorithm might reduce the bundling of small user messages in cases where bundling would need an extra delay ๐Ÿ“.

Turning this option on disables any Nagle-like algorithm ๐Ÿ’œ.

It expects an integer boolean flag: non-zero turns it on, zero turns it off ๐Ÿ’–.

8.1.6. SO_RCVBUF ๐Ÿงš

This option sets the receive buffer size in octets ๐ŸŒธ. For SCTP one-to-one style sockets, it controls the receiver window size ๐Ÿ‘‘. For one-to-many style sockets, the meaning is implementation dependent ๐Ÿ™ˆ. It might control the receive buffer for each association bound to the socket descriptor, or it might control the receive buffer for the whole socket ๐Ÿฆ‹. It expects an integer โœจ.

Note that this is a socket-level option, not an SCTP-level option. When using it, an application must specify a level of SOL_SOCKET in the call ๐Ÿ’….

8.1.7. SO_SNDBUF ๐Ÿ”ฅ

This option sets the send buffer size ๐ŸŽ€. For SCTP one-to-one style sockets, it controls how much data SCTP may have waiting in internal buffers to be sent ๐Ÿ“ฆ. That means it bounds the maximum size of data that can go out in a single send call ๐Ÿซถ. For one-to-many style sockets the effect is the same, except it applies to one or all associations (see Section 3.3) bound to the socket descriptor used in the setsockopt() or getsockopt() call ๐ŸŒˆ. The option applies to each association's window size separately ๐Ÿ’œ. It expects an integer ๐Ÿ“.

Note that this is a socket-level option, not an SCTP-level option. When using it, an application must specify a level of SOL_SOCKET in the call ๐Ÿ’–.

8.1.8. Automatic Close of Associations (SCTP_AUTOCLOSE) ๐Ÿ’€

This socket option only applies to the one-to-many style socket ๐Ÿฆ‹. When it's set, associations that sit idle for more than the specified number of seconds will automatically close using the graceful shutdown procedure ๐Ÿงš. An idle association is one that has not sent or received user data ๐Ÿ˜ด. The special value '0' means no automatic close should be performed, and that's the default ๐ŸŒธ. It expects an integer defining how many seconds of idle time pass before an association gets closed โœจ.

An application using this option should enable the ability to receive the association change notification. That's the only way an app finds out an association closed ๐Ÿšจ. Also, after an association closes, its association identifier can be reused, so the app should be aware of that to avoid sending data to the wrong peer endpoint ๐Ÿ˜ญ.

8.1.9. Set Primary Address (SCTP_PRIMARY_ADDR) ๐Ÿ‘‘

This option requests that the local SCTP stack uses the enclosed peer address as the association's primary ๐Ÿ’…. The enclosed address must be one of the association peer's addresses ๐Ÿซถ.

The following structure is used to make a set peer primary request:

struct sctp_setprim {
  sctp_assoc_t ssp_assoc_id;
  struct sockaddr_storage ssp_addr;
};
  • ssp_assoc_id: This parameter is ignored for one-to-one style sockets. For one-to-many style sockets, it identifies the association for this request. Note that the special sctp_assoc_t SCTP_{FUTURE|ALL|CURRENT}_ASSOC values are not allowed ๐Ÿ™…โ€โ™€๏ธ.
  • ssp_addr: This parameter is the address to set as primary. No wildcard address is allowed ๐ŸŒˆ.

8.1.10. Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER) ๐Ÿงš

This option requests that the local endpoint set the specified Adaptation Layer Indication parameter for all future INIT and INIT-ACK exchanges โœจ. That's a whole vibe for future handshakes ๐Ÿฆ‹.

The following structure is used to access and modify this parameter:

struct sctp_setadaptation {
  uint32_t   ssb_adaptation_ind;
};
  • ssb_adaptation_ind: The adaptation layer indicator that will be included in any outgoing Adaptation Layer Indication parameter ๐Ÿ’œ.

8.1.11. Enable/Disable Message Fragmentation (SCTP_DISABLE_FRAGMENTS) ๐Ÿ“

This option is an on/off flag passed as an integer: non-zero is on, zero is off ๐Ÿ’–. If it's enabled, no SCTP message fragmentation gets performed ๐Ÿ”ฅ. So if a message being sent is bigger than the current Path MTU (PMTU) size, the message will not be sent, and instead an error gets indicated to the user ๐Ÿ™ˆ. If this option is disabled (the default), then a message bigger than the PMTU gets fragmented and reassembled by the peer ๐Ÿซถ.

8.1.12. Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) ๐Ÿ’–

okay so this one is the heartbeat control panel ๐Ÿซถ you can turn heartbeats on or off for any peer address on an association, change how often they go out, force one to fire RIGHT NOW, and tweak how many retransmissions an address gets before it's officially considered unreachable ๐Ÿ’…

here's the struct you use to read and change an address's settings:

struct sctp_paddrparams {
  sctp_assoc_t spp_assoc_id;
  struct sockaddr_storage spp_address;
  uint32_t spp_hbinterval;
  uint16_t spp_pathmaxrxt;
  uint32_t spp_pathmtu;
  uint32_t spp_flags;
  uint32_t spp_ipv6_flowlabel;
  uint8_t  spp_dscp;
};

spp_assoc_id: this one gets ignored on one-to-one style sockets ๐Ÿ™ˆ on one-to-many style sockets, you can fill in an association identifier or SCTP_FUTURE_ASSOC for this query. putting SCTP_{CURRENT|ALL}_ASSOC in here is an error, so don't be that girl ๐Ÿ’€

spp_address: this tells you which address you're talking about. if you give it a wildcard address, the change applies to all current AND future paths โœจ

spp_hbinterval: this is the heartbeat interval in milliseconds (HB.Interval in [RFC4960]) ๐Ÿฆ‹ heads up, this value gets ignored unless spp_flags has SPP_HB_ENABLE set. also, a value of zero means "leave my current setting alone." if you actually want the interval to be zero, you use the SPP_HB_TIME_IS_ZERO flag instead. and even at zero, SCTP is NOT spamming heartbeats nonstop, because the real interval also includes the current RTO and jitter (see Section 8.3 of [RFC4960]) ๐ŸŒธ

spp_pathmaxrxt: this is the max number of retransmissions before the address is considered unreachable. a value of zero means "keep it as it is" ๐Ÿ“

spp_pathmtu: this one holds the current Path MTU of the peer address, which is basically how many bytes are free for chunks inside an SCTP packet ๐Ÿ“ฆ putting in 0 doesn't change anything. if you give a positive value AND SPP_PMTUD_DISABLE is set in spp_flags, that value gets used as the Path MTU. if SPP_PMTUD_ENABLE is set, then spp_pathmtu gets ignored ๐Ÿ˜ญ

spp_flags: these flags control all sorts of features on an association. it's a bitmask, so you can have zero or more of these options going at once ๐ŸŽ€

  • SPP_HB_ENABLE: turns heartbeats on for the specified address.
  • SPP_HB_DISABLE: turns heartbeats off for the specified address. SPP_HB_ENABLE and SPP_HB_DISABLE are mutually exclusive, so only ONE of them should be specified. setting both gives undetermined results, which is chaos, bestie ๐Ÿ’€
  • SPP_HB_DEMAND: asks for a user-initiated heartbeat to go out immediately. this must not be used together with a wildcard address.
  • SPP_HB_TIME_IS_ZERO: sets the heartbeat delay time to 0 milliseconds.
  • SPP_PMTUD_ENABLE: enables PMTU discovery on the specified address.
  • SPP_PMTUD_DISABLE: disables PMTU discovery on the specified address. if the address field is empty, then EVERY address on the association gets affected ๐Ÿ‘‘ also, SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually exclusive too. enabling both gives undetermined results, so pick one ๐Ÿ™…โ€โ™€๏ธ
  • SPP_IPV6_FLOWLABEL: setting this flag lets you set the IPv6 flow label value, which lives in the spp_ipv6_flowlabel field. on retrieval, this flag comes back set to show that spp_ipv6_flowlabel has a valid value. if you gave a specific destination address (in spp_address), the value you get back is that address's. if you only gave an association (and no address), you get the association's default flow label. if you gave neither, you get the socket's default flow label. for non-IPv6 sockets, this flag just stays cleared ๐Ÿงš
  • SPP_DSCP: setting this flag lets you set the Differentiated Services Code Point (DSCP) value for either the association or one specific address, and the value lives in the spp_dscp field. on retrieval, this flag comes back set to show that spp_dscp has a valid value. a specific destination address (in spp_address) gets you that address's DSCP. just an association gets you the association's default DSCP. neither one gets you the socket's default DSCP โœจ

spp_ipv6_flowlabel: this is used together with the SPP_IPV6_FLOWLABEL flag and holds the IPv6 flow label. only the 20 least significant bits are used for the flow label. this setting beats any IPv6-layer setting, so it wins ๐Ÿ‘‘

spp_dscp: this is used together with the SPP_DSCP flag and holds the DSCP. only the 6 most significant bits are used for the DSCP. this setting beats any IPv4- or IPv6-layer setting too ๐Ÿ’…

heads up: changing the flow label or DSCP value affects ALL packets the SCTP stack sends after you set these params. it's a whole vibe shift for every packet ๐Ÿ˜ญ

also, the flow label can be set through the sin6_flowinfo field of the sockaddr_in6 structure ๐Ÿฆ‹


8.1.13. Set Default Send Parameters (SCTP_DEFAULT_SEND_PARAM) - DEPRECATED ๐Ÿฅ€

okay this one is DEPRECATED so please don't build new stuff on it ๐Ÿ™ˆ SCTP_DEFAULT_SNDINFO (Section 8.1.31) is what you should use instead.

that said, here's what it used to do: if your app wants to use the sendto() system call and would normally pass ancillary data each time, you could set a default set of parameters ahead of time. you pass in an sctp_sndrcvinfo structure (defined in Section 5.3.2) and that becomes the default ๐Ÿ“ฆ the inputs it takes are sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context, and sinfo_timetolive. the sinfo_flags field is a bitwise OR of SCTP_UNORDERED, SCTP_EOF, and SCTP_SENDALL. the sinfo_assoc_id field says which association the parameters apply to. on a one-to-many style socket, any of the predefined constants are allowed in that field too. for one-to-one style sockets, the field just gets ignored ๐ŸŒธ


8.1.14. Set Notification and Ancillary Events (SCTP_EVENTS) - DEPRECATED ๐Ÿฅ€

another deprecated one, bestie ๐Ÿ’€ this socket option lets you specify which notifications and ancillary data you want to receive. for the full rundown, check out Section 6.2.1. this option is considered deprecated and only hangs around for backward compatibility. new apps should use the SCTP_EVENT option instead, and you can read all about that one in Section 6.2.2 โœจ


8.1.15. Set/Clear IPv4 Mapped Addresses (SCTP_I_WANT_MAPPED_V4_ADDR) ๐ŸŒˆ

this socket option is a boolean flag that turns IPv4 address mapping on or off ๐Ÿฆ‹ when it's ON, IPv4 addresses get mapped to their IPv6 representation. when it's OFF, nothing gets mapped, and you'll receive both PF_INET6 and PF_INET type addresses on the socket. for more on mapped IPv6 addresses, see [RFC3542] ๐Ÿงš

if you use this option on a socket of type PF_INET, you get an error. no cap ๐Ÿ™…โ€โ™€๏ธ

by default it's OFF, and you pass in an integer: a non-zero value turns it on, and a zero value turns it off ๐ŸŽ€


8.1.16. Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG) ๐Ÿ“

this option gets or sets the maximum size to put in any outgoing SCTP DATA chunk โœจ if a message is bigger than this max, SCTP fragments it into the size you specified. the underlying SCTP implementation might fragment into even smaller chunks when the PMTU of the underlying association is smaller than the value you set ๐Ÿ“ฆ

the default value is '0', which means you're not limiting fragmentation, and only the PMTU decides SCTP's choice of DATA chunk size ๐ŸŒธ and FYI, if you set a value larger than the maximum size of an IP datagram, it effectively lets SCTP control fragmentation (same as setting this option to 0) ๐Ÿ’…

the following structure is used to access and modify this parameter:

struct sctp_assoc_value {
  sctp_assoc_t assoc_id;
  uint32_t assoc_value;
};

assoc_id: this one gets ignored on one-to-one style sockets. on one-to-many style sockets, it says which association you're acting on. putting SCTP_{CURRENT|ALL}_ASSOC in here is an error ๐Ÿ™ˆ

assoc_value: this is the maximum size in bytes ๐Ÿฆ‹


8.1.17. Get or Set the List of Supported HMAC Identifiers (SCTP_HMAC_IDENT) ๐Ÿ”

okay this is the one where you tell the peer which HMAC algorithms your local endpoint requires it to use ๐Ÿ‘‘ (HMAC stands for Hashed Message Authentication Code, so it's basically the security guest list ๐Ÿซถ)

here's the struct you use to get or set these identifiers:

struct sctp_hmacalgo {
  uint32_t shmac_number_of_idents;
  uint16_t shmac_idents[];
};

shmac_number_of_idents: this gives the number of elements in the array shmac_idents ๐Ÿ”ข

shmac_idents: this is an array of HMAC identifiers that your local endpoint is asking the peer to use, listed in priority order ๐Ÿ’– the valid identifiers are:

  • SCTP_AUTH_HMAC_ID_SHA1
  • SCTP_AUTH_HMAC_ID_SHA256

the list you give has to include SCTP_AUTH_HMAC_ID_SHA1, and it may include any of the other values in whatever preferred order you like. the lowest list position has the HIGHEST preference when picking an algorithm, so put your favorite first ๐Ÿ‘‘

bestie, watch out: if the list is missing SCTP_AUTH_HMAC_ID_SHA1, OR if it includes an unknown HMAC identifier (that includes optional identifiers the implementation doesn't recognize), the set option FAILS and returns an error ๐Ÿ˜ญ no second chances, periodt ๐Ÿ’€

8.1.18. Get or Set the Active Shared Key (SCTP_AUTH_ACTIVE_KEY) ๐Ÿ”‘

okay so this option lets you get or set the active shared key ๐Ÿซถ that gets used to build the association shared key. literally the key that's in charge rn ๐Ÿ‘‘

to access and modify these parameters, you use this structure:

struct sctp_authkeyid {
  sctp_assoc_t scact_assoc_id;
  uint16_t scact_keynumber;
};
  • scact_assoc_id: this sets the active key on the specified association. you can use the special SCTP_{FUTURE|CURRENT|ALL}_ASSOC here. for one-to-one style sockets this field gets ignored ๐Ÿ™ˆ. but heads up, if the socket is connected, this option sets the active key on the association. if it isn't connected, it sets the default active key for the endpoint instead โœจ
  • scact_keynumber: this is the shared key identifier the app wants as the active shared key for sending authenticated chunks ๐Ÿ’…. the identifier has to match an existing shared key, no ghosting allowed ๐Ÿ’€. also, shared key identifier 0 defaults to a null key, so keep that in mind ๐ŸŒธ

when you use this with setsockopt(), the SCTP implementation has to use the indicated shared key identifier for every message handed to it through a send call after that setsockopt() call, until you change it again ๐Ÿฆ‹. so the implementation must not bundle user messages that should be authenticated with different shared key identifiers together. that's a real no-no ๐Ÿ™…โ€โ™€๏ธ

initially, the key with key identifier 0 is the active key ๐ŸŒˆ

8.1.19. Get or Set Delayed SACK Timer (SCTP_DELAYED_SACK) โฐ

this one changes how delayed SACKs get handled ๐ŸŽ€. you can get or set the delayed SACK time (in milliseconds), and you can also change the delayed SACK frequency. if you change the frequency to 1, the delayed SACK algorithm gets disabled, bye bye ๐Ÿ‘‹. also fyi, if sack_delay or sack_freq is 0 when you set this option, the current values just stay the same, no changes โœจ

to access and modify these parameters, you use this structure:

struct sctp_sack_info {
  sctp_assoc_t sack_assoc_id;
  uint32_t sack_delay;
  uint32_t sack_freq;
};
  • sack_assoc_id: this gets ignored for one-to-one style sockets ๐Ÿ™ˆ. for one-to-many style sockets, it tells you which association you're acting on. the special SCTP_{FUTURE|CURRENT|ALL}_ASSOC can be used here too ๐Ÿ’–
  • sack_delay: the number of milliseconds you're asking the delayed SACK timer to be set to โฑ๏ธ. this value is defined in [RFC4960] to be between 200 and 500 milliseconds, so stay in that range bestie ๐Ÿงš
  • sack_freq: the number of packets that have to get received before a SACK gets sent without waiting for the delay timer to run out ๐Ÿ“. the default is 2, and setting it to 1 disables the delayed SACK algorithm ๐Ÿ’…

8.1.20. Get or Set Fragmented Interleave (SCTP_FRAGMENT_INTERLEAVE) ๐Ÿงฉ

okay this is giving receiver drama ๐ŸŽญ. fragmented interleave controls how messages get presented to the message receiver ๐Ÿ’œ. there are three levels, and two of them affect one-to-one style sockets, while one-to-many style sockets are affected by all three โœจ

this option takes an integer. you can set it to 0, 1, or 2. trying to set any other value returns an error, so don't even try ๐Ÿ™ƒ

here's what the three levels do for the receiver ๐Ÿ‘‘:

  • level 0: prevents any messages from interleaving ๐Ÿ™…โ€โ™€๏ธ. when a partial delivery starts, nothing else gets received except the message being partially delivered. if another message shows up on a different stream (or association) that could be delivered, it just sits there blocked until you read all of the partially delivered message ๐Ÿ˜ญ
  • level 1: allows interleaving of messages from different associations ๐ŸŒธ. for one-to-one style sockets, level 0 and level 1 mean the exact same thing, since a one-to-one style socket always gets messages from the same association ๐Ÿ’€. heads up, setting a one-to-many style socket to this level can cause multiple partial deliveries from different associations. but for any given association, only one message gets delivered until all of its parts have been delivered. so one big message, read with association identifier "X", blocks other messages from association "X" from being delivered ๐Ÿข
  • level 2: allows complete interleaving of messages ๐Ÿ”ฅ. this level means the sender has to carefully observe the peer association identifier (or address) and also pay close attention to the stream number ๐Ÿง. with this on, a partially delivered message might start getting delivered for association "X" stream "Y", and then the next receive could return a message from association "X" stream "Z" ๐Ÿฆ‹. no other messages get delivered for association "X" stream "Y" until all of stream "Y"'s partially delivered message is read. this option also affects one-to-one style sockets ๐ŸŽ€. and for one-to-many style sockets, the next receive might return another stream's message from the same association, or even some other association's message altogether ๐ŸŒˆ
an implementation should default one-to-many style sockets to level 1. otherwise, a peer could start sending a partial message and block every other peer from sending data ๐Ÿ˜ฑ

level 2 is kind of a lot though, because the app has to be aware of the association (through the association identifier or the peer's address) and also the stream number ๐Ÿคฏ. the stream number isn't there unless you've subscribed to the sctp_data_io_event (see Section 6.2), which is deprecated, or you've enabled the SCTP_RECVRCVINFO socket option (see Section 8.1.29) ๐Ÿ’…. that's also why we recommend defaulting one-to-one style sockets to level 0, since level 1 does nothing for them โœจ. also, an implementation should return an error if an app tries to set the level to 2 without having subscribed to the sctp_data_io_event event (which is deprecated) or without having enabled the SCTP_RECVRCVINFO socket option ๐Ÿ™ˆ

for apps that have subscribed to events, those events show up in the normal socket buffer data stream ๐Ÿ’Œ. so unless you've set the fragmentation interleave level to 0, notifications can get interleaved with partially delivered messages too ๐Ÿซ 

8.1.21. Set or Get the SCTP Partial Delivery Point (SCTP_PARTIAL_DELIVERY_POINT) ๐Ÿ“

this option sets or gets the SCTP partial delivery point ๐ŸŽฏ. that point is the message size where the partial delivery API gets invoked to help free up rwnd space for the peer. so a lower value means partial deliveries happen more often ๐Ÿ”. this option expects an integer that sets or gets the partial delivery point in bytes. also, the call fails if you try to set this value bigger than the socket receive buffer size, so no oversized vibes ๐Ÿšซ

any single message with a length less than or equal to the SCTP partial delivery point gets delivered in a single read call, as long as the user-provided buffer is big enough to hold the message ๐Ÿฐ

8.1.22. Set or Get the Use of Extended Receive Info (SCTP_USE_EXT_RCVINFO) - DEPRECATED ๐Ÿ•ฐ๏ธ

this option enables or disables the extended version of the sctp_sndrcvinfo structure ๐ŸŽ. if it's disabled, the normal sctp_sndrcvinfo structure gets returned in all receive message calls. if it's enabled, the sctp_extrcvinfo structure gets returned in all receive message calls instead ๐ŸŒŸ. the default is off โœจ

heads up, the sctp_extrcvinfo structure is never used in any send call ๐Ÿ™…โ€โ™€๏ธ

this option is only here for compatibility with older apps, and it's deprecated ๐Ÿฅฒ. future apps should use SCTP_NXTINFO to get this same info through ancillary data ๐Ÿ’œ

8.1.23. Set or Get the Auto ASCONF Flag (SCTP_AUTO_ASCONF) ๐Ÿ 

this option enables or disables the automatic generation of ASCONF chunks, which add and delete addresses on an existing association ๐Ÿ—บ๏ธ. there are two caveats, though ๐Ÿ‘€: a) it only affects sockets that are bound to all addresses available to the SCTP stack, and b) the system administrator might have an overriding control that turns the ASCONF feature off no matter what the socket option says ๐Ÿšจ

this option expects an integer boolean flag. a non-zero value turns it on, and a zero value turns it off ๐Ÿ”›

8.1.24. Set or Get the Maximum Burst (SCTP_MAX_BURST) ๐Ÿ’จ

this option lets you change the maximum burst of packets that this association can emit ๐Ÿƒโ€โ™€๏ธ. the default is 4, and some implementations may restrict this setting so it can only be lowered to positive values ๐Ÿ“‰

to set or get this option, you fill in this structure:

struct sctp_assoc_value {
  sctp_assoc_t assoc_id;
  uint32_t assoc_value;
};
  • assoc_id: this gets ignored for one-to-one style sockets ๐Ÿ™ˆ. for one-to-many style sockets, it tells you which association you're acting on. the special SCTP_{FUTURE|CURRENT|ALL}_ASSOC can be used here too ๐Ÿ’–
  • assoc_value: this holds the maximum burst. setting it to 0 disables burst mitigation ๐Ÿ›‘

8.1.25. Set or Get the Default Context (SCTP_CONTEXT) ๐Ÿง 

the context field in the sctp_sndrcvinfo structure is normally only used when a failed message is retrieved, and it holds the value that got sent down on the actual send call ๐Ÿ“ฆ. this option lets you set, on a per-association basis, a default context that gets received when reading messages from the peer ๐Ÿซถ. this is super helpful for apps using one-to-many style sockets, since you can keep some reference to an internal state machine that's processing messages on the association ๐Ÿงš. just note that setting this value only affects messages received from the peer, and it doesn't touch the value saved with outbound messages ๐Ÿ’…

to set or get this option, you fill in this structure:

struct sctp_assoc_value {
  sctp_assoc_t assoc_id;
  uint32_t assoc_value;
};
  • assoc_id: this gets ignored for one-to-one style sockets ๐Ÿ™ˆ. for one-to-many style sockets, it tells you which association you're acting on. the special SCTP_{FUTURE|CURRENT|ALL}_ASSOC can be used here too ๐Ÿ’–
  • assoc_value: this holds the context ๐ŸŽ€

8.1.26. Enable or Disable Explicit EOR Marking (SCTP_EXPLICIT_EOR) โœ๏ธ

this boolean flag enables or disables explicit end of record (EOR) marking ๐Ÿ. when it's enabled, you can make multiple send system calls to send a record, and you have to indicate that you're done sending a particular record by including the SCTP_EOR flag ๐Ÿ™Œ. if the boolean flag is disabled, then every individual send system call is treated as if the SCTP_EOR indicator is set on it implicitly, so you don't have to add the flag yourself ๐Ÿซถ. the default is off ๐ŸŒ™

this option expects an integer boolean flag. a non-zero value turns it on, and a zero value turns it off ๐Ÿ”›

8.1.27. Enable SCTP Port Reusage (SCTP_REUSE_PORT) ๐Ÿฆ‹

okay bestie, this option ONLY works for one-to-one style SCTP sockets ๐Ÿ’…. If you try it on a one-to-many style socket, you get an error, no cap ๐Ÿ˜ญ.

This option expects an integer boolean flag ๐ŸŽ€. A non-zero value turns it on, and a zero value turns it off.

Now the rules are a little dramatic ๐Ÿ™ˆ. This socket option must not be used after calling bind() or sctp_bindx() for a one-to-one style SCTP socket. If you use bind() or sctp_bindx() on a socket that has SCTP_REUSE_PORT set, every other SCTP socket bound to the same port must have set SCTP_REUSE_PORT too. If you call bind() or sctp_bindx() on a socket that never set SCTP_REUSE_PORT, it will fail if there are other sockets bound to the same port. And at most one socket bound to the same port may be listening ๐Ÿ‘‘.

It should be noted that the behavior of the socket-level socket option to reuse ports and/or addresses for SCTP sockets is unspecified. So the spec just doesn't pin that part down ๐Ÿคทโ€โ™€๏ธโœจ

8.1.28. Set Notification Event (SCTP_EVENT) ๐Ÿ””

This socket option is used to set a specific notification option. Please see Section 6.2.2 for a full description of this option and its usage. Go read that section, it's the full tea โ˜•๐Ÿซถ

8.1.29. Enable or Disable the Delivery of SCTP_RCVINFO as Ancillary Data (SCTP_RECVRCVINFO) ๐ŸŒธ

Setting this option specifies that SCTP_RCVINFO (defined in Section 5.3.5) is returned as ancillary data by recvmsg(). Main character energy for your receive info ๐Ÿงš

This option expects an integer boolean flag, where a non-zero value turns on the option, and a zero value turns off the option.

8.1.30. Enable or Disable the Delivery of SCTP_NXTINFO as Ancillary Data (SCTP_RECVNXTINFO) ๐Ÿ“

Setting this option specifies that SCTP_NXTINFO (defined in Section 5.3.6) is returned as ancillary data by recvmsg(). It's giving next-info vibes ๐Ÿ’–

This option expects an integer boolean flag, where a non-zero value turns on the option, and a zero value turns off the option.

8.1.31. Set Default Send Parameters (SCTP_DEFAULT_SNDINFO) ๐Ÿ’œ

okay so apps that want to use the sendto() system call might want a default set of parameters, instead of attaching them as ancillary data every single time ๐Ÿฅน. This socket option lets an app set the default sctp_sndinfo structure. You just pass the sctp_sndinfo structure (defined in Section 5.3.4) into this call. The input parameters it takes are snd_sid, snd_flags, snd_ppid, and snd_context. The snd_flags parameter is a bitwise OR of SCTP_UNORDERED, SCTP_EOF, and SCTP_SENDALL ๐Ÿ”ฅ. The snd_assoc_id field picks the association the parameters apply to. For a one-to-many style socket, any of the predefined constants are also allowed in this field. The field is ignored for one-to-one style sockets ๐Ÿ’…

8.1.32. Set Default PR-SCTP Parameters (SCTP_DEFAULT_PRINFO) ๐ŸŽ€

This option sets and gets the default parameters for PR-SCTP. Send calls can overwrite them with their own specific info โœจ

The following structure is used to access and modify these parameters:

struct sctp_default_prinfo {
  uint16_t pr_policy;
  uint32_t pr_value;
  sctp_assoc_t pr_assoc_id;
};
  • pr_policy: This field is the same as that described in Section 5.3.7.
  • pr_value: This field is the same as that described in Section 5.3.7.
  • pr_assoc_id: This field is ignored for one-to-one style sockets. For one-to-many style sockets, pr_assoc_id can be a particular association identifier or SCTP_{FUTURE|CURRENT|ALL}_ASSOC. ๐Ÿงš๐ŸŒˆ

8.2. Read-Only Options ๐Ÿ”’

The options defined in this subsection are read-only ๐Ÿ™…โ€โ™€๏ธ. Using this option in a setsockopt() call will result in an error indicating EOPNOTSUPP. Look, but don't touch ๐Ÿ’€

8.2.1. Association Status (SCTP_STATUS) ๐Ÿ“Š

Applications can retrieve current status info about an association, including association state, peer receiver window size, number of unacknowledged DATA chunks, and number of DATA chunks pending receipt. This info is read-only ๐Ÿ‘€

The following structure is used to access this information:

struct sctp_status {
  sctp_assoc_t sstat_assoc_id;
  int32_t  sstat_state;
  uint32_t sstat_rwnd;
  uint16_t sstat_unackdata;
  uint16_t sstat_penddata;
  uint16_t sstat_instrms;
  uint16_t sstat_outstrms;
  uint32_t sstat_fragmentation_point;
  struct sctp_paddrinfo sstat_primary;
};
  • sstat_assoc_id: This parameter is ignored for one-to-one style sockets. For one-to-many style sockets, it holds the identifier for the association. All notifications for a given association have the same association identifier. The special SCTP_{FUTURE|CURRENT|ALL}_ASSOC cannot be used.
  • sstat_state: This contains the association's current state, i.e., one of the following values:
    • SCTP_CLOSED
    • SCTP_BOUND
    • SCTP_LISTEN
    • SCTP_COOKIE_WAIT
    • SCTP_COOKIE_ECHOED
    • SCTP_ESTABLISHED
    • SCTP_SHUTDOWN_PENDING
    • SCTP_SHUTDOWN_SENT
    • SCTP_SHUTDOWN_RECEIVED
    • SCTP_SHUTDOWN_ACK_SENT
  • sstat_rwnd: This contains the association peer's current receiver window size.
  • sstat_unackdata: This is the number of unacknowledged DATA chunks.
  • sstat_penddata: This is the number of DATA chunks pending receipt.
  • sstat_instrms: This is the number of streams that the peer will be using outbound.
  • sstat_outstrms: This is the number of outbound streams that the endpoint is allowed to use.
  • sstat_fragmentation_point: This is the size at which SCTP fragmentation will occur.
  • sstat_primary: This is information on the current primary peer address. ๐ŸŒŸ

To access these status values, the application calls getsockopt() with the option name SCTP_STATUS ๐Ÿ’–

8.2.2. Peer Address Information (SCTP_GET_PEER_ADDR_INFO) ๐Ÿ—บ๏ธ

Applications can retrieve info about a specific peer address of an association, including its reachability state, congestion window, and retransmission timer values. This info is read-only too ๐Ÿซถ

The following structure is used to access this information:

struct sctp_paddrinfo {
  sctp_assoc_t spinfo_assoc_id;
  struct sockaddr_storage spinfo_address;
  int32_t  spinfo_state;
  uint32_t spinfo_cwnd;
  uint32_t spinfo_srtt;
  uint32_t spinfo_rto;
  uint32_t spinfo_mtu;
};
  • spinfo_assoc_id: This parameter is ignored for one-to-one style sockets.

For one-to-many style sockets, this field may be filled by the application, and if so, this field will have priority in looking up the association instead of using the address specified in spinfo_address. Note that if the address does not belong to the association specified, then this call will fail. If the application does not fill in the spinfo_assoc_id, then the address will be used to look up the association, and on return, this field will have the valid association identifier. In other words, this call can be used to translate an address into an association identifier. Note that the predefined constants are not allowed for this option. ๐Ÿ”

  • spinfo_address: This is filled by the application and contains the peer address of interest.
  • spinfo_state: This contains the peer address's state:
    • SCTP_UNCONFIRMED: This is the initial state of a peer address.
    • SCTP_ACTIVE: This state is entered the first time after path verification. It can also be entered if the state is SCTP_INACTIVE and the path supervision detects that the peer address is reachable again. โœ…
    • SCTP_INACTIVE: This state is entered whenever a path failure is detected. ๐Ÿ’”
  • spinfo_cwnd: This contains the peer address's current congestion window.
  • spinfo_srtt: This contains the peer address's current smoothed round-trip time calculation in milliseconds.
  • spinfo_rto: This contains the peer address's current retransmission timeout value in milliseconds.
  • spinfo_mtu: This is the current Path MTU of the peer address. It is the number of bytes available in an SCTP packet for chunks. ๐Ÿ“ฆ

8.2.3. Get the List of Chunks the Peer Requires to Be Authenticated (SCTP_PEER_AUTH_CHUNKS) ๐Ÿ”

This option gets a list of chunk types (see [RFC4960]) for a specified association that the peer requires to be received authenticated only ๐Ÿชช

The following structure is used to access these parameters:

struct sctp_authchunks {
  sctp_assoc_t gauth_assoc_id;
  uint32_t gauth_number_of_chunks
  uint8_t  gauth_chunks[];
};
  • gauth_assoc_id: This parameter indicates for which association the user is requesting the list of peer-authenticated chunks. For one-to-one style sockets, this parameter is ignored. Note that the predefined constants are not allowed with this option.
  • gauth_number_of_chunks: This parameter gives the number of elements in the array gauth_chunks.
  • gauth_chunks: This parameter contains an array of chunk types that the peer is requesting to be authenticated. If the passed-in buffer size is not large enough to hold the list of chunk types, ENOBUFS is returned. ๐Ÿซฃ

8.2.4. Get the List of Chunks the Local Endpoint Requires to Be Authenticated (SCTP_LOCAL_AUTH_CHUNKS) ๐Ÿ›ก๏ธ

This option gets a list of chunk types (see [RFC4960]) for a specified association that the local endpoint requires to be received authenticated only. Same energy, other side of the connection ๐Ÿชž

The following structure is used to access these parameters:

struct sctp_authchunks {
  sctp_assoc_t gauth_assoc_id;
  uint32_t gauth_number_of_chunks;
  uint8_t  gauth_chunks[];
};
  • gauth_assoc_id: This parameter is ignored for one-to-one style sockets. For one-to-many style sockets, the application may fill in an association identifier or SCTP_FUTURE_ASSOC. It is an error to use SCTP_{CURRENT|ALL}_ASSOC in gauth_assoc_id.
  • gauth_number_of_chunks: This parameter gives the number of elements in the array gauth_chunks.
  • gauth_chunks: This parameter contains an array of chunk types that the local endpoint is requesting to be authenticated. If the passed-in buffer is not large enough to hold the list of chunk types, ENOBUFS is returned. ๐Ÿ™ˆ

8.2.5. Get the Current Number of Associations (SCTP_GET_ASSOC_NUMBER) ๐Ÿ”ข

This option gets the current number of associations that are attached to a one-to-many style socket. The option value is an uint32_t. Note that this number is only a snapshot ๐Ÿ“ธ. This means the number of associations may have changed by the time the caller gets back the option result. Blink and you miss it โœจ

For a one-to-one style socket, this socket option results in an error.

8.2.6. Get the Current Identifiers of Associations (SCTP_GET_ASSOC_ID_LIST) ๐Ÿชช

This option gets the current list of SCTP association identifiers of the SCTP associations handled by a one-to-many style socket ๐ŸŒˆ

The option value has the structure:

struct sctp_assoc_ids {
  uint32_t gaids_number_of_ids;
  sctp_assoc_t gaids_assoc_id[];
};

The caller must provide a large enough buffer to hold all association identifiers. If the buffer is too small, an error must be returned. The user can use the SCTP_GET_ASSOC_NUMBER socket option to get an idea of how large the buffer has to be ๐Ÿงฎ. gaids_number_of_ids gives the number of elements in the array gaids_assoc_id. Note also that some or all of sctp_assoc_t returned in the array may become invalid by the time the caller gets back the result. Things change fast, babe ๐Ÿ˜ญ

For a one-to-one style socket, this socket option results in an error.

8.3. Write-Only Options ๐Ÿซถ

okay so these ones are write-only, bestie ๐Ÿ’… if you try to read one back with getsockopt() or sctp_opt_info(), you're gonna get an error indicating EOPNOTSUPP ๐Ÿ™ˆ

8.3.1. Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)

this call asks the peer to mark the address you enclose as the association's primary (see [RFC5061]) ๐Ÿฆ‹ the address you enclose must be one of the association's locally bound addresses, so no random address allowed โœจ

the struct you use for a set peer primary request looks like this:

struct sctp_setpeerprim {
  sctp_assoc_t sspp_assoc_id;
  struct sockaddr_storage sspp_addr;
};
  • sspp_assoc_id: this one gets ignored for one-to-one style sockets. for one-to-many style sockets, it tells you which association the request is for. also, the predefined constants are not allowed for this option ๐Ÿšซ
  • sspp_addr: the address you want to set as primary ๐ŸŽ€

8.3.2. Add a Chunk That Must Be Authenticated (SCTP_AUTH_CHUNK)

this set option adds a chunk type that you're asking to be received only in an authenticated way ๐Ÿ” changes to the list only affect future associations on the socket, so it won't touch the ones already going ๐ŸŒธ

the struct for adding a chunk is:

struct sctp_authchunk {
  uint8_t sauth_chunk;
};
  • sauth_chunk: the chunk type you're requesting to be authenticated ๐Ÿ’–

the chunk types for INIT, INIT-ACK, SHUTDOWN-COMPLETE, and AUTH chunks must not be used. if you use them anyway, an error must be returned ๐Ÿ’€ also, using this option turns on SCTP AUTH in situations where it isn't required by other means, like when you're doing dynamic address reconfiguration โœจ

8.3.3. Set a Shared Key (SCTP_AUTH_KEY)

this option sets a shared secret key that gets used to build an association shared key ๐Ÿ—๏ธ bestie, it's giving security glow-up ๐Ÿ’œ

the struct you use to access and modify these parameters is:

struct sctp_authkey {
  sctp_assoc_t sca_assoc_id;
  uint16_t sca_keynumber;
  uint16_t sca_keylength;
  uint8_t  sca_key[];
};
  • sca_assoc_id: this tells you which association the shared key is being set on. you can use the special SCTP_{FUTURE|CURRENT|ALL}_ASSOC values. for one-to-one style sockets, this parameter gets ignored. BUT, the tea is that on one-to-one style sockets, this option sets a key on the association if the socket is connected. otherwise, it sets a key on the endpoint ๐Ÿ‘‘
  • sca_keynumber: the shared key identifier you'll use to refer to this key later ๐Ÿ”‘ if a key with that index already exists, the new key replaces the old one. also, shared key identifier 0 defaults to a null key ๐Ÿซง
  • sca_keylength: the length of the array sca_key ๐Ÿ“
  • sca_key: an array of bytes that the endpoint (or association) uses as the shared secret key ๐Ÿ“ if this field's length is zero, a null key gets set ๐Ÿ™ˆ

8.3.4. Deactivate a Shared Key (SCTP_AUTH_DEACTIVATE_KEY)

this set option says the application won't send user messages using the indicated key identifier anymore ๐Ÿซถ

the struct is:

struct sctp_authkeyid {
  sctp_assoc_t scact_assoc_id;
  uint16_t scact_keynumber;
};
  • scact_assoc_id: this tells you which association the shared key identifier is being deleted from. the special SCTP_{FUTURE|CURRENT|ALL}_ASSOC values work here too. for one-to-one style sockets, this parameter gets ignored. but this option deactivates the key on the association if the socket is connected, and otherwise it deactivates the key on the endpoint ๐Ÿงš
  • scact_keynumber: the shared key identifier you're asking to be deactivated. the key identifier must match an existing shared key. if this parameter is zero, use of the null key identifier 0 gets deactivated on the endpoint and/or association ๐ŸŒˆ

the currently active key cannot be deactivated ๐Ÿ™…โ€โ™€๏ธ

8.3.5. Delete a Shared Key (SCTP_AUTH_DELETE_KEY)

this set option deletes an SCTP association's shared secret key, but only if that key has already been deactivated ๐Ÿ—‘๏ธ bestie, order matters ๐Ÿ’…

the struct is the same one you saw in Section 8.3.4:

struct sctp_authkeyid {
  sctp_assoc_t scact_assoc_id;
  uint16_t scact_keynumber;
};
  • scact_assoc_id: this tells you which association the shared key identifier is being deleted from. the special SCTP_{FUTURE|CURRENT|ALL}_ASSOC values can be used. for one-to-one style sockets, this parameter gets ignored. but this option deletes the key from the association if the socket is connected, and otherwise it deletes the key from the endpoint ๐ŸŒธ
  • scact_keynumber: the shared key identifier you're asking to be deleted. the key identifier must correspond to an existing shared key, and it must not be in use for any packet being sent by the SCTP implementation. this means, in particular, that it must be deactivated first ๐Ÿ’€ if this parameter is zero, use of the null key identifier 0 gets deleted from the endpoint and/or association โœจ

only deactivated keys that are no longer used by an association can be deleted ๐Ÿฆ‹


9. New Functions ๐Ÿงš

depending on the system, the following interface can be implemented as a system call or a library function ๐Ÿ’–

9.1. sctp_bindx() ๐ŸŒธ

this function lets you bind a specific subset of addresses or, if the SCTP extension described in [RFC5061] is supported, add or delete specific addresses ๐Ÿซถ

the function prototype is:

int sctp_bindx(int sd,
              struct sockaddr *addrs,
              int addrcnt,
              int flags);

if sd is an IPv4 socket, the addresses you pass must be IPv4 addresses. if sd is an IPv6 socket, the addresses you pass can be either IPv4 or IPv6 addresses ๐ŸŽ€

a single address can be specified as INADDR_ANY for an IPv4 address, or as IN6ADDR_ANY_INIT or in6addr_any for an IPv6 address. see Section 3.1.2 for this usage โœจ

addrs is a pointer to an array of one or more socket addresses. each address is in its appropriate structure. for an IPv6 socket, you use an array of sockaddr_in6, and for an IPv4 socket, you use an array of sockaddr_in. you tell the function how many addresses are in the array with addrcnt ๐Ÿ“ heads up, the wildcard addresses cannot be combined with non-wildcard addresses on a socket with this function. doing that will result in an error ๐Ÿ™ˆ

on success, sctp_bindx() returns 0. on failure, it returns -1 and sets errno to the appropriate error code ๐Ÿ“

for SCTP, the port in each socket address must be the same, or sctp_bindx() will fail and set errno to EINVAL ๐Ÿšซ

the flags parameter is formed from the bitwise OR of zero or more of the following currently defined flags:

  • SCTP_BINDX_ADD_ADDR
  • SCTP_BINDX_REM_ADDR

SCTP_BINDX_ADD_ADDR tells SCTP to add the given addresses to the socket (aka the endpoint), and SCTP_BINDX_REM_ADDR tells SCTP to remove them. these two flags are mutually exclusive, so if you give both, sctp_bindx() will fail with EINVAL ๐Ÿ˜ญ you also can't remove every address from a socket. sctp_bindx() will reject that with EINVAL too ๐Ÿ™…โ€โ™€๏ธ

you can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate extra addresses with an endpoint after calling bind(). or you can use sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses from a listening socket, so that no new association accepted gets associated with those addresses ๐Ÿฆ‹ if the endpoint supports dynamic address reconfiguration, an SCTP_BINDX_REM_ADDR or SCTP_BINDX_ADD_ADDR may cause the endpoint to send the appropriate message to its peers so they can update their address lists โœจ

adding and removing addresses from established associations is an optional functionality. implementations that don't support it should return -1 and set errno to EOPNOTSUPP ๐Ÿซง

sctp_bindx() can be called on an already bound socket or on an unbound socket. if the socket is unbound and the first port number in the addrs parameter is zero, the kernel chooses a port number. all port numbers after the first one being 0 must also be zero. if the first port number is not zero, the following port numbers must be zero or have the same value as the first one. for an already bound socket, all port numbers provided must be the bound one or 0 ๐ŸŒˆ

sctp_bindx() is an atomic operation ๐Ÿ’… so the binding will either succeed on all addresses or fail on all of them. if you provide multiple addresses and the call fails, there's no indication of which address caused the failure. the only way to find the specific error is to call sctp_bindx() sequentially with only one address per call ๐Ÿ•ต๏ธโ€โ™€๏ธ

9.2. sctp_peeloff() ๐Ÿฆ‹

after an association is established on a one-to-many style socket, the application may want to branch the association off into a separate socket or file descriptor ๐Ÿ’–

this is especially handy when, say, the application wants a bunch of sporadic message senders and receivers to stay on the original one-to-many style socket, but branches the associations carrying high-volume data traffic into their own separate socket descriptors ๐Ÿš€

the application uses the sctp_peeloff() call to branch an association off into a separate socket. (fun fact, the semantics are a little different from the traditional one-to-one style accept() call ๐Ÿ™ˆ) also, the new socket is a one-to-one style socket, so it's confined to the operations allowed for one-to-one style sockets ๐ŸŽ€

the function prototype is:

int sctp_peeloff(int sd,
                 sctp_assoc_t assoc_id);

and the arguments are:

  • sd: the original one-to-many style socket descriptor returned from the socket() system call (see Section 3.1.1) ๐Ÿงš
  • assoc_id: the identifier of the association that's going to be branched off to a separate file descriptor. (in a traditional one-to-one style accept() call this would be an out parameter, but for the one-to-many style call, it's an in parameter ๐Ÿ’œ)

the function returns a non-negative file descriptor representing the branched-off association, or -1 if an error occurred, and then errno is set appropriately ๐ŸŒˆ

9.3. sctp_getpaddrs() โœจ

okay so sctp_getpaddrs() is the function that gives you all the peer addresses in an association ๐Ÿ’– bestie, it's giving "show me everyone you're connected to" ๐Ÿซถ

The function prototype is

int sctp_getpaddrs(int sd,
                   sctp_assoc_t id,
                   struct sockaddr **addrs);

on return, addrs points to a dynamically allocated array of sockaddr structures of the appropriate type for the socket type ๐ŸŽ€ the caller should use sctp_freepaddrs() to free the memory. note that the in/out parameter addrs must not be NULL ๐Ÿ™ˆ

if sd is an IPv4 socket, the addresses returned will be all IPv4 addresses ๐ŸŒธ if sd is an IPv6 socket, the addresses returned can be a mix of IPv4 or IPv6 addresses, with IPv4 addresses returned according to the SCTP_I_WANT_MAPPED_V4_ADDR option setting. the vibe is "it depends on your settings" ๐Ÿ’…

for one-to-many style sockets, id specifies the association to query ๐Ÿฆ‹ for one-to-one style sockets, id is ignored.

on success, sctp_getpaddrs() returns the number of peer addresses in the association โœจ if there is no association on this socket, sctp_getpaddrs() returns 0, and the value of *addrs is undefined ๐Ÿ˜ญ if an error occurs, sctp_getpaddrs() returns -1, and the value of *addrs is undefined. no cap, undefined is doing a lot of work here ๐Ÿ’€

9.4. sctp_freepaddrs() ๐Ÿงš

sctp_freepaddrs() frees all resources allocated by sctp_getpaddrs() ๐Ÿซง the prototype is

void sctp_freepaddrs(struct sockaddr *addrs);

and addrs is the array of peer addresses returned by sctp_getpaddrs(). cleanup crew, assemble ๐Ÿงน

9.5. sctp_getladdrs() ๐ŸŒˆ

sctp_getladdrs() returns all locally bound addresses on a socket ๐Ÿ’œ the function prototype is

int sctp_getladdrs(int sd,
                   sctp_assoc_t id,
                   struct sockaddr **addrs);

on return, addrs points to a dynamically allocated array of sockaddr structures of the appropriate type for the socket type ๐ŸŽ€ the caller should use sctp_freeladdrs() to free the memory. note that the in/out parameter addrs must not be NULL ๐Ÿ™ˆ

if sd is an IPv4 socket, the addresses returned will be all IPv4 addresses ๐ŸŒธ if sd is an IPv6 socket, the addresses returned can be a mix of IPv4 or IPv6 addresses, with IPv4 addresses returned according to the SCTP_I_WANT_MAPPED_V4_ADDR option setting. same energy as before, slay โœจ

for one-to-many style sockets, id specifies the association to query ๐Ÿฆ‹ for one-to-one style sockets, id is ignored.

if the id field is set to the value '0', then the locally bound addresses are returned without regard to any particular association. ooh, the "no association needed" mode ๐Ÿ‘‘

on success, sctp_getladdrs() returns the number of local addresses bound to the socket โœจ if the socket is unbound, sctp_getladdrs() returns 0, and the value of *addrs is undefined ๐Ÿ˜ญ if an error occurs, sctp_getladdrs() returns -1, and the value of *addrs is undefined.

9.6. sctp_freeladdrs() ๐ŸŒท

sctp_freeladdrs() frees all resources allocated by sctp_getladdrs() ๐Ÿซถ the prototype is

void sctp_freeladdrs(struct sockaddr *addrs);

and addrs is the array of local addresses returned by sctp_getladdrs(). same cleanup routine, different guest list ๐ŸŽ‰

9.7. sctp_sendmsg() - DEPRECATED ๐Ÿ™…โ€โ™€๏ธ

this one is deprecated, so sctp_sendv() (see Section 9.12) should be used instead ๐Ÿ’… bestie, glow up to the newer function ๐Ÿฆ‹

an implementation may provide a library function (or possibly system call) to assist the user with the advanced features of SCTP ๐Ÿงš the function prototype is

ssize_t sctp_sendmsg(int sd,
                     const void *msg,
                     size_t len,
                     const struct sockaddr *to,
                     socklen_t tolen,
                     uint32_t ppid,
                     uint32_t flags,
                     uint16_t stream_no,
                     uint32_t timetolive,
                     uint32_t context);

and the arguments are

  • sd: The socket descriptor.
  • msg: The message to be sent.
  • len: The length of the message.
  • to: The destination address of the message.
  • tolen: The length of the destination address.
  • ppid: The same as sinfo_ppid (see Section 5.3.2).
  • flags: The same as sinfo_flags (see Section 5.3.2).
  • stream_no: The same as sinfo_stream (see Section 5.3.2).
  • timetolive: The same as sinfo_timetolive (see Section 5.3.2).
  • context: The same as sinfo_context (see Section 5.3.2).

the call returns the number of characters sent, or -1 if an error occurred ๐ŸŒธ the variable errno is then set appropriately.

sending a message using sctp_sendmsg() is atomic (unless explicit EOR marking is enabled on the socket specified by sd) โœจ

using sctp_sendmsg() on a non-connected one-to-one style socket for implicit connection setup may or may not work, depending on the SCTP implementation ๐Ÿคทโ€โ™€๏ธ it's giving "maybe, maybe not" energy.

9.8. sctp_recvmsg() - DEPRECATED ๐Ÿ™…โ€โ™€๏ธ

also deprecated, so sctp_recvv() (see Section 9.13) should be used instead ๐Ÿ’–

an implementation may provide a library function (or possibly system call) to assist the user with the advanced features of SCTP ๐Ÿฆ‹ note that in order for the sctp_sndrcvinfo structure to be filled in by sctp_recvmsg(), the caller must enable the sctp_data_io_event with the SCTP_EVENTS option ๐ŸŽ€ note that the setting of the SCTP_USE_EXT_RCVINFO will affect this function as well, causing the sctp_sndrcvinfo information to be extended. so many settings, so little time ๐Ÿ˜ญ

the function prototype is

ssize_t sctp_recvmsg(int sd,
                     void *msg,
                     size_t len,
                     struct sockaddr *from,
                     socklen_t *fromlen
                     struct sctp_sndrcvinfo *sinfo
                     int *msg_flags);

and the arguments are

  • sd: The socket descriptor.
  • msg: The message buffer to be filled.
  • len: The length of the message buffer.
  • from: A pointer to an address to be filled with the address of the sender of this message.
  • fromlen: An in/out parameter describing the from length.
  • sinfo: A pointer to an sctp_sndrcvinfo structure to be filled upon receipt of the message.
  • msg_flags: A pointer to an integer to be filled with any message flags (e.g., MSG_NOTIFICATION). Note that this field is an in-out field. Options for the receive may also be passed into the value (e.g., MSG_PEEK). On return from the call, the msg_flags value will be different than what was sent in to the call. If implemented via a recvmsg() call, the msg_flags parameter should only contain the value of the flags from the recvmsg() call. this one is a little plot twisty, bestie ๐ŸŒ€

the call returns the number of bytes received, or -1 if an error occurred ๐Ÿ˜ญ the variable errno is then set appropriately.

9.9. sctp_connectx() ๐Ÿซง

an implementation may provide a library function (or possibly system call) to assist the user with associating to an endpoint that is multi-homed ๐ŸŒˆ much like sctp_bindx(), this call allows a caller to specify multiple addresses at which a peer can be reached. the way the SCTP stack uses the list of addresses to set up the association is implementation dependent ๐Ÿงš this function only specifies that the stack will try to make use of all of the addresses in the list when needed. so it's a promise to try ๐Ÿ‘‘

note that the list of addresses passed in is only used for setting up the association ๐Ÿ’… it does not necessarily equal the set of addresses the peer uses for the resulting association. if the caller wants to find out the set of peer addresses, it must use sctp_getpaddrs() to retrieve them after the association has been set up. go check with sctp_getpaddrs() after ๐Ÿซถ

the function prototype is

int sctp_connectx(int sd,
                  struct sockaddr *addrs,
                  int addrcnt,
                  sctp_assoc_t *id);

and the arguments are

  • sd: The socket descriptor.
  • addrs: An array of addresses.
  • addrcnt: The number of addresses in the array.
  • id: An output parameter that, if passed in as non-NULL, will return the association identifier for the newly created association (if successful).

the call returns 0 on success or -1 if an error occurred ๐ŸŽ€ the variable errno is then set appropriately.

9.10. sctp_send() - DEPRECATED ๐Ÿ™…โ€โ™€๏ธ

deprecated again, so sctp_sendv() should be used instead ๐Ÿฆ‹

an implementation may provide another alternative function or system call to assist an application with the sending of data without the use of the cmsghdr structures ๐ŸŒธ the function prototype is

ssize_t sctp_send(int sd,
                  const void *msg,
                  size_t len,
                  const struct sctp_sndrcvinfo *sinfo,
                  int flags);

and the arguments are

  • sd: The socket descriptor.
  • msg: The message to be sent.
  • len: The length of the message.
  • sinfo: A pointer to an sctp_sndrcvinfo structure used as described in Section 5.3.2 for a sendmsg() call.
  • flags: The same flags as used by the sendmsg() call flags (e.g., MSG_DONTROUTE).

the call returns the number of bytes sent, or -1 if an error occurred โœจ the variable errno is then set appropriately.

this function call may also be used to terminate an association using an association identifier by setting the sinfo.sinfo_flags to SCTP_EOF and the sinfo.sinfo_assoc_id to the association that needs to be terminated ๐Ÿ’€ in such a case, len can be zero. goodbye association, it's been a ride ๐Ÿ‘‹

using sctp_send() on a non-connected one-to-one style socket for implicit connection setup may or may not work, depending on the SCTP implementation ๐Ÿคทโ€โ™€๏ธ

sending a message using sctp_send() is atomic unless explicit EOR marking is enabled on the socket specified by sd ๐ŸŽ€

9.11. sctp_sendx() - DEPRECATED ๐Ÿ™…โ€โ™€๏ธ

deprecated, so sctp_sendv() should be used instead ๐Ÿ’…

an implementation may provide another alternative function or system call to assist an application with the sending of data without the use of the cmsghdr structure, and to provide a list of addresses ๐Ÿงš the list of addresses is provided for implicit association setup. in such a case, the list of addresses serves the same purpose as the addresses given in sctp_connectx() (see Section 9.9). it's the same vibe as sctp_connectx() ๐Ÿซถ

the function prototype is

ssize_t sctp_sendx(int sd,
                   const void *msg,
                   size_t len,
                   struct sockaddr *addrs,
                   int addrcnt,
                   struct sctp_sndrcvinfo *sinfo,
                   int flags);

and the arguments are

  • sd: The socket descriptor.
  • msg: The message to be sent.
  • len: The length of the message.
  • addrs: An array of addresses.
  • addrcnt: The number of addresses in the array.
  • sinfo: A pointer to an sctp_sndrcvinfo structure used as described in Section 5.3.2 for a sendmsg() call.
  • flags: The same flags as used by the sendmsg() call flags (e.g., MSG_DONTROUTE).

the call returns the number of bytes sent, or -1 if an error occurred ๐ŸŒธ the variable errno is then set appropriately.

note that in the case of implicit connection setup, on return from this call, the sinfo_assoc_id field of the sinfo structure will contain the new association identifier ๐Ÿ†” ooh, the new ID gets handed right back to you โœจ

this function call may also be used to terminate an association using an association identifier by setting the sinfo.sinfo_flags to SCTP_EOF and the sinfo.sinfo_assoc_id to the association that needs to be terminated ๐Ÿ’€ in such a case, len would be zero.

sending a message using sctp_sendx() is atomic unless explicit EOR marking is enabled on the socket specified by sd ๐Ÿ’–

using sctp_sendx() on a non-connected one-to-one style socket for implicit connection setup may or may not work, depending on the SCTP implementation ๐Ÿคทโ€โ™€๏ธ

9.12. sctp_sendv() โœจ

Okay bestie, this one is the extensible send call ๐Ÿ’… The prototype looks like this:

ssize_t sctp_sendv(int sd,
                   const struct iovec *iov,
                   int iovcnt,
                   struct sockaddr *addrs,
                   int addrcnt,
                   void *info,
                   socklen_t infolen,
                   unsigned int infotype,
                   int flags);

So sctp_sendv() lets an app pass different send attributes to the SCTP stack when it sends a message ๐Ÿฆ‹ An implementation can offer it as a library function or as a system call, whichever it likes.

This doc defines three attribute types you can use to describe a message to send: struct sctp_sndinfo (Section 5.3.4), struct sctp_prinfo (Section 5.3.7), and struct sctp_authinfo (Section 5.3.8). When you need more than one of those at once, you use this struct:

struct sctp_sendv_spa {
  uint32_t sendv_flags;
  struct sctp_sndinfo sendv_sndinfo;
  struct sctp_prinfo sendv_prinfo;
  struct sctp_authinfo sendv_authinfo;
};

The sendv_flags field is a bitwise OR of SCTP_SEND_SNDINFO_VALID, SCTP_SEND_PRINFO_VALID, and SCTP_SEND_AUTHINFO_VALID. Those flags tell the stack which of sendv_sndinfo, sendv_prinfo, and sendv_authinfo actually hold valid info ๐ŸŽ€

Future attributes can get their own new structures too ๐ŸŒธ They don't have to be built on any of the ones above.

The arguments:

  • sd: the socket descriptor.
  • iov: the gather buffer. The data in it is treated as one single user message.
  • iovcnt: the number of elements in iov.
  • addrs: an array of addresses used to set up an association, or one address used to send the message. You pass NULL if you neither want to set up an association nor send to a specific address.
  • addrcnt: the number of addresses in the addrs array.
  • info: a pointer to the buffer holding the attribute for the message. The info_type parameter says what type it is.
  • infolen: the length of info, in bytes.
  • infotype: says what kind of info is in info. The current values are:
    • SCTP_SENDV_NOINFO: no info is provided. info is a NULL pointer and infolen is 0.
    • SCTP_SENDV_SNDINFO: info points to a struct sctp_sndinfo.
    • SCTP_SENDV_PRINFO: info points to a struct sctp_prinfo.
    • SCTP_SENDV_AUTHINFO: info points to a struct sctp_authinfo.
    • SCTP_SENDV_SPA: info points to a struct sctp_sendv_spa.
  • flags: the same flags you'd use with the sendmsg() call (e.g., MSG_DONTROUTE).

It returns the number of bytes sent, or -1 if something went wrong, and then errno is set appropriately โœจ

๐Ÿ‘€ A note on the one-to-many style socket: the struct sctp_sndinfo attribute must always be used to specify the association the message is sent on. The only time it isn't needed is when this call is setting up a brand new association.

If you pass a list of addresses in addrs to set up an association, this function behaves like calling sctp_connectx() (see Section 9.9) first with that list, and then calling sendmsg() with your message and attributes ๐Ÿ’– For a one-to-many style socket, if you include a struct sctp_sndinfo attribute, its snd_assoc_id field must be 0. When the function returns, snd_assoc_id contains the association identifier of the newly established association ๐Ÿงš You don't have to include struct sctp_sndinfo to set up an association on a one-to-many style socket. If you leave it out, you can turn on the SCTP_ASSOC_CHANGE notification and look for the SCTP_COMM_UP message to find out the association identifier.

Want to send to one specific peer address instead of the primary one? Put that address in addrs and give a struct sctp_sndinfo with snd_flags set to SCTP_ADDR_OVER ๐Ÿ“

You can also use this call to terminate an association. Pass a struct sctp_sndinfo with snd_flags set to SCTP_EOF. In that case, len would be zero ๐Ÿซถ

Sending with sctp_sendv() is atomic, unless explicit EOR marking is enabled on the socket specified by sd ๐Ÿ”ฅ


9.13. sctp_recvv() ๐ŸŒˆ

The prototype:

ssize_t sctp_recvv(int sd,
                   const struct iovec *iov,
                   int iovlen,
                   struct sockaddr *from,
                   socklen_t *fromlen,
                   void *info,
                   socklen_t *infolen,
                   unsigned int *infotype,
                   int *flags);

sctp_recvv() is the receive-side version. It gives the app a flexible way to get different SCTP attributes that go with a received message ๐Ÿฆ‹ Like the send side, an implementation can provide it as a library function or a system call.

This doc defines two kinds of attributes you can get back from this call: the attribute of the received message, and the attribute of the next message sitting in the receive buffer. You turn these on with the SCTP_RECVRCVINFO and SCTP_RECVNXTINFO socket options. The received message's attributes come back in struct sctp_rcvinfo (Section 5.3.5), and the next message's come back in struct sctp_nxtinfo (Section 5.3.6). If both options are on, both come back in this struct:

struct sctp_recvv_rn {
  struct sctp_rcvinfo recvv_rcvinfo;
  struct sctp_nxtinfo recvv_nxtinfo;
};

New types of attributes can get their own new structures later โœจ Those don't need to be based on struct sctp_recvv_rn or struct sctp_rcvinfo.

The arguments:

  • sd: the socket descriptor.
  • iov: the scatter buffer. Only one user message comes back in this buffer.
  • iovlen: the number of elements in iov.
  • from: a pointer to an address to be filled in with the sender's address for the received message.
  • fromlen: an in/out parameter describing the length of from.
  • info: a pointer to the buffer that holds the attributes of the received message. The structure type of info is determined by the info_type parameter.
  • infolen: an in/out parameter describing the size of the info buffer.
  • infotype: on return, *info_type is set to the type of the info buffer. The current values are:
    • SCTP_RECVV_NOINFO: if neither SCTP_RECVRCVINFO nor SCTP_RECVNXTINFO is enabled, no attribute is returned. If only SCTP_RECVNXTINFO is enabled but there is no next message in the buffer, no attribute is returned either. In both cases, *info_type is set to SCTP_RECVV_NOINFO.
    • SCTP_RECVV_RCVINFO: the type of info is struct sctp_rcvinfo, and the attribute relates to the received message.
    • SCTP_RECVV_NXTINFO: the type of info is struct sctp_nxtinfo, and the attribute relates to the next message in the receive buffer. This is the case when only SCTP_RECVNXTINFO is enabled and there is a next message in the buffer.
    • SCTP_RECVV_RN: the type of info is struct sctp_recvv_rn. The recvv_rcvinfo field is the attribute of the received message, and the recvv_nxtinfo field is the attribute of the next message in the buffer. This is the case when both SCTP_RECVRCVINFO and SCTP_RECVNXTINFO are enabled and there is a next message in the receive buffer.
  • flags: a pointer to an integer that gets filled with any message flags (e.g., MSG_NOTIFICATION). This one is an in/out parameter too. Receive options (e.g., MSG_PEEK) can also be passed in through it. When the call returns, the flags value will be different from what was sent in. If it's implemented via a recvmsg() call, flags should only contain the value of the flags from the recvmsg() call when calling sctp_recvv(), and on return it has the value from msg_flags ๐Ÿ’…

It returns the number of bytes received, or -1 if something went wrong, and then errno is set appropriately.


10. Security Considerations ๐Ÿ”’

Many TCP and UDP implementations reserve port numbers below 1024 for privileged users. If the target platform supports privileged users, the SCTP implementation should restrict the ability to call bind() or sctp_bindx() on these port numbers to privileged users.

Similarly, unprivileged users should not be able to set protocol parameters that could result in the congestion control algorithm being more aggressive than permitted on the public Internet. These parameters are as follows:

  • struct sctp_rtoinfo

If an unprivileged user inherits a one-to-many style socket with open associations on a privileged port, accepting new associations might be permitted, but opening new associations should not be permitted. This could be relevant for the r* family (rsh, rlogin, rwho, ...) of protocols ๐Ÿ˜ฌ

Applications using the one-to-many style sockets and using the interleave level (if 0) are subject to denial-of-service attacks, as described in Section 8.1.20. ๐Ÿšจ This is a real one, so take it seriously.

Applications needing transport layer security can use Datagram Transport Layer Security/SCTP (DTLS/SCTP) as specified in [RFC6083]. This can be implemented using the sockets API described in this document ๐Ÿ›ก๏ธ


11. Acknowledgments ๐Ÿซถ

Special acknowledgment is given to Ken Fujita, Jonathan Woods, Qiaobing Xie, and La Monte Yarroll, who helped extensively in the early formation of this document. Iconic early contributors ๐Ÿ‘‘

The authors also wish to thank Kavitha Baratakke, Mike Bartlett, Martin Becke, Jon Berger, Mark Butler, Thomas Dreibholz, Andreas Fink, Scott Kimble, Jonathan Leighton, Renee Revis, Irene Ruengeler, Dan Wing, and many others on the TSVWG mailing list for contributing valuable comments ๐Ÿ’œ

A special thanks to Phillip Conrad, for his suggested text, quick and constructive insights, and most of all his persistent fighting to keep the interface to SCTP usable for the application programmer. Literally a legend ๐Ÿฅนโœจ


12. References ๐Ÿ“š

12.1. Normative References โœจ

okay bestie, these are the normative ones, meaning you literally need them to implement this stuff. ๐Ÿซถ Heads up, I'm keeping every title and author list exactly as written so you can look them up.

[IEEE-1003.1-2008] Institute of Electrical and Electronics Engineers, "Information Technology - Portable Operating System Interface (POSIX)", IEEE Standard 1003.1, 2008. ๐Ÿ“š

[RFC3493] Gilligan, R., Thomson, S., Bound, J., McCann, J., and W. Stevens, "Basic Socket Interface Extensions for IPv6", RFC 3493, February 2003. ๐ŸŒ

[RFC3542] Stevens, W., Thomas, M., Nordmark, E., and T. Jinmei, "Advanced Sockets Application Program Interface (API) for IPv6", RFC 3542, May 2003. ๐Ÿงš

[RFC3758] Stewart, R., Ramalho, M., Xie, Q., Tuexen, M., and P. Conrad, "Stream Control Transmission Protocol (SCTP) Partial Reliability Extension", RFC 3758, May 2004. ๐Ÿฆ‹

[RFC4895] Tuexen, M., Stewart, R., Lei, P., and E. Rescorla, "Authenticated Chunks for the Stream Control Transmission Protocol (SCTP)", RFC 4895, August 2007. ๐Ÿ”

[RFC4960] Stewart, R., Ed., "Stream Control Transmission Protocol", RFC 4960, September 2007. ๐Ÿ’œ

[RFC5061] Stewart, R., Xie, Q., Tuexen, M., Maruyama, S., and M. Kozuka, "Stream Control Transmission Protocol (SCTP) Dynamic Address Reconfiguration", RFC 5061, September 2007. ๐ŸŒธ

12.2. Informative References ๐ŸŒˆ

okay these ones are informative, so they're more like good background reading. Still super worth a look, no cap. ๐Ÿ’…

[RFC0768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, August 1980. ๐ŸŽ€

[RFC0793] Postel, J., "Transmission Control Protocol", STD 7, RFC 793, September 1981. โญ

[RFC1644] Braden, R., "T/TCP -- TCP Extensions for Transactions Functional Specification", RFC 1644, July 1994. ๐Ÿ“

[RFC6083] Tuexen, M., Seggelmann, R., and E. Rescorla, "Datagram Transport Layer Security (DTLS) for Stream Control Transmission Protocol (SCTP)", RFC 6083, January 2011. ๐Ÿ”’

[RFC6247] Eggert, L., "Moving the Undeployed TCP Extensions RFC 1072, RFC 1106, RFC 1110, RFC 1145, RFC 1146, RFC 1379, RFC 1644, and RFC 1693 to Historic Status", RFC 6247, May 2011. ๐Ÿ•ฐ๏ธ

Appendix A. Example Using One-to-One Style Sockets ๐ŸŽฌ

okay so this part is the demo and it's so iconic. ๐Ÿ˜ญ The following code is an implementation of a simple client that sends a number of messages marked for unordered delivery to an echo server making use of all outgoing streams. ๐Ÿซง The example shows how to use some features of one-to-one style IPv4 SCTP sockets, including:

  • Creating and connecting an SCTP socket.
  • Making a request to negotiate a number of outgoing streams.
  • Determining the negotiated number of outgoing streams.
  • Setting an adaptation layer indication.
  • Sending messages with a given payload protocol identifier on a particular stream using sctp_sendv().
<CODE BEGINS>
/*

   Copyright (c) 2011 IETF Trust and the persons identified
   as authors of the code.  All rights reserved.

   Redistribution and use in source and binary forms, with
   or without modification, is permitted pursuant to, and subject
   to the license terms contained in, the Simplified BSD License
   set forth in Section 4.c of the IETF Trust's Legal Provisions
   Relating to IETF Documents (http://trustee.ietf.org/license-info).

*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#define PORT 9
#define ADDR "127.0.0.1"
#define SIZE_OF_MESSAGE 1000
#define NUMBER_OF_MESSAGES 10
#define PPID 1234
int
main(void) {
  unsigned int i;
  int sd;
  struct sockaddr_in addr;
  char buffer[SIZE_OF_MESSAGE];
  struct iovec iov;
  struct sctp_status status;
  struct sctp_initmsg init;
  struct sctp_sndinfo info;
  struct sctp_setadaptation ind;
  socklen_t opt_len;

  /* Create a one-to-one style SCTP socket. */
  if ((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
    perror("socket");
    exit(1);
  }

  /* Prepare for requesting 2048 outgoing streams. */
  memset(&init, 0, sizeof(init));
  init.sinit_num_ostreams = 2048;
  if (setsockopt(sd, IPPROTO_SCTP, SCTP_INITMSG,
                 &init, (socklen_t)sizeof(init)) < 0) {
    perror("setsockopt");
    exit(1);
  }

  ind.ssb_adaptation_ind  = 0x01020304;
  if (setsockopt(sd, IPPROTO_SCTP, SCTP_ADAPTATION_LAYER,
                 &ind, (socklen_t)sizeof(ind)) < 0) {
    perror("setsockopt");
    exit(1);
  }

  /* Connect to the discard server. */
  memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SIN_LEN
  addr.sin_len         = sizeof(struct sockaddr_in);
#endif
  addr.sin_family      = AF_INET;
  addr.sin_port        = htons(PORT);
  addr.sin_addr.s_addr = inet_addr(ADDR);
  if (connect(sd,
              (const struct sockaddr *)&addr,
              sizeof(struct sockaddr_in)) < 0) {
    perror("connect");
    exit(1);
  }

  /* Get the actual number of outgoing streams. */
  memset(&status, 0, sizeof(status));
  opt_len = (socklen_t)sizeof(status);
  if (getsockopt(sd, IPPROTO_SCTP, SCTP_STATUS,
                 &status, &opt_len) < 0) {
    perror("getsockopt");
    exit(1);
  }

  memset(&info, 0, sizeof(info));
  info.snd_ppid = htonl(PPID);
  info.snd_flags = SCTP_UNORDERED;
  memset(buffer, 'A', SIZE_OF_MESSAGE);
  iov.iov_base = buffer;
  iov.iov_len = SIZE_OF_MESSAGE;
  for (i = 0; i <  NUMBER_OF_MESSAGES; i++) {
    info.snd_sid = i % status.sstat_outstrms;
    if (sctp_sendv(sd,
                   (const struct iovec *)&iov, 1,
                   NULL, 0,
                   &info, sizeof(info), SCTP_SENDV_SNDINFO,
                   0) < 0) {
      perror("sctp_sendv");
      exit(1);
    }
  }

  if (close(sd) < 0) {
    perror("close");
    exit(1);
  }
  return(0);
}
<CODE ENDS>

Appendix B. Example Using One-to-Many Style Sockets ๐Ÿฆ‹

okay so this is a simple discard server over SCTP ๐ŸŒŠ it's giving "receive everything, say nothing" energy ๐Ÿ’… it shows off some of the best bits of one-to-many style IPv6 SCTP sockets, like:

  • Opening and binding a socket.
  • Enabling notifications.
  • Handling notifications.
  • Configuring the auto-close timer.
  • Using sctp_recvv() to receive messages.

bestie, this server pairs perfectly with the client from Appendix A ๐Ÿซถ run them together and you'll see the whole conversation happen โœจ

<CODE BEGINS>

/*

   Copyright (c) 2011 IETF Trust and the persons identified
   as authors of the code.  All rights reserved.

   Redistribution and use in source and binary forms, with
   or without modification, is permitted pursuant to, and subject
   to the license terms contained in, the Simplified BSD License
   set forth in Section 4.c of the IETF Trust's Legal Provisions
   Relating to IETF Documents (http://trustee.ietf.org/license-info).

*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BUFFER_SIZE (1<<16)
#define PORT 9
#define ADDR "0.0.0.0"
#define TIMEOUT 5
static void
print_notification(void *buf)
{
  struct sctp_assoc_change *sac;
  struct sctp_paddr_change *spc;
  struct sctp_adaptation_event *sad;
  union sctp_notification *snp;
  char addrbuf[INET6_ADDRSTRLEN];
  const char *ap;
  struct sockaddr_in *sin;
  struct sockaddr_in6 *sin6;

  snp = buf;

  switch (snp->sn_header.sn_type) {
  case SCTP_ASSOC_CHANGE:
    sac = &snp->sn_assoc_change;
    printf("^^^ Association change: ");
    switch (sac->sac_state) {
    case SCTP_COMM_UP:
      printf("Communication up (streams (in/out)=(%u/%u)).\n",
             sac->sac_inbound_streams, sac->sac_outbound_streams);
      break;
    case SCTP_COMM_LOST:
      printf("Communication lost (error=%d).\n", sac->sac_error);
      break;
    case SCTP_RESTART:
      printf("Communication restarted (streams (in/out)=(%u/%u).\n",
             sac->sac_inbound_streams, sac->sac_outbound_streams);
      break;
    case SCTP_SHUTDOWN_COMP:
      printf("Communication completed.\n");
      break;
    case SCTP_CANT_STR_ASSOC:
      printf("Communication couldn't be started.\n");
      break;
    default:
      printf("Unknown state: %d.\n", sac->sac_state);
      break;
    }
    break;
  case SCTP_PEER_ADDR_CHANGE:
    spc = &snp->sn_paddr_change;
    if (spc->spc_aaddr.ss_family == AF_INET) {
      sin = (struct sockaddr_in *)&spc->spc_aaddr;
      ap = inet_ntop(AF_INET, &sin->sin_addr,
                     addrbuf, INET6_ADDRSTRLEN);
    } else {

      sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;
      ap = inet_ntop(AF_INET6, &sin6->sin6_addr,
                     addrbuf, INET6_ADDRSTRLEN);
    }
    printf("^^^ Peer Address change: %s ", ap);
    switch (spc->spc_state) {
    case SCTP_ADDR_AVAILABLE:
      printf("is available.\n");
      break;
    case SCTP_ADDR_UNREACHABLE:
      printf("is not available (error=%d).\n", spc->spc_error);
      break;
    case SCTP_ADDR_REMOVED:
      printf("was removed.\n");
      break;
    case SCTP_ADDR_ADDED:
      printf("was added.\n");
      break;
    case SCTP_ADDR_MADE_PRIM:
      printf("is primary.\n");
      break;
    default:
      printf("unknown state (%d).\n", spc->spc_state);
      break;
    }
    break;
  case SCTP_SHUTDOWN_EVENT:
    printf("^^^ Shutdown received.\n");
    break;
  case SCTP_ADAPTATION_INDICATION:
    sad = &snp->sn_adaptation_event;
    printf("^^^ Adaptation indication 0x%08x received.\n",
           sad->sai_adaptation_ind);
    break;
  default:
    printf("^^^ Unknown event of type: %u.\n",
           snp->sn_header.sn_type);
    break;
  };
}
int
main(void) {
  int sd, flags, timeout, on;
  ssize_t n;
  unsigned int i;
  union {
    struct sockaddr sa;
    struct sockaddr_in sin;
    struct sockaddr_in6 sin6;
  } addr;
  socklen_t fromlen, infolen;
  struct sctp_rcvinfo info;
  unsigned int infotype;
  struct iovec iov;
  char buffer[BUFFER_SIZE];
  struct sctp_event event;
  uint16_t event_types[] = {SCTP_ASSOC_CHANGE,
                            SCTP_PEER_ADDR_CHANGE,
                            SCTP_SHUTDOWN_EVENT,
                            SCTP_ADAPTATION_INDICATION};

  /* Create a one-to-many style SCTP socket. */
  if ((sd = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) {
    perror("socket");
    exit(1);
  }

  /* Enable the events of interest. */
  memset(&event, 0, sizeof(event));
  event.se_assoc_id = SCTP_FUTURE_ASSOC;
  event.se_on = 1;
  for (i = 0; i < sizeof(event_types)/sizeof(uint16_t); i++) {
    event.se_type = event_types[i];
    if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENT,
                   &event, sizeof(event)) < 0) {
      perror("setsockopt");
      exit(1);
    }
  }

  /* Configure auto-close timer. */
  timeout = TIMEOUT;
  if (setsockopt(sd, IPPROTO_SCTP, SCTP_AUTOCLOSE,
                 &timeout, sizeof(timeout)) < 0) {
    perror("setsockopt SCTP_AUTOCLOSE");
    exit(1);
  }
  /* Enable delivery of SCTP_RCVINFO. */
  on = 1;
  if (setsockopt(sd, IPPROTO_SCTP, SCTP_RECVRCVINFO,
                 &on, sizeof(on)) < 0) {
    perror("setsockopt SCTP_RECVRCVINFO");
    exit(1);
  }

  /* Bind the socket to all local addresses. */
  memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SIN6_LEN
  addr.sin6.sin6_len         = sizeof(addr.sin6);
#endif
  addr.sin6.sin6_family      = AF_INET6;
  addr.sin6.sin6_port        = htons(PORT);
  addr.sin6.sin6_addr        = in6addr_any;
  if (bind(sd, &addr.sa, sizeof(addr.sin6)) < 0) {
    perror("bind");
    exit(1);
  }
  /* Enable accepting associations. */
  if (listen(sd, 1) < 0) {
    perror("listen");
    exit(1);
  }

  for (;;) {
    flags = 0;
    memset(&addr, 0, sizeof(addr));
    fromlen = (socklen_t)sizeof(addr);
    memset(&info, 0, sizeof(info));
    infolen = (socklen_t)sizeof(info);
    infotype = 0;
    iov.iov_base = buffer;
    iov.iov_len = BUFFER_SIZE;

    n = sctp_recvv(sd, &iov, 1,
                   &addr.sa, &fromlen,
                   &info, &infolen, &infotype,
                   &flags);

    if (flags & MSG_NOTIFICATION) {
      print_notification(iov.iov_base);
    } else {
      char addrbuf[INET6_ADDRSTRLEN];
      const char *ap;
      in_port_t port;
      if (addr.sa.sa_family == AF_INET) {
             ap = inet_ntop(AF_INET, &addr.sin.sin_addr,
                            addrbuf, INET6_ADDRSTRLEN);
             port = ntohs(addr.sin.sin_port);
      } else {
             ap = inet_ntop(AF_INET6, &addr.sin6.sin6_addr,
                            addrbuf, INET6_ADDRSTRLEN);
             port = ntohs(addr.sin6.sin6_port);
      }
      printf("Message received from %s:%u: len=%d",
             ap, port, (int)n);
      switch (infotype) {
      case SCTP_RECVV_RCVINFO:
        printf(", sid=%u", info.rcv_sid);
        if (info.rcv_flags & SCTP_UNORDERED) {
          printf(", unordered");
        } else {
          printf(", ssn=%u", info.rcv_ssn);
        }
        printf(", tsn=%u", info.rcv_tsn);
        printf(", ppid=%u.\n", ntohl(info.rcv_ppid));
        break;
      case SCTP_RECVV_NOINFO:
      case SCTP_RECVV_NXTINFO:
      case SCTP_RECVV_RN:
        printf(".\n");
        break;
      default:
        printf(" unknown infotype.\n");
      }
    }
  }

  if (close(sd) < 0) {
    perror("close");
    exit(1);
  }

  return (0);
}

<CODE ENDS>


Authors' Addresses ๐ŸŒธ

Randall R. Stewart
Adara Networks
Chapin, SC  29036
USA

EMail: randall@lakerest.net


Michael Tuexen
Muenster University of Applied Sciences
Stegerwaldstr. 39
48565 Steinfurt
Germany

EMail: tuexen@fh-muenster.de


Kacheong Poon
Oracle Corporation

EMail: ka-cheong.poon@oracle.com


Peter Lei
Cisco Systems, Inc.
9501 Technology Blvd.
West Office Center
Rosemont, IL  60018
USA

EMail: peterlei@cisco.com


Vladislav Yasevich
HP
110 Spitrook Rd.
Nashua, NH  03062
USA

EMail: vladislav.yasevich@hp.com

and that's the whole squad, bestie ๐Ÿ’– the people who wrote this are a literal dream team of SCTP legends ๐Ÿ‘‘

๐Ÿ“ฆ where this page comes from