aboutsummaryrefslogtreecommitdiff
path: root/lib/libc/net/sctp_sys_calls.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/net/sctp_sys_calls.c')
-rw-r--r--lib/libc/net/sctp_sys_calls.c1195
1 files changed, 1195 insertions, 0 deletions
diff --git a/lib/libc/net/sctp_sys_calls.c b/lib/libc/net/sctp_sys_calls.c
new file mode 100644
index 000000000000..5cb26266c888
--- /dev/null
+++ b/lib/libc/net/sctp_sys_calls.c
@@ -0,0 +1,1195 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
+ * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * a) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * b) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * c) Neither the name of Cisco Systems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/syscall.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/sctp_uio.h>
+#include <netinet/sctp.h>
+
+#ifndef IN6_IS_ADDR_V4MAPPED
+#define IN6_IS_ADDR_V4MAPPED(a) \
+ ((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \
+ (*(const uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \
+ (*(const uint32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)))
+#endif
+
+#define SCTP_CONTROL_VEC_SIZE_RCV 16384
+
+
+static void
+in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+ bzero(sin, sizeof(*sin));
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_port = sin6->sin6_port;
+ sin->sin_addr.s_addr = sin6->sin6_addr.__u6_addr.__u6_addr32[3];
+}
+
+int
+sctp_getaddrlen(sa_family_t family)
+{
+ int ret, sd;
+ socklen_t siz;
+ struct sctp_assoc_value av;
+
+ av.assoc_value = family;
+ siz = sizeof(av);
+#if defined(AF_INET)
+ sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
+#elif defined(AF_INET6)
+ sd = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP);
+#else
+ sd = -1;
+#endif
+ if (sd == -1) {
+ return (-1);
+ }
+ ret = getsockopt(sd, IPPROTO_SCTP, SCTP_GET_ADDR_LEN, &av, &siz);
+ close(sd);
+ if (ret == 0) {
+ return ((int)av.assoc_value);
+ } else {
+ return (-1);
+ }
+}
+
+int
+sctp_connectx(int sd, const struct sockaddr *addrs, int addrcnt,
+ sctp_assoc_t *id)
+{
+ char *buf;
+ int i, ret, *aa;
+ char *cpto;
+ const struct sockaddr *at;
+ size_t len;
+
+ /* validate the address count and list */
+ if ((addrs == NULL) || (addrcnt <= 0)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((buf = malloc(sizeof(int) + (size_t)addrcnt * sizeof(struct sockaddr_in6))) == NULL) {
+ errno = E2BIG;
+ return (-1);
+ }
+ len = sizeof(int);
+ at = addrs;
+ cpto = buf + sizeof(int);
+ /* validate all the addresses and get the size */
+ for (i = 0; i < addrcnt; i++) {
+ switch (at->sa_family) {
+ case AF_INET:
+ if (at->sa_len != sizeof(struct sockaddr_in)) {
+ free(buf);
+ errno = EINVAL;
+ return (-1);
+ }
+ memcpy(cpto, at, sizeof(struct sockaddr_in));
+ cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
+ len += sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ if (at->sa_len != sizeof(struct sockaddr_in6)) {
+ free(buf);
+ errno = EINVAL;
+ return (-1);
+ }
+ if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) {
+ in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at);
+ cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
+ len += sizeof(struct sockaddr_in);
+ } else {
+ memcpy(cpto, at, sizeof(struct sockaddr_in6));
+ cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in6));
+ len += sizeof(struct sockaddr_in6);
+ }
+ break;
+ default:
+ free(buf);
+ errno = EINVAL;
+ return (-1);
+ }
+ at = (struct sockaddr *)((caddr_t)at + at->sa_len);
+ }
+ aa = (int *)buf;
+ *aa = addrcnt;
+ ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf,
+ (socklen_t)len);
+ if ((ret == 0) && (id != NULL)) {
+ *id = *(sctp_assoc_t *)buf;
+ }
+ free(buf);
+ return (ret);
+}
+
+int
+sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
+{
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int i;
+ uint16_t sport;
+ bool fix_port;
+
+ /* validate the flags */
+ if ((flags != SCTP_BINDX_ADD_ADDR) &&
+ (flags != SCTP_BINDX_REM_ADDR)) {
+ errno = EFAULT;
+ return (-1);
+ }
+ /* validate the address count and list */
+ if ((addrcnt <= 0) || (addrs == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ sport = 0;
+ fix_port = false;
+ /* First pre-screen the addresses */
+ sa = addrs;
+ for (i = 0; i < addrcnt; i++) {
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (sa->sa_len != sizeof(struct sockaddr_in)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ sin = (struct sockaddr_in *)sa;
+ if (sin->sin_port) {
+ /* non-zero port, check or save */
+ if (sport) {
+ /* Check against our port */
+ if (sport != sin->sin_port) {
+ errno = EINVAL;
+ return (-1);
+ }
+ } else {
+ /* save off the port */
+ sport = sin->sin_port;
+ fix_port = (i > 0);
+ }
+ }
+ break;
+ case AF_INET6:
+ if (sa->sa_len != sizeof(struct sockaddr_in6)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ sin6 = (struct sockaddr_in6 *)sa;
+ if (sin6->sin6_port) {
+ /* non-zero port, check or save */
+ if (sport) {
+ /* Check against our port */
+ if (sport != sin6->sin6_port) {
+ errno = EINVAL;
+ return (-1);
+ }
+ } else {
+ /* save off the port */
+ sport = sin6->sin6_port;
+ fix_port = (i > 0);
+ }
+ }
+ break;
+ default:
+ /* Invalid address family specified. */
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
+ }
+ sa = addrs;
+ for (i = 0; i < addrcnt; i++) {
+ /*
+ * Now, if there was a port mentioned, assure that the first
+ * address has that port to make sure it fails or succeeds
+ * correctly.
+ */
+ if (fix_port) {
+ switch (sa->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)sa)->sin_port = sport;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)sa)->sin6_port = sport;
+ break;
+ }
+ fix_port = false;
+ }
+ if (setsockopt(sd, IPPROTO_SCTP, flags, sa, sa->sa_len) != 0) {
+ return (-1);
+ }
+ sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
+ }
+ return (0);
+}
+
+int
+sctp_opt_info(int sd, sctp_assoc_t id, int opt, void *arg, socklen_t *size)
+{
+ if (arg == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((id == SCTP_CURRENT_ASSOC) ||
+ (id == SCTP_ALL_ASSOC)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ switch (opt) {
+ case SCTP_RTOINFO:
+ ((struct sctp_rtoinfo *)arg)->srto_assoc_id = id;
+ break;
+ case SCTP_ASSOCINFO:
+ ((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
+ break;
+ case SCTP_DEFAULT_SEND_PARAM:
+ ((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
+ break;
+ case SCTP_PRIMARY_ADDR:
+ ((struct sctp_setprim *)arg)->ssp_assoc_id = id;
+ break;
+ case SCTP_PEER_ADDR_PARAMS:
+ ((struct sctp_paddrparams *)arg)->spp_assoc_id = id;
+ break;
+ case SCTP_MAXSEG:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_AUTH_KEY:
+ ((struct sctp_authkey *)arg)->sca_assoc_id = id;
+ break;
+ case SCTP_AUTH_ACTIVE_KEY:
+ ((struct sctp_authkeyid *)arg)->scact_assoc_id = id;
+ break;
+ case SCTP_DELAYED_SACK:
+ ((struct sctp_sack_info *)arg)->sack_assoc_id = id;
+ break;
+ case SCTP_CONTEXT:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_STATUS:
+ ((struct sctp_status *)arg)->sstat_assoc_id = id;
+ break;
+ case SCTP_GET_PEER_ADDR_INFO:
+ ((struct sctp_paddrinfo *)arg)->spinfo_assoc_id = id;
+ break;
+ case SCTP_PEER_AUTH_CHUNKS:
+ ((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
+ break;
+ case SCTP_LOCAL_AUTH_CHUNKS:
+ ((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
+ break;
+ case SCTP_TIMEOUTS:
+ ((struct sctp_timeouts *)arg)->stimo_assoc_id = id;
+ break;
+ case SCTP_EVENT:
+ ((struct sctp_event *)arg)->se_assoc_id = id;
+ break;
+ case SCTP_DEFAULT_SNDINFO:
+ ((struct sctp_sndinfo *)arg)->snd_assoc_id = id;
+ break;
+ case SCTP_DEFAULT_PRINFO:
+ ((struct sctp_default_prinfo *)arg)->pr_assoc_id = id;
+ break;
+ case SCTP_PEER_ADDR_THLDS:
+ ((struct sctp_paddrthlds *)arg)->spt_assoc_id = id;
+ break;
+ case SCTP_REMOTE_UDP_ENCAPS_PORT:
+ ((struct sctp_udpencaps *)arg)->sue_assoc_id = id;
+ break;
+ case SCTP_ECN_SUPPORTED:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_PR_SUPPORTED:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_AUTH_SUPPORTED:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_ASCONF_SUPPORTED:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_RECONFIG_SUPPORTED:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_NRSACK_SUPPORTED:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_PKTDROP_SUPPORTED:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_MAX_BURST:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_ENABLE_STREAM_RESET:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_PR_STREAM_STATUS:
+ ((struct sctp_prstatus *)arg)->sprstat_assoc_id = id;
+ break;
+ case SCTP_PR_ASSOC_STATUS:
+ ((struct sctp_prstatus *)arg)->sprstat_assoc_id = id;
+ break;
+ case SCTP_MAX_CWND:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ default:
+ break;
+ }
+ return (getsockopt(sd, IPPROTO_SCTP, opt, arg, size));
+}
+
+int
+sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
+{
+ struct sctp_getaddresses *addrs;
+ struct sockaddr *sa;
+ caddr_t lim;
+ socklen_t opt_len;
+ uint32_t size_of_addresses;
+ int cnt;
+
+ if (raddrs == NULL) {
+ errno = EFAULT;
+ return (-1);
+ }
+ /* When calling getsockopt(), the value contains the assoc_id. */
+ size_of_addresses = (uint32_t)id;
+ opt_len = (socklen_t)sizeof(uint32_t);
+ if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_REMOTE_ADDR_SIZE,
+ &size_of_addresses, &opt_len) != 0) {
+ if (errno == ENOENT) {
+ return (0);
+ } else {
+ return (-1);
+ }
+ }
+ opt_len = (socklen_t)((size_t)size_of_addresses + sizeof(struct sctp_getaddresses));
+ addrs = calloc(1, (size_t)opt_len);
+ if (addrs == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ addrs->sget_assoc_id = id;
+ /* Now lets get the array of addresses */
+ if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_PEER_ADDRESSES,
+ addrs, &opt_len) != 0) {
+ free(addrs);
+ return (-1);
+ }
+ *raddrs = &addrs->addr[0].sa;
+ cnt = 0;
+ sa = &addrs->addr[0].sa;
+ lim = (caddr_t)addrs + opt_len;
+ while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
+ sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
+ cnt++;
+ }
+ return (cnt);
+}
+
+void
+sctp_freepaddrs(struct sockaddr *addrs)
+{
+ void *fr_addr;
+
+ /* Take away the hidden association id */
+ fr_addr = (void *)((caddr_t)addrs - offsetof(struct sctp_getaddresses, addr));
+ /* Now free it */
+ free(fr_addr);
+}
+
+int
+sctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
+{
+ struct sctp_getaddresses *addrs;
+ struct sockaddr *sa;
+ caddr_t lim;
+ socklen_t opt_len;
+ uint32_t size_of_addresses;
+ int cnt;
+
+ if (raddrs == NULL) {
+ errno = EFAULT;
+ return (-1);
+ }
+ size_of_addresses = 0;
+ opt_len = (socklen_t)sizeof(uint32_t);
+ if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDR_SIZE,
+ &size_of_addresses, &opt_len) != 0) {
+ return (-1);
+ }
+ opt_len = (socklen_t)((size_t)size_of_addresses + sizeof(struct sctp_getaddresses));
+ addrs = calloc(1, (size_t)opt_len);
+ if (addrs == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ addrs->sget_assoc_id = id;
+ /* Now lets get the array of addresses */
+ if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDRESSES, addrs,
+ &opt_len) != 0) {
+ free(addrs);
+ return (-1);
+ }
+ if (size_of_addresses == 0) {
+ free(addrs);
+ return (0);
+ }
+ *raddrs = &addrs->addr[0].sa;
+ cnt = 0;
+ sa = &addrs->addr[0].sa;
+ lim = (caddr_t)addrs + opt_len;
+ while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
+ sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
+ cnt++;
+ }
+ return (cnt);
+}
+
+void
+sctp_freeladdrs(struct sockaddr *addrs)
+{
+ void *fr_addr;
+
+ /* Take away the hidden association id */
+ fr_addr = (void *)((caddr_t)addrs - offsetof(struct sctp_getaddresses, addr));
+ /* Now free it */
+ free(fr_addr);
+}
+
+ssize_t
+sctp_sendmsg(int s,
+ const void *data,
+ 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)
+{
+#ifdef SYS_sctp_generic_sendmsg
+ struct sctp_sndrcvinfo sinfo;
+
+ memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
+ sinfo.sinfo_ppid = ppid;
+ sinfo.sinfo_flags = flags;
+ sinfo.sinfo_stream = stream_no;
+ sinfo.sinfo_timetolive = timetolive;
+ sinfo.sinfo_context = context;
+ sinfo.sinfo_assoc_id = 0;
+ return (syscall(SYS_sctp_generic_sendmsg, s,
+ data, len, to, tolen, &sinfo, 0));
+#else
+ struct msghdr msg;
+ struct sctp_sndrcvinfo *sinfo;
+ struct iovec iov;
+ char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+ struct cmsghdr *cmsg;
+ struct sockaddr *who = NULL;
+ union {
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } addr;
+
+ if ((tolen > 0) &&
+ ((to == NULL) || (tolen < sizeof(struct sockaddr)))) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((to != NULL) && (tolen > 0)) {
+ switch (to->sa_family) {
+ case AF_INET:
+ if (tolen != sizeof(struct sockaddr_in)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((to->sa_len > 0) &&
+ (to->sa_len != sizeof(struct sockaddr_in))) {
+ errno = EINVAL;
+ return (-1);
+ }
+ memcpy(&addr, to, sizeof(struct sockaddr_in));
+ addr.in.sin_len = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ if (tolen != sizeof(struct sockaddr_in6)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((to->sa_len > 0) &&
+ (to->sa_len != sizeof(struct sockaddr_in6))) {
+ errno = EINVAL;
+ return (-1);
+ }
+ memcpy(&addr, to, sizeof(struct sockaddr_in6));
+ addr.in6.sin6_len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ who = (struct sockaddr *)&addr;
+ }
+
+ iov.iov_base = (char *)data;
+ iov.iov_len = len;
+
+ if (who) {
+ msg.msg_name = (caddr_t)who;
+ msg.msg_namelen = who->sa_len;
+ } else {
+ msg.msg_name = (caddr_t)NULL;
+ msg.msg_namelen = 0;
+ }
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
+ msg.msg_flags = 0;
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+ sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
+ sinfo->sinfo_stream = stream_no;
+ sinfo->sinfo_ssn = 0;
+ sinfo->sinfo_flags = flags;
+ sinfo->sinfo_ppid = ppid;
+ sinfo->sinfo_context = context;
+ sinfo->sinfo_assoc_id = 0;
+ sinfo->sinfo_timetolive = timetolive;
+ return (sendmsg(s, &msg, 0));
+#endif
+}
+
+
+sctp_assoc_t
+sctp_getassocid(int sd, struct sockaddr *sa)
+{
+ struct sctp_paddrinfo sp;
+ socklen_t siz;
+
+ /* First get the assoc id */
+ siz = sizeof(sp);
+ memset(&sp, 0, sizeof(sp));
+ memcpy((caddr_t)&sp.spinfo_address, sa, sa->sa_len);
+ if (getsockopt(sd, IPPROTO_SCTP,
+ SCTP_GET_PEER_ADDR_INFO, &sp, &siz) != 0) {
+ /* We depend on the fact that 0 can never be returned */
+ return ((sctp_assoc_t)0);
+ }
+ return (sp.spinfo_assoc_id);
+}
+
+ssize_t
+sctp_send(int sd, const void *data, size_t len,
+ const struct sctp_sndrcvinfo *sinfo,
+ int flags)
+{
+
+#ifdef SYS_sctp_generic_sendmsg
+ struct sockaddr *to = NULL;
+
+ return (syscall(SYS_sctp_generic_sendmsg, sd,
+ data, len, to, 0, sinfo, flags));
+#else
+ struct msghdr msg;
+ struct iovec iov;
+ char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+ struct cmsghdr *cmsg;
+
+ if (sinfo == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ iov.iov_base = (char *)data;
+ iov.iov_len = len;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
+ msg.msg_flags = 0;
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+ memcpy(CMSG_DATA(cmsg), sinfo, sizeof(struct sctp_sndrcvinfo));
+ return (sendmsg(sd, &msg, flags));
+#endif
+}
+
+
+
+ssize_t
+sctp_sendx(int sd, const void *msg, size_t msg_len,
+ struct sockaddr *addrs, int addrcnt,
+ struct sctp_sndrcvinfo *sinfo,
+ int flags)
+{
+ struct sctp_sndrcvinfo __sinfo;
+ ssize_t ret;
+ int i, cnt, *aa, saved_errno;
+ char *buf;
+ int no_end_cx = 0;
+ size_t len, add_len;
+ struct sockaddr *at;
+
+ if (addrs == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+#ifdef SYS_sctp_generic_sendmsg
+ if (addrcnt == 1) {
+ socklen_t l;
+ ssize_t ret;
+
+ /*
+ * Quick way, we don't need to do a connectx so lets use the
+ * syscall directly.
+ */
+ l = addrs->sa_len;
+ ret = syscall(SYS_sctp_generic_sendmsg, sd,
+ msg, msg_len, addrs, l, sinfo, flags);
+ if ((ret >= 0) && (sinfo != NULL)) {
+ sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
+ }
+ return (ret);
+ }
+#endif
+
+ len = sizeof(int);
+ at = addrs;
+ cnt = 0;
+ /* validate all the addresses and get the size */
+ for (i = 0; i < addrcnt; i++) {
+ if (at->sa_family == AF_INET) {
+ add_len = sizeof(struct sockaddr_in);
+ } else if (at->sa_family == AF_INET6) {
+ add_len = sizeof(struct sockaddr_in6);
+ } else {
+ errno = EINVAL;
+ return (-1);
+ }
+ len += add_len;
+ at = (struct sockaddr *)((caddr_t)at + add_len);
+ cnt++;
+ }
+ /* do we have any? */
+ if (cnt == 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ buf = malloc(len);
+ if (buf == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ aa = (int *)buf;
+ *aa = cnt;
+ aa++;
+ memcpy((caddr_t)aa, addrs, (size_t)(len - sizeof(int)));
+ ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_DELAYED, (void *)buf,
+ (socklen_t)len);
+
+ free(buf);
+ if (ret != 0) {
+ if (errno == EALREADY) {
+ no_end_cx = 1;
+ goto continue_send;
+ }
+ return (ret);
+ }
+continue_send:
+ if (sinfo == NULL) {
+ sinfo = &__sinfo;
+ memset(&__sinfo, 0, sizeof(__sinfo));
+ }
+ sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
+ if (sinfo->sinfo_assoc_id == 0) {
+ (void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
+ (socklen_t)addrs->sa_len);
+ errno = ENOENT;
+ return (-1);
+ }
+ ret = sctp_send(sd, msg, msg_len, sinfo, flags);
+ saved_errno = errno;
+ if (no_end_cx == 0)
+ (void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
+ (socklen_t)addrs->sa_len);
+
+ errno = saved_errno;
+ return (ret);
+}
+
+ssize_t
+sctp_sendmsgx(int sd,
+ const void *msg,
+ size_t len,
+ struct sockaddr *addrs,
+ int addrcnt,
+ uint32_t ppid,
+ uint32_t flags,
+ uint16_t stream_no,
+ uint32_t timetolive,
+ uint32_t context)
+{
+ struct sctp_sndrcvinfo sinfo;
+
+ memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
+ sinfo.sinfo_ppid = ppid;
+ sinfo.sinfo_flags = flags;
+ sinfo.sinfo_stream = stream_no;
+ sinfo.sinfo_timetolive = timetolive;
+ sinfo.sinfo_context = context;
+ return (sctp_sendx(sd, msg, len, addrs, addrcnt, &sinfo, 0));
+}
+
+ssize_t
+sctp_recvmsg(int s,
+ void *dbuf,
+ size_t len,
+ struct sockaddr *from,
+ socklen_t *fromlen,
+ struct sctp_sndrcvinfo *sinfo,
+ int *msg_flags)
+{
+#ifdef SYS_sctp_generic_recvmsg
+ struct iovec iov;
+
+ iov.iov_base = dbuf;
+ iov.iov_len = len;
+ return (syscall(SYS_sctp_generic_recvmsg, s,
+ &iov, 1, from, fromlen, sinfo, msg_flags));
+#else
+ ssize_t sz;
+ struct msghdr msg;
+ struct iovec iov;
+ char cmsgbuf[SCTP_CONTROL_VEC_SIZE_RCV];
+ struct cmsghdr *cmsg;
+
+ if (msg_flags == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ iov.iov_base = dbuf;
+ iov.iov_len = len;
+ msg.msg_name = (caddr_t)from;
+ if (fromlen == NULL)
+ msg.msg_namelen = 0;
+ else
+ msg.msg_namelen = *fromlen;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+ msg.msg_flags = 0;
+ sz = recvmsg(s, &msg, *msg_flags);
+ *msg_flags = msg.msg_flags;
+ if (sz <= 0) {
+ return (sz);
+ }
+ if (sinfo) {
+ sinfo->sinfo_assoc_id = 0;
+ }
+ if ((msg.msg_controllen > 0) && (sinfo != NULL)) {
+ /*
+ * parse through and see if we find the sctp_sndrcvinfo (if
+ * the user wants it).
+ */
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != IPPROTO_SCTP) {
+ continue;
+ }
+ if (cmsg->cmsg_type == SCTP_SNDRCV) {
+ memcpy(sinfo, CMSG_DATA(cmsg), sizeof(struct sctp_sndrcvinfo));
+ break;
+ }
+ if (cmsg->cmsg_type == SCTP_EXTRCV) {
+ /*
+ * Let's hope that the user provided enough
+ * enough memory. At least he asked for more
+ * information.
+ */
+ memcpy(sinfo, CMSG_DATA(cmsg), sizeof(struct sctp_extrcvinfo));
+ break;
+ }
+ }
+ }
+ return (sz);
+#endif
+}
+
+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)
+{
+ char cmsgbuf[SCTP_CONTROL_VEC_SIZE_RCV];
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ ssize_t ret;
+ struct sctp_rcvinfo *rcvinfo;
+ struct sctp_nxtinfo *nxtinfo;
+
+ if (((info != NULL) && (infolen == NULL)) ||
+ ((info == NULL) && (infolen != NULL) && (*infolen != 0)) ||
+ ((info != NULL) && (infotype == NULL))) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if (infotype) {
+ *infotype = SCTP_RECVV_NOINFO;
+ }
+ msg.msg_name = from;
+ if (fromlen == NULL) {
+ msg.msg_namelen = 0;
+ } else {
+ msg.msg_namelen = *fromlen;
+ }
+ msg.msg_iov = (struct iovec *)iov;
+ msg.msg_iovlen = iovlen;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+ msg.msg_flags = 0;
+ ret = recvmsg(sd, &msg, *flags);
+ *flags = msg.msg_flags;
+ if ((ret > 0) &&
+ (msg.msg_controllen > 0) &&
+ (infotype != NULL) &&
+ (infolen != NULL) &&
+ (*infolen > 0)) {
+ rcvinfo = NULL;
+ nxtinfo = NULL;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != IPPROTO_SCTP) {
+ continue;
+ }
+ if (cmsg->cmsg_type == SCTP_RCVINFO) {
+ rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmsg);
+ if (nxtinfo != NULL) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ if (cmsg->cmsg_type == SCTP_NXTINFO) {
+ nxtinfo = (struct sctp_nxtinfo *)CMSG_DATA(cmsg);
+ if (rcvinfo != NULL) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ }
+ if (rcvinfo != NULL) {
+ if ((nxtinfo != NULL) && (*infolen >= sizeof(struct sctp_recvv_rn))) {
+ struct sctp_recvv_rn *rn_info;
+
+ rn_info = (struct sctp_recvv_rn *)info;
+ rn_info->recvv_rcvinfo = *rcvinfo;
+ rn_info->recvv_nxtinfo = *nxtinfo;
+ *infolen = (socklen_t)sizeof(struct sctp_recvv_rn);
+ *infotype = SCTP_RECVV_RN;
+ } else if (*infolen >= sizeof(struct sctp_rcvinfo)) {
+ memcpy(info, rcvinfo, sizeof(struct sctp_rcvinfo));
+ *infolen = (socklen_t)sizeof(struct sctp_rcvinfo);
+ *infotype = SCTP_RECVV_RCVINFO;
+ }
+ } else if (nxtinfo != NULL) {
+ if (*infolen >= sizeof(struct sctp_nxtinfo)) {
+ memcpy(info, nxtinfo, sizeof(struct sctp_nxtinfo));
+ *infolen = (socklen_t)sizeof(struct sctp_nxtinfo);
+ *infotype = SCTP_RECVV_NXTINFO;
+ }
+ }
+ }
+ return (ret);
+}
+
+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)
+{
+ ssize_t ret;
+ int i;
+ socklen_t addr_len;
+ struct msghdr msg;
+ in_port_t port;
+ struct sctp_sendv_spa *spa_info;
+ struct cmsghdr *cmsg;
+ char *cmsgbuf;
+ struct sockaddr *addr;
+ struct sockaddr_in *addr_in;
+ struct sockaddr_in6 *addr_in6;
+ sctp_assoc_t *assoc_id;
+
+ if ((addrcnt < 0) ||
+ (iovcnt < 0) ||
+ ((addrs == NULL) && (addrcnt > 0)) ||
+ ((addrs != NULL) && (addrcnt == 0)) ||
+ ((iov == NULL) && (iovcnt > 0)) ||
+ ((iov != NULL) && (iovcnt == 0))) {
+ errno = EINVAL;
+ return (-1);
+ }
+ cmsgbuf = malloc(CMSG_SPACE(sizeof(struct sctp_sndinfo)) +
+ CMSG_SPACE(sizeof(struct sctp_prinfo)) +
+ CMSG_SPACE(sizeof(struct sctp_authinfo)) +
+ (size_t)addrcnt * CMSG_SPACE(sizeof(struct in6_addr)));
+ if (cmsgbuf == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ assoc_id = NULL;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = 0;
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ switch (infotype) {
+ case SCTP_SENDV_NOINFO:
+ if ((infolen != 0) || (info != NULL)) {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ break;
+ case SCTP_SENDV_SNDINFO:
+ if ((info == NULL) || (infolen < sizeof(struct sctp_sndinfo))) {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
+ memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_sndinfo));
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
+ cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
+ assoc_id = &(((struct sctp_sndinfo *)info)->snd_assoc_id);
+ break;
+ case SCTP_SENDV_PRINFO:
+ if ((info == NULL) || (infolen < sizeof(struct sctp_prinfo))) {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_PRINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
+ memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_prinfo));
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
+ cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
+ break;
+ case SCTP_SENDV_AUTHINFO:
+ if ((info == NULL) || (infolen < sizeof(struct sctp_authinfo))) {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_AUTHINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo));
+ memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_authinfo));
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo));
+ cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
+ break;
+ case SCTP_SENDV_SPA:
+ if ((info == NULL) || (infolen < sizeof(struct sctp_sendv_spa))) {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ spa_info = (struct sctp_sendv_spa *)info;
+ if (spa_info->sendv_flags & SCTP_SEND_SNDINFO_VALID) {
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
+ memcpy(CMSG_DATA(cmsg), &spa_info->sendv_sndinfo, sizeof(struct sctp_sndinfo));
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
+ cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
+ assoc_id = &(spa_info->sendv_sndinfo.snd_assoc_id);
+ }
+ if (spa_info->sendv_flags & SCTP_SEND_PRINFO_VALID) {
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_PRINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
+ memcpy(CMSG_DATA(cmsg), &spa_info->sendv_prinfo, sizeof(struct sctp_prinfo));
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
+ cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
+ }
+ if (spa_info->sendv_flags & SCTP_SEND_AUTHINFO_VALID) {
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_AUTHINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo));
+ memcpy(CMSG_DATA(cmsg), &spa_info->sendv_authinfo, sizeof(struct sctp_authinfo));
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo));
+ cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
+ }
+ break;
+ default:
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ addr = addrs;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ for (i = 0; i < addrcnt; i++) {
+ switch (addr->sa_family) {
+ case AF_INET:
+ addr_len = (socklen_t)sizeof(struct sockaddr_in);
+ addr_in = (struct sockaddr_in *)addr;
+ if (addr_in->sin_len != addr_len) {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ if (i == 0) {
+ port = addr_in->sin_port;
+ } else {
+ if (port == addr_in->sin_port) {
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_DSTADDRV4;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+ memcpy(CMSG_DATA(cmsg), &addr_in->sin_addr, sizeof(struct in_addr));
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr));
+ cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in_addr)));
+ } else {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ }
+ break;
+ case AF_INET6:
+ addr_len = (socklen_t)sizeof(struct sockaddr_in6);
+ addr_in6 = (struct sockaddr_in6 *)addr;
+ if (addr_in6->sin6_len != addr_len) {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ if (i == 0) {
+ port = addr_in6->sin6_port;
+ } else {
+ if (port == addr_in6->sin6_port) {
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_DSTADDRV6;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_addr));
+ memcpy(CMSG_DATA(cmsg), &addr_in6->sin6_addr, sizeof(struct in6_addr));
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct in6_addr));
+ cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in6_addr)));
+ } else {
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ }
+ break;
+ default:
+ free(cmsgbuf);
+ errno = EINVAL;
+ return (-1);
+ }
+ if (i == 0) {
+ msg.msg_name = addr;
+ msg.msg_namelen = addr_len;
+ }
+ addr = (struct sockaddr *)((caddr_t)addr + addr_len);
+ }
+ if (msg.msg_controllen == 0) {
+ msg.msg_control = NULL;
+ }
+ msg.msg_iov = (struct iovec *)iov;
+ msg.msg_iovlen = iovcnt;
+ msg.msg_flags = 0;
+ ret = sendmsg(sd, &msg, flags);
+ free(cmsgbuf);
+ if ((ret >= 0) && (addrs != NULL) && (assoc_id != NULL)) {
+ *assoc_id = sctp_getassocid(sd, addrs);
+ }
+ return (ret);
+}
+
+
+#if !defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
+
+int
+sctp_peeloff(int sd, sctp_assoc_t assoc_id)
+{
+ /* NOT supported, return invalid sd */
+ errno = ENOTSUP;
+ return (-1);
+}
+
+#endif
+#if defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
+int
+sctp_peeloff(int sd, sctp_assoc_t assoc_id)
+{
+ return (syscall(SYS_sctp_peeloff, sd, assoc_id));
+}
+
+#endif
+
+#undef SCTP_CONTROL_VEC_SIZE_RCV