diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libsctp/common/sctp.c | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libsctp/common/sctp.c')
| -rw-r--r-- | usr/src/lib/libsctp/common/sctp.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/usr/src/lib/libsctp/common/sctp.c b/usr/src/lib/libsctp/common/sctp.c new file mode 100644 index 0000000000..3468b2a60a --- /dev/null +++ b/usr/src/lib/libsctp/common/sctp.c @@ -0,0 +1,369 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define _XPG4_2 +#define __EXTENSIONS__ + +#include <assert.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/stropts.h> +#include <sys/stream.h> +#include <sys/socketvar.h> +#include <sys/sockio.h> + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <stropts.h> +#include <stdio.h> +#include <strings.h> +#include <netinet/in.h> +#include <netinet/sctp.h> + +/* This will hold either a v4 or a v6 sockaddr */ +union sockaddr_storage_v6 { + struct sockaddr_in in; + struct sockaddr_in6 in6; +}; + +/* + * This file implements all the libsctp calls. + */ + +/* + * To bind a list of addresses to a socket. If the socket is + * v4, the type of the list of addresses is (struct in_addr). + * If the socket is v6, the type is (struct in6_addr). + */ +int +sctp_bindx(int sock, void *addrs, int addrcnt, int flags) +{ + socklen_t sz; + + if (addrs == NULL || addrcnt == 0) { + errno = EINVAL; + return (-1); + } + + /* Assume the caller uses the correct family type. */ + switch (((struct sockaddr *)addrs)->sa_family) { + case AF_INET: + sz = sizeof (struct sockaddr_in); + break; + case AF_INET6: + sz = sizeof (struct sockaddr_in6); + break; + default: + errno = EAFNOSUPPORT; + return (-1); + } + + switch (flags) { + case SCTP_BINDX_ADD_ADDR: + return (setsockopt(sock, IPPROTO_SCTP, SCTP_ADD_ADDR, addrs, + sz * addrcnt)); + case SCTP_BINDX_REM_ADDR: + return (setsockopt(sock, IPPROTO_SCTP, SCTP_REM_ADDR, addrs, + sz * addrcnt)); + default: + errno = EINVAL; + return (-1); + } +} + +/* + * XXX currently not atomic -- need a better way to do this. + */ +int +sctp_getpaddrs(int sock, sctp_assoc_t id, void **addrs) +{ + uint32_t naddrs; + socklen_t bufsz; + struct sctpopt opt; + + if (addrs == NULL) { + errno = EINVAL; + return (-1); + } + + /* First, find out how many peer addresses there are. */ + *addrs = NULL; + + opt.sopt_aid = id; + opt.sopt_name = SCTP_GET_NPADDRS; + opt.sopt_val = (caddr_t)&naddrs; + opt.sopt_len = sizeof (naddrs); + if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) { + return (-1); + } + if (naddrs == 0) + return (0); + + /* + * Now we can get all the peer addresses. This will over allocate + * space for v4 socket. But it should be OK and save us + * the job to find out if it is a v4 or v6 socket. + */ + bufsz = sizeof (union sockaddr_storage_v6) * naddrs; + if ((*addrs = malloc(bufsz)) == NULL) { + return (-1); + } + opt.sopt_name = SCTP_GET_PADDRS; + opt.sopt_val = *addrs; + opt.sopt_len = bufsz; + if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) { + free(*addrs); + *addrs = NULL; + return (-1); + } + + /* Calculate the number of addresses returned. */ + switch (((struct sockaddr *)*addrs)->sa_family) { + case AF_INET: + naddrs = opt.sopt_len / sizeof (struct sockaddr_in); + break; + case AF_INET6: + naddrs = opt.sopt_len / sizeof (struct sockaddr_in6); + break; + } + return (naddrs); +} + +void +sctp_freepaddrs(void *addrs) +{ + free(addrs); +} + +int +sctp_getladdrs(int sock, sctp_assoc_t id, void **addrs) +{ + uint32_t naddrs; + socklen_t bufsz; + struct sctpopt opt; + + if (addrs == NULL) { + errno = EINVAL; + return (-1); + } + + /* First, try to find out how many bound addresses there are. */ + *addrs = NULL; + + opt.sopt_aid = id; + opt.sopt_name = SCTP_GET_NLADDRS; + opt.sopt_val = (caddr_t)&naddrs; + opt.sopt_len = sizeof (naddrs); + if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) { + return (-1); + } + if (naddrs == 0) + return (0); + + /* + * Now we can get all the bound addresses. This will over allocate + * space for v4 socket. But it should be OK and save us + * the job to find out if it is a v4 or v6 socket. + */ + bufsz = sizeof (union sockaddr_storage_v6) * naddrs; + if ((*addrs = malloc(bufsz)) == NULL) { + return (-1); + } + opt.sopt_name = SCTP_GET_LADDRS; + opt.sopt_val = *addrs; + opt.sopt_len = bufsz; + if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) { + free(*addrs); + *addrs = NULL; + return (-1); + } + + /* Calculate the number of addresses returned. */ + switch (((struct sockaddr *)*addrs)->sa_family) { + case AF_INET: + naddrs = opt.sopt_len / sizeof (struct sockaddr_in); + break; + case AF_INET6: + naddrs = opt.sopt_len / sizeof (struct sockaddr_in6); + break; + } + return (naddrs); +} + +void +sctp_freeladdrs(void *addrs) +{ + free(addrs); +} + +int +sctp_opt_info(int sock, sctp_assoc_t id, int opt, void *arg, socklen_t *len) +{ + struct sctpopt sopt; + + sopt.sopt_aid = id; + sopt.sopt_name = opt; + sopt.sopt_val = arg; + sopt.sopt_len = *len; + + if (ioctl(sock, SIOCSCTPGOPT, &sopt) == -1) { + return (-1); + } + *len = sopt.sopt_len; + return (0); +} + +/* + * Branch off an association to its own socket. ioctl() allocates and + * returns new fd. + */ +int +sctp_peeloff(int sock, sctp_assoc_t id) +{ + int fd; + + fd = id; + if (ioctl(sock, SIOCSCTPPEELOFF, &fd) == -1) { + return (-1); + } + return (fd); +} + + +ssize_t +sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from, + socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags) +{ + struct msghdr hdr; + struct iovec iov; + struct cmsghdr *cmsg; + char cinmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT]; + int err; + + hdr.msg_name = from; + hdr.msg_namelen = (fromlen != NULL) ? *fromlen : 0; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + if (sinfo != NULL) { + hdr.msg_control = (void *)_CMSG_HDR_ALIGN(cinmsg); + hdr.msg_controllen = sizeof (cinmsg) - + (_CMSG_HDR_ALIGN(cinmsg) - (uintptr_t)cinmsg); + } else { + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + } + + iov.iov_base = msg; + iov.iov_len = len; + err = recvmsg(s, &hdr, msg_flags == NULL ? 0 : *msg_flags); + if (err == -1) { + return (-1); + } + if (fromlen != NULL) { + *fromlen = hdr.msg_namelen; + } + if (msg_flags != NULL) { + *msg_flags = hdr.msg_flags; + } + if (sinfo != NULL) { + for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL; + cmsg = CMSG_NXTHDR(&hdr, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_SCTP && + cmsg->cmsg_type == SCTP_SNDRCV) { + bcopy(CMSG_DATA(cmsg), sinfo, sizeof (*sinfo)); + break; + } + } + } + return (err); +} + +static ssize_t +sctp_send_common(int s, const void *msg, size_t len, const struct sockaddr *to, + socklen_t tolen, uint32_t ppid, uint32_t sinfo_flags, uint16_t stream_no, + uint32_t timetolive, uint32_t context, sctp_assoc_t aid, int flags) +{ + struct msghdr hdr; + struct iovec iov; + struct sctp_sndrcvinfo *sinfo; + struct cmsghdr *cmsg; + char coutmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT]; + + hdr.msg_name = (caddr_t)to; + hdr.msg_namelen = tolen; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = (void *)_CMSG_HDR_ALIGN(coutmsg); + hdr.msg_controllen = sizeof (*cmsg) + sizeof (*sinfo); + + iov.iov_len = len; + iov.iov_base = (caddr_t)msg; + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_SNDRCV; + cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sinfo); + + sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); + sinfo->sinfo_stream = stream_no; + sinfo->sinfo_ssn = 0; + sinfo->sinfo_flags = sinfo_flags; + sinfo->sinfo_ppid = ppid; + sinfo->sinfo_context = context; + sinfo->sinfo_timetolive = timetolive; + sinfo->sinfo_tsn = 0; + sinfo->sinfo_cumtsn = 0; + sinfo->sinfo_assoc_id = aid; + + return (sendmsg(s, &hdr, flags)); +} + +ssize_t +sctp_send(int s, const void *msg, size_t len, + const struct sctp_sndrcvinfo *sinfo, int flags) +{ + /* Note that msg can be NULL for pure control message. */ + if (sinfo == NULL) { + errno = EINVAL; + return (-1); + } + return (sctp_send_common(s, msg, len, NULL, 0, sinfo->sinfo_ppid, + sinfo->sinfo_flags, sinfo->sinfo_stream, sinfo->sinfo_timetolive, + sinfo->sinfo_context, sinfo->sinfo_assoc_id, flags)); +} + +ssize_t +sctp_sendmsg(int s, 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) +{ + return (sctp_send_common(s, msg, len, to, tolen, ppid, flags, + stream_no, timetolive, context, 0, 0)); +} |
