9. New Functions
Depending on the system, the following interface can be implemented as a system call or library function.9.1. sctp_bindx()
This function allows the user to 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 passed must be IPv4 addresses. If the sd is an IPv6 socket, the addresses passed can either be IPv4 or IPv6 addresses. A single address may 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 contained in its appropriate structure. For an IPv6 socket, an array of sockaddr_in6 is used. For an IPv4 socket, an array of sockaddr_in is used. The caller specifies the number of addresses in the array with addrcnt. Note that the wildcard addresses cannot be used in combination with non-wildcard addresses on a socket with this function; doing so will result in an error. On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns -1 and sets errno to the appropriate error code. For SCTP, the port given in each socket address must be the same, or sctp_bindx() will fail, setting errno to EINVAL. The flags parameter is formed from the bitwise OR of zero or more of the following currently defined flags: o SCTP_BINDX_ADD_ADDR o SCTP_BINDX_REM_ADDR SCTP_BINDX_ADD_ADDR directs SCTP to add the given addresses to the socket (i.e., endpoint), and SCTP_BINDX_REM_ADDR directs SCTP to remove the given addresses from the socket. The two flags are mutually exclusive; if both are given, sctp_bindx() will fail with EINVAL. A caller may not remove all addresses from a socket; sctp_bindx() will reject such an attempt with EINVAL. An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate additional addresses with an endpoint after calling bind(). Or, an application can use sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses with which a listening socket is associated, so that no new association accepted will be associated with these addresses. If the
endpoint supports dynamic address reconfiguration, an SCTP_BINDX_REM_ADDR or SCTP_BINDX_ADD_ADDR may cause an endpoint to send the appropriate message to its peers to change the peers' address lists. Adding and removing addresses from established associations is an optional functionality. Implementations that do not support this functionality 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 will choose 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. Therefore, the binding will either succeed on all addresses or fail on all addresses. If multiple addresses are provided and the sctp_bindx() call fails, there is no indication of which address is responsible for the failure. The only way to identify the specific error indication 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 wish to branch off the association into a separate socket/file descriptor. This is particularly desirable when, for instance, the application wishes to have a number of sporadic message senders/receivers remain under the original one-to-many style socket but branch off these associations carrying high-volume data traffic into their own separate socket descriptors. The application uses the sctp_peeloff() call to branch off an association into a separate socket. (Note that the semantics are somewhat changed from the traditional one-to-one style accept() call.) Note also that the new socket is a one-to-one style socket. Thus, it will be confined to operations allowed for a one-to-one style socket. 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 specified identifier of the association that is to be branched off to a separate file descriptor. (Note that in a traditional one-to-one style accept() call, this would be an out parameter, but for the one-to-many style call, this is an in parameter.) The function returns a non-negative file descriptor representing the branched-off association, or -1 if an error occurred. The variable errno is then set appropriately.9.3. sctp_getpaddrs()
sctp_getpaddrs() returns all peer addresses in an association. The function prototype is int sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **addrs); On return, addrs will point 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. 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.
9.4. sctp_freepaddrs()
sctp_freepaddrs() frees all resources allocated by sctp_getpaddrs(). The function prototype is void sctp_freepaddrs(struct sockaddr *addrs); and addrs is the array of peer addresses returned by sctp_getpaddrs().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 will point 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. 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. 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 function prototype is void sctp_freeladdrs(struct sockaddr *addrs); and addrs is the array of local addresses returned by sctp_getladdrs().9.7. sctp_sendmsg() - DEPRECATED
This function is deprecated; sctp_sendv() (see Section 9.12) 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. The function prototype is ssize_t sctp_sendmsg(int sd, const void *msg, size_t len, const struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream_no, uint32_t timetolive, uint32_t context); and the arguments are sd: The socket descriptor. msg: The message to be sent. len: The length of the message. to: The destination address of the message. tolen: The length of the destination address. ppid: The same as sinfo_ppid (see Section 5.3.2). flags: The same as sinfo_flags (see Section 5.3.2).
stream_no: The same as sinfo_stream (see Section 5.3.2). timetolive: The same as sinfo_timetolive (see Section 5.3.2). context: The same as sinfo_context (see Section 5.3.2). The call returns the number of characters sent, or -1 if an error occurred. The variable errno is then set appropriately. Sending a message using sctp_sendmsg() is atomic (unless explicit EOR marking is enabled on the socket specified by sd). Using sctp_sendmsg() on a non-connected one-to-one style socket for implicit connection setup may or may not work, depending on the SCTP implementation.9.8. sctp_recvmsg() - DEPRECATED
This function is deprecated; 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. The function prototype is ssize_t sctp_recvmsg(int sd, void *msg, size_t len, struct sockaddr *from, socklen_t *fromlen struct sctp_sndrcvinfo *sinfo int *msg_flags); and the arguments are sd: The socket descriptor. msg: The message buffer to be filled. len: The length of the message buffer.
from: A pointer to an address to be filled with the address of the sender of this message. fromlen: An in/out parameter describing the from length. sinfo: A pointer to an sctp_sndrcvinfo structure to be filled upon receipt of the message. msg_flags: A pointer to an integer to be filled with any message flags (e.g., MSG_NOTIFICATION). Note that this field is an in-out field. Options for the receive may also be passed into the value (e.g., MSG_PEEK). On return from the call, the msg_flags value will be different than what was sent in to the call. If implemented via a recvmsg() call, the msg_flags parameter should only contain the value of the flags from the recvmsg() call. 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. 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. 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
This function is deprecated; sctp_sendv() should be used instead. An implementation may provide another alternative function or system call to assist an application with the sending of data without the use of the cmsghdr structures. The function prototype is ssize_t sctp_send(int sd, const void *msg, size_t len, const struct sctp_sndrcvinfo *sinfo, int flags); and the arguments are sd: The socket descriptor. msg: The message to be sent. len: The length of the message. sinfo: A pointer to an sctp_sndrcvinfo structure used as described in Section 5.3.2 for a sendmsg() call. flags: The same flags as used by the sendmsg() call flags (e.g., MSG_DONTROUTE). The call returns the number of bytes sent, or -1 if an error occurred. The variable errno is then set appropriately. This function call may also be used to terminate an association using an association identifier by setting the sinfo.sinfo_flags to SCTP_EOF and the sinfo.sinfo_assoc_id to the association that needs to be terminated. In such a case, len can be zero.
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
This function is deprecated; 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). The function prototype is ssize_t sctp_sendx(int sd, const void *msg, size_t len, struct sockaddr *addrs, int addrcnt, struct sctp_sndrcvinfo *sinfo, int flags); and the arguments are sd: The socket descriptor. msg: The message to be sent. len: The length of the message. addrs: An array of addresses. addrcnt: The number of addresses in the array. sinfo: A pointer to an sctp_sndrcvinfo structure used as described in Section 5.3.2 for a sendmsg() call. flags: The same flags as used by the sendmsg() call flags (e.g., MSG_DONTROUTE). The call returns the number of bytes sent, or -1 if an error occurred. The variable errno is then set appropriately.
Note that in the case of implicit connection setup, on return from this call, the sinfo_assoc_id field of the sinfo structure will contain the new association identifier. 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()
The function prototype is 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); The function sctp_sendv() provides an extensible way for an application to communicate different send attributes to the SCTP stack when sending a message. An implementation may provide sctp_sendv() as a library function or a system call. This document defines three types of attributes that can be used to describe a message to be sent. They are struct sctp_sndinfo (Section 5.3.4), struct sctp_prinfo (Section 5.3.7), and struct sctp_authinfo (Section 5.3.8). The following structure, sctp_sendv_spa, is defined to be used when more than one of the above attributes are needed to describe a message to be sent. 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 holds a bitwise OR of SCTP_SEND_SNDINFO_VALID, SCTP_SEND_PRINFO_VALID, and SCTP_SEND_AUTHINFO_VALID indicating if the sendv_sndinfo/sendv_prinfo/sendv_authinfo fields contain valid information. In future, when new send attributes are needed, new structures can be defined. But those new structures do not need to be based on any of the above defined structures. The function takes the following arguments: sd: The socket descriptor. iov: The gather buffer. The data in the buffer is treated as a single user message. iovcnt: The number of elements in iov. addrs: An array of addresses to be used to set up an association or a single address to be used to send the message. NULL is passed in if the caller neither wants to set up an association nor wants to send the message to a specific address. addrcnt: The number of addresses in the addrs array. info: A pointer to the buffer containing the attribute associated with the message to be sent. The type is indicated by the info_type parameter. infolen: The length of info, in bytes. infotype: Identifies the type of the information provided in info. The current defined values are as follows: SCTP_SENDV_NOINFO: No information is provided. The parameter info is a NULL pointer, and infolen is 0. SCTP_SENDV_SNDINFO: The parameter info is pointing to a struct sctp_sndinfo. SCTP_SENDV_PRINFO: The parameter info is pointing to a struct sctp_prinfo. SCTP_SENDV_AUTHINFO: The parameter info is pointing to a struct sctp_authinfo. SCTP_SENDV_SPA: The parameter info is pointing to a struct sctp_sendv_spa.
flags: The same flags as used by the sendmsg() call flags (e.g., MSG_DONTROUTE). The call returns the number of bytes sent, or -1 if an error occurred. The variable errno is then set appropriately. A note on the one-to-many style socket: The struct sctp_sndinfo attribute must always be used in order to specify the association on which the message is to be sent. The only case where it is not needed is when this call is used to set up a new association. The caller provides a list of addresses in the addrs parameter to set up an association. This function will behave like calling sctp_connectx() (see Section 9.9), first using the list of addresses and then calling sendmsg() with the given message and attributes. For a one-to-many style socket, if the struct sctp_sndinfo attribute is provided, the snd_assoc_id field must be 0. When this function returns, the snd_assoc_id field will contain the association identifier of the newly established association. Note that the struct sctp_sndinfo attribute is not required to set up an association for a one-to-many style socket. If this attribute is not provided, the caller can enable the SCTP_ASSOC_CHANGE notification and use the SCTP_COMM_UP message to find out the association identifier. If the caller wants to send the message to a specific peer address (hence overriding the primary address), it can provide the specific address in the addrs parameter and provide a struct sctp_sndinfo attribute with the field snd_flags set to SCTP_ADDR_OVER. This function call may also be used to terminate an association. The caller provides an sctp_sndinfo attribute with the snd_flags set to SCTP_EOF. In this case, len would be zero. Sending a message using sctp_sendv() is atomic unless explicit EOR marking is enabled on the socket specified by sd.
9.13. sctp_recvv()
The function prototype is 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); The function sctp_recvv() provides an extensible way for the SCTP stack to pass up different SCTP attributes associated with a received message to an application. An implementation may provide sctp_recvv() as a library function or as a system call. This document defines two types of attributes that can be returned by this call: the attribute of the received message and the attribute of the next message in the receive buffer. The caller enables the SCTP_RECVRCVINFO and SCTP_RECVNXTINFO socket options, respectively, to receive these attributes. Attributes of the received message are returned in struct sctp_rcvinfo (Section 5.3.5), and attributes of the next message are returned in struct sctp_nxtinfo (Section 5.3.6). If both options are enabled, both attributes are returned using the following structure. struct sctp_recvv_rn { struct sctp_rcvinfo recvv_rcvinfo; struct sctp_nxtinfo recvv_nxtinfo; }; In future, new structures can be defined to hold new types of attributes. The new structures do not need to be based on struct sctp_recvv_rn or struct sctp_rcvinfo. This function takes the following arguments: sd: The socket descriptor. iov: The scatter buffer. Only one user message is returned in this buffer. iovlen: The number of elements in iov.
from: A pointer to an address to be filled with the sender of the received message's address. fromlen: An in/out parameter describing the from length. info: A pointer to the buffer to hold the attributes of the received message. The structure type of info is determined by the info_type parameter. infolen: An in/out parameter describing the size of the info buffer. infotype: On return, *info_type is set to the type of the info buffer. The current defined values are as follows: SCTP_RECVV_NOINFO: If both SCTP_RECVRCVINFO and SCTP_RECVNXTINFO options are not enabled, no attribute will be returned. If only the SCTP_RECVNXTINFO option is enabled but there is no next message in the buffer, no attribute will be returned. In these cases, *info_type will be set to SCTP_RECVV_NOINFO. SCTP_RECVV_RCVINFO: The type of info is struct sctp_rcvinfo, and the attribute relates to the received message. SCTP_RECVV_NXTINFO: The type of info is struct sctp_nxtinfo, and the attribute relates to the next message in the receive buffer. This is the case when only the SCTP_RECVNXTINFO option is enabled and there is a next message in the buffer. SCTP_RECVV_RN: The type of info is struct sctp_recvv_rn. The recvv_rcvinfo field is the attribute of the received message, and the recvv_nxtinfo field is the attribute of the next message in the buffer. This is the case when both SCTP_RECVRCVINFO and SCTP_RECVNXTINFO options are enabled and there is a next message in the receive buffer. 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 parameter. Options for the receive may also be passed into the value (e.g., MSG_PEEK). On return from the call, the flags value will be different than what was sent in to the call. If implemented via a recvmsg() call, the flags should only contain the value of the flags from the recvmsg() call when calling sctp_recvv(), and on return it has the value from msg_flags. The call returns the number of bytes received, or -1 if an error occurred. The variable errno is then 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: o 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. 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. 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.
12. References
12.1. Normative References
[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
[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
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 o Creating and connecting an SCTP socket. o Making a request to negotiate a number of outgoing streams. o Determining the negotiated number of outgoing streams. o Setting an adaptation layer indication. o 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
The following code is a simple implementation of a discard server over SCTP. The example shows how to use some features of one-to-many style IPv6 SCTP sockets, including o Opening and binding of a socket. o Enabling notifications. o Handling notifications. o Configuring the auto-close timer. o Using sctp_recvv() to receive messages. Please note that this server can be used in combination with the client described in Appendix A. <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