RFC 6458
Sockets API Extensions for the Stream Control Transmission Protocol (SCTP)
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 Notice
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 ๐
- 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 ๐
- 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 ๐
- 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 bysocket().addr:The address structure (struct sockaddr_infor an IPv4 address orstruct sockaddr_in6for 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 thisbind()process does not allow the SCTP endpoint to accept inbound SCTP association requests. Until alisten()system call, described below, is performed on the socket, the SCTP endpoint will promptly reject an inbound SCTPINITrequest with an SCTPABORT๐ซ
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: Ifbacklogis 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 themsghdrstructure 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 themsghdrstructure.
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_infor an IPv4 address orstruct sockaddr_in6for 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 bysocket().addr: The address structure (struct sockaddr_infor an IPv4 address orstruct sockaddr_in6for 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_infor an IPv4 address orstruct sockaddr_in6for an IPv6 address; see [RFC3493]) will contain the primary address of the peer endpoint.addrlen: On return,addrlenwill contain the size ofaddr.
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_infor an IPv4 address orstruct sockaddr_in6for 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:
- When sending, the
msg_namefield in themsghdris 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 anSCTP_SEND_FAILED_EVENTevent will be delivered to the application if send failure events are enabled. ๐ - 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 ofaddresshere. 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. TheSCTP_COMM_UPnotification confirms this number, and you must verify it, because it's negotiated with the remote endpoint ๐ค A default value of0means 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 of0means 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 SCTPMax.Init.Retransmitsvalue ๐ A default value of0means the endpoint's default value gets used. This is normally set to the system's defaultMax.Init.Retransmitvalue.sinit_max_init_timeo: This is the largest timeout, or retransmission timeout (RTO), in milliseconds, to use when attempting an INIT โฐ Normally,RTO.Maxcaps how much the RTO can double on each timeout. For the INIT message, this value may overrideRTO.Max. Still, it must not influenceRTO.Maxduring data transmission, since it's only there to bound the initial setup time ๐ธ A default value of0means the endpoint's default value. This is normally set to the system'sRTO.Maxvalue (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: Forrecvmsg(), the SCTP stack puts the message's stream number in here. Forsendmsg(), 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: Forrecvmsg(), 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 onerecvmsg()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 thesendto/sendmsgcall ๐ฆ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 ofUser Initiated Abortwith cause code12. The cause-specific info for this error cause is provided inmsg_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: Insendmsg(), this is an unsigned integer that gets passed to the remote end in each user message ๐ Inrecvmsg(), 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 thehtonl()computation.sinfo_context: This is an opaque 32-bit context datum used in thesendmsg()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 of0is 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 theSCTP_COMM_UPnotification ๐ฆ 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, soserinfo_next_stream,serinfo_next_aid,serinfo_next_length, andserinfo_next_ppidall 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, soserinfo_next_lengthhas the entire message size. if it's 0, thenserinfo_next_lengthonly 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 (checkserinfo_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 (checkserinfo_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 (checkserinfo_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 innext_flags๐serinfo_next_ppid: when this is valid (checkserinfo_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 fornxt_length, so read thenxt_lengthbit 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 thentohl()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 whenSCTP_COMPLETEis set innxt_flags๐nxt_assoc_id: the association handle of the next message. it holds the identifier for the association that was announced in theSCTP_COMM_UPnotification, 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. usingSCTP_PR_SCTP_NONEgives you a reliable transmission. whenSCTP_PR_SCTP_TTLis used, the PR-SCTP policy "timed reliability" from [RFC3758] kicks in, and the lifetime goes inpr_valueโณpr_value: what this means depends on the policy named inpr_policy. it gets ignored whenSCTP_PR_SCTP_NONEis specified. withSCTP_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 fieldsn_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 detailsSCTP_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 formatSCTP_SEND_FAILED_EVENT: the attached datagram couldn't be sent to the remote endpoint ๐ this structure includes the originalSCTP_SNDINFOthat was used to send this message, so it uses thesctp_sndinfoper Section 6.1.11SCTP_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.7SCTP_AUTHENTICATION_EVENT: tells a receiver that either an authentication error happened, or a new key was made active ๐ see Section 6.1.8SCTP_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 wholesctp_notificationstructure, including thesn_type,sn_flags, andsn_lengthfields ๐
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 toSCTP_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, anSCTP_COMM_LOSTcomes with a series ofSCTP_SEND_FAILED_EVENTevents, 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), anSCTP_CANT_STR_ASSOCcomes with a series ofSCTP_SEND_FAILED_EVENTevents, 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_streamsandsac_inbound_streams: The maximum number of streams allowed in each direction is available insac_outbound_streamsandsac_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: Ifsac_stateisSCTP_COMM_LOSTand 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] ๐งพ. Ifsac_stateisSCTP_COMM_UPorSCTP_RESTART,sac_infomay contain an array ofuint8_tdescribing 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 toSCTP_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 toSCTP_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 ofsre_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 toSCTP_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 inssf_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, thessf_info.sinfo_flagsfield indicates whether the complete message or only part of the message is returned inssf_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: Thessf_assoc_idfield,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 thessf_datafield ๐. Use thessf_info.sinfo_flagsfield 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 toSCTP_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 besizeof(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 toSCTP_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 besizeof(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 toSCTP_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 besizeof(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 toSCTP_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 besizeof(struct sctp_authkey_event)๐auth_keynumber: this holds the key number for the affected key in the event. what it means depends onauth_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 โจ theauth_keynumberfield holds the user-specified key numberSCTP_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 inauth_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 toSCTP_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 besizeof(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 toSCTP_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, thessfe_info.sinfo_flagsfield 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 thessfe_assoc_idfield (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 ignoredssfe_data: the undelivered message, or part of it, shows up in the ssf_data field ๐ usessf_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 ofSCTP_SNDRCVinformation on a per-message basis ๐. The application will need to use therecvmsg()interface so it can receive the event information contained in themsg_controlfield. 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: These_assoc_idfield is ignored for one-to-one style sockets. For one-to-many style sockets, this field can be a particular association identifier orSCTP_{FUTURE|CURRENT|ALL}_ASSOC๐.se_type: These_typefield can be filled with any value that would show up in the respectivesn_typefield (in thesctp_tlvstructure of the notification) ๐.se_on: These_onfield 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 thefromaddress.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 toIPPROTO_SCTPfor 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_RTOINFOSCTP_ASSOCINFOSCTP_PRIMARY_ADDRSCTP_PEER_ADDR_PARAMSSCTP_DEFAULT_SEND_PARAMSCTP_MAX_SEGSCTP_AUTH_ACTIVE_KEYSCTP_DELAYED_SACKSCTP_MAX_BURSTSCTP_CONTEXTSCTP_EVENTSCTP_DEFAULT_SNDINFOSCTP_DEFAULT_PRINFOSCTP_STATUSSCTP_GET_PEER_ADDR_INFOSCTP_PEER_AUTH_CHUNKSSCTP_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 orSCTP_FUTURE_ASSOC. it's an error to useSCTP_{CURRENT|ALL}_ASSOChere ๐srto_initial: the initial RTO value ๐ธsrto_maxandsrto_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 orSCTP_FUTURE_ASSOC. it's an error to useSCTP_{CURRENT|ALL}_ASSOChere ๐ฆ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 specialsctp_assoc_tSCTP_{FUTURE|ALL|CURRENT}_ASSOCvalues 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_ENABLEandSPP_HB_DISABLEare 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_ENABLEandSPP_PMTUD_DISABLEare 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 thespp_ipv6_flowlabelfield. on retrieval, this flag comes back set to show thatspp_ipv6_flowlabelhas a valid value. if you gave a specific destination address (inspp_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 thespp_dscpfield. on retrieval, this flag comes back set to show thatspp_dscphas a valid value. a specific destination address (inspp_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_SHA1SCTP_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 specialSCTP_{FUTURE|CURRENT|ALL}_ASSOChere. 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 identifier0defaults 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 specialSCTP_{FUTURE|CURRENT|ALL}_ASSOCcan 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 is2, and setting it to1disables 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 specialSCTP_{FUTURE|CURRENT|ALL}_ASSOCcan be used here too ๐assoc_value: this holds the maximum burst. setting it to0disables 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 specialSCTP_{FUTURE|CURRENT|ALL}_ASSOCcan 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_idcan be a particular association identifier orSCTP_{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 specialSCTP_{FUTURE|CURRENT|ALL}_ASSOCcannot be used.sstat_state: This contains the association's current state, i.e., one of the following values:SCTP_CLOSEDSCTP_BOUNDSCTP_LISTENSCTP_COOKIE_WAITSCTP_COOKIE_ECHOEDSCTP_ESTABLISHEDSCTP_SHUTDOWN_PENDINGSCTP_SHUTDOWN_SENTSCTP_SHUTDOWN_RECEIVEDSCTP_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 isSCTP_INACTIVEand 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 arraygauth_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,ENOBUFSis 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 orSCTP_FUTURE_ASSOC. It is an error to useSCTP_{CURRENT|ALL}_ASSOCingauth_assoc_id.gauth_number_of_chunks: This parameter gives the number of elements in the arraygauth_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,ENOBUFSis 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 specialSCTP_{FUTURE|CURRENT|ALL}_ASSOCvalues. 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 identifier0defaults to a null key ๐ซงsca_keylength: the length of the arraysca_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 specialSCTP_{FUTURE|CURRENT|ALL}_ASSOCvalues 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 identifier0gets 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 specialSCTP_{FUTURE|CURRENT|ALL}_ASSOCvalues 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 identifier0gets 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_ADDRSCTP_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 assinfo_ppid(see Section 5.3.2).flags: The same assinfo_flags(see Section 5.3.2).stream_no: The same assinfo_stream(see Section 5.3.2).timetolive: The same assinfo_timetolive(see Section 5.3.2).context: The same assinfo_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 ansctp_sndrcvinfostructure 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, themsg_flagsvalue will be different than what was sent in to the call. If implemented via arecvmsg()call, themsg_flagsparameter should only contain the value of the flags from therecvmsg()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 ansctp_sndrcvinfostructure used as described in Section 5.3.2 for asendmsg()call.flags: The same flags as used by thesendmsg()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 ansctp_sndrcvinfostructure used as described in Section 5.3.2 for asendmsg()call.flags: The same flags as used by thesendmsg()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 iniov.addrs: an array of addresses used to set up an association, or one address used to send the message. You passNULLif you neither want to set up an association nor send to a specific address.addrcnt: the number of addresses in theaddrsarray.info: a pointer to the buffer holding the attribute for the message. Theinfo_typeparameter says what type it is.infolen: the length ofinfo, in bytes.infotype: says what kind of info is ininfo. The current values are:SCTP_SENDV_NOINFO: no info is provided.infois a NULL pointer andinfolenis 0.SCTP_SENDV_SNDINFO:infopoints to astruct sctp_sndinfo.SCTP_SENDV_PRINFO:infopoints to astruct sctp_prinfo.SCTP_SENDV_AUTHINFO:infopoints to astruct sctp_authinfo.SCTP_SENDV_SPA:infopoints to astruct sctp_sendv_spa.
flags: the same flags you'd use with thesendmsg()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 iniov.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 offrom.info: a pointer to the buffer that holds the attributes of the received message. The structure type ofinfois determined by theinfo_typeparameter.infolen: an in/out parameter describing the size of theinfobuffer.infotype: on return,*info_typeis set to the type of theinfobuffer. The current values are:SCTP_RECVV_NOINFO: if neitherSCTP_RECVRCVINFOnorSCTP_RECVNXTINFOis enabled, no attribute is returned. If onlySCTP_RECVNXTINFOis enabled but there is no next message in the buffer, no attribute is returned either. In both cases,*info_typeis set toSCTP_RECVV_NOINFO.SCTP_RECVV_RCVINFO: the type ofinfoisstruct sctp_rcvinfo, and the attribute relates to the received message.SCTP_RECVV_NXTINFO: the type ofinfoisstruct sctp_nxtinfo, and the attribute relates to the next message in the receive buffer. This is the case when onlySCTP_RECVNXTINFOis enabled and there is a next message in the buffer.SCTP_RECVV_RN: the type ofinfoisstruct sctp_recvv_rn. Therecvv_rcvinfofield is the attribute of the received message, and therecvv_nxtinfofield is the attribute of the next message in the buffer. This is the case when bothSCTP_RECVRCVINFOandSCTP_RECVNXTINFOare 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, theflagsvalue will be different from what was sent in. If it's implemented via arecvmsg()call,flagsshould only contain the value of the flags from therecvmsg()call when callingsctp_recvv(), and on return it has the value frommsg_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
- ietf: rfc6458 2011-12
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms