sctp.c revision 2
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#define _XPG4_2
2N/A#define __EXTENSIONS__
2N/A
2N/A#include <assert.h>
2N/A#include <sys/types.h>
2N/A#include <sys/uio.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/stropts.h>
2N/A#include <sys/stream.h>
2N/A#include <sys/socketvar.h>
2N/A#include <sys/sockio.h>
2N/A
2N/A#include <errno.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <stropts.h>
2N/A#include <stdio.h>
2N/A#include <strings.h>
2N/A#include <netinet/in.h>
2N/A#include <netinet/sctp.h>
2N/A
2N/A/* This will hold either a v4 or a v6 sockaddr */
2N/Aunion sockaddr_storage_v6 {
2N/A struct sockaddr_in in;
2N/A struct sockaddr_in6 in6;
2N/A};
2N/A
2N/A/*
2N/A * This file implements all the libsctp calls.
2N/A */
2N/A
2N/A/*
2N/A * To bind a list of addresses to a socket. If the socket is
2N/A * v4, the type of the list of addresses is (struct in_addr).
2N/A * If the socket is v6, the type is (struct in6_addr).
2N/A */
2N/Aint
2N/Asctp_bindx(int sock, void *addrs, int addrcnt, int flags)
2N/A{
2N/A socklen_t sz;
2N/A
2N/A if (addrs == NULL || addrcnt == 0) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A /* Assume the caller uses the correct family type. */
2N/A switch (((struct sockaddr *)addrs)->sa_family) {
2N/A case AF_INET:
2N/A sz = sizeof (struct sockaddr_in);
2N/A break;
2N/A case AF_INET6:
2N/A sz = sizeof (struct sockaddr_in6);
2N/A break;
2N/A default:
2N/A errno = EAFNOSUPPORT;
2N/A return (-1);
2N/A }
2N/A
2N/A switch (flags) {
2N/A case SCTP_BINDX_ADD_ADDR:
2N/A return (setsockopt(sock, IPPROTO_SCTP, SCTP_ADD_ADDR, addrs,
2N/A sz * addrcnt));
2N/A case SCTP_BINDX_REM_ADDR:
2N/A return (setsockopt(sock, IPPROTO_SCTP, SCTP_REM_ADDR, addrs,
2N/A sz * addrcnt));
2N/A default:
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * XXX currently not atomic -- need a better way to do this.
2N/A */
2N/Aint
2N/Asctp_getpaddrs(int sock, sctp_assoc_t id, void **addrs)
2N/A{
2N/A uint32_t naddrs;
2N/A socklen_t bufsz;
2N/A struct sctpopt opt;
2N/A
2N/A if (addrs == NULL) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A /* First, find out how many peer addresses there are. */
2N/A *addrs = NULL;
2N/A
2N/A opt.sopt_aid = id;
2N/A opt.sopt_name = SCTP_GET_NPADDRS;
2N/A opt.sopt_val = (caddr_t)&naddrs;
2N/A opt.sopt_len = sizeof (naddrs);
2N/A if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
2N/A return (-1);
2N/A }
2N/A if (naddrs == 0)
2N/A return (0);
2N/A
2N/A /*
2N/A * Now we can get all the peer addresses. This will over allocate
2N/A * space for v4 socket. But it should be OK and save us
2N/A * the job to find out if it is a v4 or v6 socket.
2N/A */
2N/A bufsz = sizeof (union sockaddr_storage_v6) * naddrs;
2N/A if ((*addrs = malloc(bufsz)) == NULL) {
2N/A return (-1);
2N/A }
2N/A opt.sopt_name = SCTP_GET_PADDRS;
2N/A opt.sopt_val = *addrs;
2N/A opt.sopt_len = bufsz;
2N/A if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
2N/A free(*addrs);
2N/A *addrs = NULL;
2N/A return (-1);
2N/A }
2N/A
2N/A /* Calculate the number of addresses returned. */
2N/A switch (((struct sockaddr *)*addrs)->sa_family) {
2N/A case AF_INET:
2N/A naddrs = opt.sopt_len / sizeof (struct sockaddr_in);
2N/A break;
2N/A case AF_INET6:
2N/A naddrs = opt.sopt_len / sizeof (struct sockaddr_in6);
2N/A break;
2N/A }
2N/A return (naddrs);
2N/A}
2N/A
2N/Avoid
2N/Asctp_freepaddrs(void *addrs)
2N/A{
2N/A free(addrs);
2N/A}
2N/A
2N/Aint
2N/Asctp_getladdrs(int sock, sctp_assoc_t id, void **addrs)
2N/A{
2N/A uint32_t naddrs;
2N/A socklen_t bufsz;
2N/A struct sctpopt opt;
2N/A
2N/A if (addrs == NULL) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A /* First, try to find out how many bound addresses there are. */
2N/A *addrs = NULL;
2N/A
2N/A opt.sopt_aid = id;
2N/A opt.sopt_name = SCTP_GET_NLADDRS;
2N/A opt.sopt_val = (caddr_t)&naddrs;
2N/A opt.sopt_len = sizeof (naddrs);
2N/A if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
2N/A return (-1);
2N/A }
2N/A if (naddrs == 0)
2N/A return (0);
2N/A
2N/A /*
2N/A * Now we can get all the bound addresses. This will over allocate
2N/A * space for v4 socket. But it should be OK and save us
2N/A * the job to find out if it is a v4 or v6 socket.
2N/A */
2N/A bufsz = sizeof (union sockaddr_storage_v6) * naddrs;
2N/A if ((*addrs = malloc(bufsz)) == NULL) {
2N/A return (-1);
2N/A }
2N/A opt.sopt_name = SCTP_GET_LADDRS;
2N/A opt.sopt_val = *addrs;
2N/A opt.sopt_len = bufsz;
2N/A if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
2N/A free(*addrs);
2N/A *addrs = NULL;
2N/A return (-1);
2N/A }
2N/A
2N/A /* Calculate the number of addresses returned. */
2N/A switch (((struct sockaddr *)*addrs)->sa_family) {
2N/A case AF_INET:
2N/A naddrs = opt.sopt_len / sizeof (struct sockaddr_in);
2N/A break;
2N/A case AF_INET6:
2N/A naddrs = opt.sopt_len / sizeof (struct sockaddr_in6);
2N/A break;
2N/A }
2N/A return (naddrs);
2N/A}
2N/A
2N/Avoid
2N/Asctp_freeladdrs(void *addrs)
2N/A{
2N/A free(addrs);
2N/A}
2N/A
2N/Aint
2N/Asctp_opt_info(int sock, sctp_assoc_t id, int opt, void *arg, socklen_t *len)
2N/A{
2N/A struct sctpopt sopt;
2N/A
2N/A sopt.sopt_aid = id;
2N/A sopt.sopt_name = opt;
2N/A sopt.sopt_val = arg;
2N/A sopt.sopt_len = *len;
2N/A
2N/A if (ioctl(sock, SIOCSCTPGOPT, &sopt) == -1) {
2N/A return (-1);
2N/A }
2N/A *len = sopt.sopt_len;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Branch off an association to its own socket. ioctl() allocates and
2N/A * returns new fd.
2N/A */
2N/Aint
2N/Asctp_peeloff(int sock, sctp_assoc_t id)
2N/A{
2N/A int fd;
2N/A
2N/A fd = id;
2N/A if (ioctl(sock, SIOCSCTPPEELOFF, &fd) == -1) {
2N/A return (-1);
2N/A }
2N/A return (fd);
2N/A}
2N/A
2N/A
2N/Assize_t
2N/Asctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from,
2N/A socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags)
2N/A{
2N/A struct msghdr hdr;
2N/A struct iovec iov;
2N/A struct cmsghdr *cmsg;
2N/A char cinmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT];
2N/A int err;
2N/A
2N/A hdr.msg_name = from;
2N/A hdr.msg_namelen = (fromlen != NULL) ? *fromlen : 0;
2N/A hdr.msg_iov = &iov;
2N/A hdr.msg_iovlen = 1;
2N/A if (sinfo != NULL) {
2N/A hdr.msg_control = (void *)_CMSG_HDR_ALIGN(cinmsg);
2N/A hdr.msg_controllen = sizeof (cinmsg) -
2N/A (_CMSG_HDR_ALIGN(cinmsg) - (uintptr_t)cinmsg);
2N/A } else {
2N/A hdr.msg_control = NULL;
2N/A hdr.msg_controllen = 0;
2N/A }
2N/A
2N/A iov.iov_base = msg;
2N/A iov.iov_len = len;
2N/A err = recvmsg(s, &hdr, msg_flags == NULL ? 0 : *msg_flags);
2N/A if (err == -1) {
2N/A return (-1);
2N/A }
2N/A if (fromlen != NULL) {
2N/A *fromlen = hdr.msg_namelen;
2N/A }
2N/A if (msg_flags != NULL) {
2N/A *msg_flags = hdr.msg_flags;
2N/A }
2N/A if (sinfo != NULL) {
2N/A for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
2N/A cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
2N/A if (cmsg->cmsg_level == IPPROTO_SCTP &&
2N/A cmsg->cmsg_type == SCTP_SNDRCV) {
2N/A bcopy(CMSG_DATA(cmsg), sinfo, sizeof (*sinfo));
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A return (err);
2N/A}
2N/A
2N/Astatic ssize_t
2N/Asctp_send_common(int s, const void *msg, size_t len, const struct sockaddr *to,
2N/A socklen_t tolen, uint32_t ppid, uint32_t sinfo_flags, uint16_t stream_no,
2N/A uint32_t timetolive, uint32_t context, sctp_assoc_t aid, int flags)
2N/A{
2N/A struct msghdr hdr;
2N/A struct iovec iov;
2N/A struct sctp_sndrcvinfo *sinfo;
2N/A struct cmsghdr *cmsg;
2N/A char coutmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT];
2N/A
2N/A hdr.msg_name = (caddr_t)to;
2N/A hdr.msg_namelen = tolen;
2N/A hdr.msg_iov = &iov;
2N/A hdr.msg_iovlen = 1;
2N/A hdr.msg_control = (void *)_CMSG_HDR_ALIGN(coutmsg);
2N/A hdr.msg_controllen = sizeof (*cmsg) + sizeof (*sinfo);
2N/A
2N/A iov.iov_len = len;
2N/A iov.iov_base = (caddr_t)msg;
2N/A
2N/A cmsg = CMSG_FIRSTHDR(&hdr);
2N/A cmsg->cmsg_level = IPPROTO_SCTP;
2N/A cmsg->cmsg_type = SCTP_SNDRCV;
2N/A cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sinfo);
2N/A
2N/A sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
2N/A sinfo->sinfo_stream = stream_no;
2N/A sinfo->sinfo_ssn = 0;
2N/A sinfo->sinfo_flags = sinfo_flags;
2N/A sinfo->sinfo_ppid = ppid;
2N/A sinfo->sinfo_context = context;
2N/A sinfo->sinfo_timetolive = timetolive;
2N/A sinfo->sinfo_tsn = 0;
2N/A sinfo->sinfo_cumtsn = 0;
2N/A sinfo->sinfo_assoc_id = aid;
2N/A
2N/A return (sendmsg(s, &hdr, flags));
2N/A}
2N/A
2N/Assize_t
2N/Asctp_send(int s, const void *msg, size_t len,
2N/A const struct sctp_sndrcvinfo *sinfo, int flags)
2N/A{
2N/A /* Note that msg can be NULL for pure control message. */
2N/A if (sinfo == NULL) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A return (sctp_send_common(s, msg, len, NULL, 0, sinfo->sinfo_ppid,
2N/A sinfo->sinfo_flags, sinfo->sinfo_stream, sinfo->sinfo_timetolive,
2N/A sinfo->sinfo_context, sinfo->sinfo_assoc_id, flags));
2N/A}
2N/A
2N/Assize_t
2N/Asctp_sendmsg(int s, const void *msg, size_t len, const struct sockaddr *to,
2N/A socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream_no,
2N/A uint32_t timetolive, uint32_t context)
2N/A{
2N/A return (sctp_send_common(s, msg, len, to, tolen, ppid, flags,
2N/A stream_no, timetolive, context, 0, 0));
2N/A}
2N/A
2N/A/*
2N/A * This function uses the SIOCSCTPCTX ioctl to try setting up an association
2N/A * using the list of addresses given. For 1-N style socket, the assoc ID
2N/A * of the new association is returned in aid.
2N/A */
2N/Aint
2N/Asctp_connectx(int sd, struct sockaddr *addrs, int addrcnt, sctp_assoc_t *aid)
2N/A{
2N/A socklen_t sz;
2N/A struct sctp_ctx ctx;
2N/A
2N/A if (addrs == NULL || addrcnt <= 0) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A switch (addrs->sa_family) {
2N/A case AF_INET:
2N/A sz = sizeof (struct sockaddr_in);
2N/A break;
2N/A case AF_INET6:
2N/A sz = sizeof (struct sockaddr_in6);
2N/A break;
2N/A default:
2N/A errno = EAFNOSUPPORT;
2N/A return (-1);
2N/A }
2N/A
2N/A ctx.sctx_aid = 0;
2N/A ctx.sctx_addr_cnt = addrcnt;
2N/A ctx.sctx_addr_len = addrcnt * sz;
2N/A ctx.sctx_addrs = (caddr_t)addrs;
2N/A
2N/A if (ioctl(sd, SIOCSCTPCTX, &ctx) == -1)
2N/A return (-1);
2N/A
2N/A /* Need to return the aid created for 1-N style socket. */
2N/A if (aid != NULL)
2N/A *aid = ctx.sctx_aid;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Receive a message along with its SCTP attributes. The message is stored
2N/A * in the iov vector. The sender of the message is stored in from. The
2N/A * receive attriute is stoed in info. The type of attribute is stored in
2N/A * infotype.
2N/A */
2N/Assize_t
2N/Asctp_recvv(int sd, const struct iovec *iov, int iovlen, struct sockaddr *from,
2N/A socklen_t *fromlen, void *info, socklen_t *infolen, unsigned int *infotype,
2N/A int *flags)
2N/A{
2N/A struct msghdr hdr;
2N/A struct cmsghdr *cmsg;
2N/A char *control = NULL;
2N/A struct sctp_rcvinfo *rcvinfo;
2N/A struct sctp_nxtinfo *nxtinfo;
2N/A int err;
2N/A
2N/A hdr.msg_name = from;
2N/A hdr.msg_namelen = (fromlen != NULL) ? *fromlen : 0;
2N/A hdr.msg_iov = (struct iovec *)iov;
2N/A hdr.msg_iovlen = iovlen;
2N/A if (info != NULL) {
2N/A ssize_t clen;
2N/A
2N/A /*
2N/A * Just in case, allocate a space to hold both sctp_rcvinfo and
2N/A * sctp_nxtinfo.
2N/A */
2N/A clen = CMSG_SPACE(sizeof (struct sctp_rcvinfo)) +
2N/A CMSG_SPACE(sizeof (struct sctp_nxtinfo)) +
2N/A _CMSG_HDR_ALIGNMENT;
2N/A if ((control = malloc(clen)) == NULL)
2N/A return (-1);
2N/A
2N/A hdr.msg_control = (void *)_CMSG_HDR_ALIGN(control);
2N/A hdr.msg_controllen = clen - (_CMSG_HDR_ALIGN(control) -
2N/A (uintptr_t)control);
2N/A } else {
2N/A hdr.msg_control = NULL;
2N/A hdr.msg_controllen = 0;
2N/A }
2N/A
2N/A err = recvmsg(sd, &hdr, flags == NULL ? 0 : *flags);
2N/A if (err == -1) {
2N/A if (control != NULL)
2N/A free(control);
2N/A return (-1);
2N/A }
2N/A
2N/A if (fromlen != NULL)
2N/A *fromlen = hdr.msg_namelen;
2N/A if (flags != NULL)
2N/A *flags = hdr.msg_flags;
2N/A if (infotype != NULL)
2N/A *infotype = SCTP_RECVV_NOINFO;
2N/A
2N/A if (info != NULL) {
2N/A socklen_t tmp_infolen;
2N/A struct sctp_recvv_rn *rn;
2N/A
2N/A rcvinfo = NULL;
2N/A nxtinfo = NULL;
2N/A
2N/A /*
2N/A * SCTP does not send up sctp_recvv_rn. So we need to first
2N/A * find the sctp_rcvinfo and sctp_nxtinfo. If both are
2N/A * present, we create a sctp_recvv_rn.
2N/A */
2N/A for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
2N/A cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
2N/A if (cmsg->cmsg_level == IPPROTO_SCTP) {
2N/A switch (cmsg->cmsg_type) {
2N/A case SCTP_RCVINFO:
2N/A rcvinfo = (struct sctp_rcvinfo *)
2N/A CMSG_DATA(cmsg);
2N/A break;
2N/A case SCTP_NXTINFO:
2N/A nxtinfo = (struct sctp_nxtinfo *)
2N/A CMSG_DATA(cmsg);
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (rcvinfo == NULL && nxtinfo == NULL)
2N/A goto done;
2N/A
2N/A tmp_infolen = *infolen;
2N/A if (rcvinfo != NULL) {
2N/A if (tmp_infolen < sizeof (*rcvinfo))
2N/A goto done;
2N/A bcopy(rcvinfo, info, sizeof (*rcvinfo));
2N/A *infotype = SCTP_RECVV_RCVINFO;
2N/A *infolen = sizeof (*rcvinfo);
2N/A }
2N/A if (nxtinfo != NULL) {
2N/A if (rcvinfo != NULL) {
2N/A if (tmp_infolen >= sizeof (*rn)) {
2N/A rn = info;
2N/A bcopy(nxtinfo, &rn->recvv_nxtinfo,
2N/A sizeof (*nxtinfo));
2N/A *infotype = SCTP_RECVV_RN;
2N/A *infolen = sizeof (*rn);
2N/A }
2N/A } else if (tmp_infolen >= sizeof (*nxtinfo)) {
2N/A bcopy(nxtinfo, info, sizeof (*nxtinfo));
2N/A *infotype = SCTP_RECVV_NXTINFO;
2N/A *infolen = sizeof (*nxtinfo);
2N/A }
2N/A }
2N/A }
2N/Adone:
2N/A if (control != NULL)
2N/A free(control);
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * This function uses SIOCSCTPSNDV ioctl to send a message with attributes
2N/A * if given. It can also be used to set up an association and then send
2N/A * a message. The message is stored in vector iov. The addrs parameter
2N/A * is used to set up an association or to specify an address the message
2N/A * should be sent to. In the latter case, SCTP_ADDR_OVER should be set.
2N/A * The send attributes are stored in info and its type is passed in infotype.
2N/A */
2N/Assize_t
2N/Asctp_sendv(int sd, const struct iovec *iov, int iovcnt, struct sockaddr *addrs,
2N/A int addrcnt, void *info, socklen_t infolen, unsigned int infotype,
2N/A int flags)
2N/A{
2N/A boolean_t conn_req = B_FALSE;
2N/A struct sctp_sndinfo *sinfo;
2N/A struct sctp_sendv_spa *spa;
2N/A boolean_t sinfo_present = B_FALSE;
2N/A sctp_assoc_t aid = 0;
2N/A struct sctp_sendv sndv;
2N/A
2N/A if ((addrcnt > 0 && addrs == NULL) || (iovcnt > 0 && iov == NULL) ||
2N/A (infolen > 0 && info == NULL)) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A /* Check if this is a connect request. */
2N/A if (addrcnt > 1 || (addrcnt == 1 && info == NULL)) {
2N/A conn_req = B_TRUE;
2N/A } else if (addrcnt == 1) {
2N/A if (infotype == SCTP_SENDV_SNDINFO) {
2N/A sinfo = info;
2N/Acheck_aid:
2N/A sinfo_present = B_TRUE;
2N/A if (sinfo->snd_assoc_id == 0) {
2N/A if (sinfo->snd_flags & SCTP_ADDR_OVER) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A conn_req = B_TRUE;
2N/A } else if (!(sinfo->snd_flags & SCTP_ADDR_OVER)) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A } else if (infotype == SCTP_SENDV_SPA) {
2N/A spa = info;
2N/A if (spa->sendv_flags & SCTP_SEND_SNDINFO_VALID) {
2N/A sinfo = &spa->sendv_sndinfo;
2N/A goto check_aid;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (conn_req) {
2N/A if (sctp_connectx(sd, addrs, addrcnt, &aid) != 0)
2N/A return (-1);
2N/A }
2N/A
2N/A if (conn_req) {
2N/A sndv.ssndv_addr = NULL;
2N/A sndv.ssndv_addr_len = 0;
2N/A } else {
2N/A /*
2N/A * If it is not a connect request, only take the first address
2N/A * and ignore the rest if any.
2N/A */
2N/A sndv.ssndv_addr = (caddr_t)addrs;
2N/A if (addrs != NULL) {
2N/A if (addrs->sa_family == AF_INET) {
2N/A sndv.ssndv_addr_len =
2N/A sizeof (struct sockaddr_in);
2N/A } else {
2N/A sndv.ssndv_addr_len =
2N/A sizeof (struct sockaddr_in6);
2N/A }
2N/A } else {
2N/A sndv.ssndv_addr_len = 0;
2N/A }
2N/A }
2N/A
2N/A sndv.ssndv_aid = aid;
2N/A sndv.ssndv_iov = iov;
2N/A sndv.ssndv_iovcnt = iovcnt;
2N/A
2N/A sndv.ssndv_info = (caddr_t)info;
2N/A sndv.ssndv_info_len = infolen;
2N/A sndv.ssndv_info_type = infotype;
2N/A sndv.ssndv_flags = flags;
2N/A
2N/A if (ioctl(sd, SIOCSCTPSNDV, &sndv) == -1) {
2N/A /* Terminate the new association if an error occurs. */
2N/A if (conn_req) {
2N/A struct sctp_sndinfo abort_info = { 0 };
2N/A
2N/A sndv.ssndv_aid = aid;
2N/A sndv.ssndv_iov = NULL;
2N/A sndv.ssndv_iovcnt = 0;
2N/A
2N/A abort_info.snd_flags = SCTP_ABORT;
2N/A sndv.ssndv_info = (caddr_t)&abort_info;
2N/A sndv.ssndv_info_len = sizeof (abort_info);
2N/A sndv.ssndv_info_type = SCTP_SENDV_SNDINFO;
2N/A sndv.ssndv_flags = 0;
2N/A
2N/A /* Nothing we can do if an error happens again. */
2N/A (void) ioctl(sd, SIOCSCTPSNDV, &sndv);
2N/A }
2N/A return (-1);
2N/A }
2N/A
2N/A if (conn_req && sinfo_present)
2N/A sinfo->snd_assoc_id = aid;
2N/A
2N/A return (0);
2N/A}