1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #define _XPG4_2 30 #define __EXTENSIONS__ 31 32 #include <assert.h> 33 #include <sys/types.h> 34 #include <sys/uio.h> 35 #include <sys/socket.h> 36 #include <sys/stropts.h> 37 #include <sys/stream.h> 38 #include <sys/socketvar.h> 39 #include <sys/sockio.h> 40 41 #include <errno.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <stropts.h> 45 #include <stdio.h> 46 #include <strings.h> 47 #include <netinet/in.h> 48 #include <netinet/sctp.h> 49 50 /* This will hold either a v4 or a v6 sockaddr */ 51 union sockaddr_storage_v6 { 52 struct sockaddr_in in; 53 struct sockaddr_in6 in6; 54 }; 55 56 /* 57 * This file implements all the libsctp calls. 58 */ 59 60 /* 61 * To bind a list of addresses to a socket. If the socket is 62 * v4, the type of the list of addresses is (struct in_addr). 63 * If the socket is v6, the type is (struct in6_addr). 64 */ 65 int 66 sctp_bindx(int sock, void *addrs, int addrcnt, int flags) 67 { 68 socklen_t sz; 69 70 if (addrs == NULL || addrcnt == 0) { 71 errno = EINVAL; 72 return (-1); 73 } 74 75 /* Assume the caller uses the correct family type. */ 76 switch (((struct sockaddr *)addrs)->sa_family) { 77 case AF_INET: 78 sz = sizeof (struct sockaddr_in); 79 break; 80 case AF_INET6: 81 sz = sizeof (struct sockaddr_in6); 82 break; 83 default: 84 errno = EAFNOSUPPORT; 85 return (-1); 86 } 87 88 switch (flags) { 89 case SCTP_BINDX_ADD_ADDR: 90 return (setsockopt(sock, IPPROTO_SCTP, SCTP_ADD_ADDR, addrs, 91 sz * addrcnt)); 92 case SCTP_BINDX_REM_ADDR: 93 return (setsockopt(sock, IPPROTO_SCTP, SCTP_REM_ADDR, addrs, 94 sz * addrcnt)); 95 default: 96 errno = EINVAL; 97 return (-1); 98 } 99 } 100 101 /* 102 * XXX currently not atomic -- need a better way to do this. 103 */ 104 int 105 sctp_getpaddrs(int sock, sctp_assoc_t id, void **addrs) 106 { 107 uint32_t naddrs; 108 socklen_t bufsz; 109 struct sctpopt opt; 110 111 if (addrs == NULL) { 112 errno = EINVAL; 113 return (-1); 114 } 115 116 /* First, find out how many peer addresses there are. */ 117 *addrs = NULL; 118 119 opt.sopt_aid = id; 120 opt.sopt_name = SCTP_GET_NPADDRS; 121 opt.sopt_val = (caddr_t)&naddrs; 122 opt.sopt_len = sizeof (naddrs); 123 if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) { 124 return (-1); 125 } 126 if (naddrs == 0) 127 return (0); 128 129 /* 130 * Now we can get all the peer addresses. This will over allocate 131 * space for v4 socket. But it should be OK and save us 132 * the job to find out if it is a v4 or v6 socket. 133 */ 134 bufsz = sizeof (union sockaddr_storage_v6) * naddrs; 135 if ((*addrs = malloc(bufsz)) == NULL) { 136 return (-1); 137 } 138 opt.sopt_name = SCTP_GET_PADDRS; 139 opt.sopt_val = *addrs; 140 opt.sopt_len = bufsz; 141 if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) { 142 free(*addrs); 143 *addrs = NULL; 144 return (-1); 145 } 146 147 /* Calculate the number of addresses returned. */ 148 switch (((struct sockaddr *)*addrs)->sa_family) { 149 case AF_INET: 150 naddrs = opt.sopt_len / sizeof (struct sockaddr_in); 151 break; 152 case AF_INET6: 153 naddrs = opt.sopt_len / sizeof (struct sockaddr_in6); 154 break; 155 } 156 return (naddrs); 157 } 158 159 void 160 sctp_freepaddrs(void *addrs) 161 { 162 free(addrs); 163 } 164 165 int 166 sctp_getladdrs(int sock, sctp_assoc_t id, void **addrs) 167 { 168 uint32_t naddrs; 169 socklen_t bufsz; 170 struct sctpopt opt; 171 172 if (addrs == NULL) { 173 errno = EINVAL; 174 return (-1); 175 } 176 177 /* First, try to find out how many bound addresses there are. */ 178 *addrs = NULL; 179 180 opt.sopt_aid = id; 181 opt.sopt_name = SCTP_GET_NLADDRS; 182 opt.sopt_val = (caddr_t)&naddrs; 183 opt.sopt_len = sizeof (naddrs); 184 if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) { 185 return (-1); 186 } 187 if (naddrs == 0) 188 return (0); 189 190 /* 191 * Now we can get all the bound addresses. This will over allocate 192 * space for v4 socket. But it should be OK and save us 193 * the job to find out if it is a v4 or v6 socket. 194 */ 195 bufsz = sizeof (union sockaddr_storage_v6) * naddrs; 196 if ((*addrs = malloc(bufsz)) == NULL) { 197 return (-1); 198 } 199 opt.sopt_name = SCTP_GET_LADDRS; 200 opt.sopt_val = *addrs; 201 opt.sopt_len = bufsz; 202 if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) { 203 free(*addrs); 204 *addrs = NULL; 205 return (-1); 206 } 207 208 /* Calculate the number of addresses returned. */ 209 switch (((struct sockaddr *)*addrs)->sa_family) { 210 case AF_INET: 211 naddrs = opt.sopt_len / sizeof (struct sockaddr_in); 212 break; 213 case AF_INET6: 214 naddrs = opt.sopt_len / sizeof (struct sockaddr_in6); 215 break; 216 } 217 return (naddrs); 218 } 219 220 void 221 sctp_freeladdrs(void *addrs) 222 { 223 free(addrs); 224 } 225 226 int 227 sctp_opt_info(int sock, sctp_assoc_t id, int opt, void *arg, socklen_t *len) 228 { 229 struct sctpopt sopt; 230 231 sopt.sopt_aid = id; 232 sopt.sopt_name = opt; 233 sopt.sopt_val = arg; 234 sopt.sopt_len = *len; 235 236 if (ioctl(sock, SIOCSCTPGOPT, &sopt) == -1) { 237 return (-1); 238 } 239 *len = sopt.sopt_len; 240 return (0); 241 } 242 243 /* 244 * Branch off an association to its own socket. ioctl() allocates and 245 * returns new fd. 246 */ 247 int 248 sctp_peeloff(int sock, sctp_assoc_t id) 249 { 250 int fd; 251 252 fd = id; 253 if (ioctl(sock, SIOCSCTPPEELOFF, &fd) == -1) { 254 return (-1); 255 } 256 return (fd); 257 } 258 259 260 ssize_t 261 sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from, 262 socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags) 263 { 264 struct msghdr hdr; 265 struct iovec iov; 266 struct cmsghdr *cmsg; 267 char cinmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT]; 268 int err; 269 270 hdr.msg_name = from; 271 hdr.msg_namelen = (fromlen != NULL) ? *fromlen : 0; 272 hdr.msg_iov = &iov; 273 hdr.msg_iovlen = 1; 274 if (sinfo != NULL) { 275 hdr.msg_control = (void *)_CMSG_HDR_ALIGN(cinmsg); 276 hdr.msg_controllen = sizeof (cinmsg) - 277 (_CMSG_HDR_ALIGN(cinmsg) - (uintptr_t)cinmsg); 278 } else { 279 hdr.msg_control = NULL; 280 hdr.msg_controllen = 0; 281 } 282 283 iov.iov_base = msg; 284 iov.iov_len = len; 285 err = recvmsg(s, &hdr, msg_flags == NULL ? 0 : *msg_flags); 286 if (err == -1) { 287 return (-1); 288 } 289 if (fromlen != NULL) { 290 *fromlen = hdr.msg_namelen; 291 } 292 if (msg_flags != NULL) { 293 *msg_flags = hdr.msg_flags; 294 } 295 if (sinfo != NULL) { 296 for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL; 297 cmsg = CMSG_NXTHDR(&hdr, cmsg)) { 298 if (cmsg->cmsg_level == IPPROTO_SCTP && 299 cmsg->cmsg_type == SCTP_SNDRCV) { 300 bcopy(CMSG_DATA(cmsg), sinfo, sizeof (*sinfo)); 301 break; 302 } 303 } 304 } 305 return (err); 306 } 307 308 static ssize_t 309 sctp_send_common(int s, const void *msg, size_t len, const struct sockaddr *to, 310 socklen_t tolen, uint32_t ppid, uint32_t sinfo_flags, uint16_t stream_no, 311 uint32_t timetolive, uint32_t context, sctp_assoc_t aid, int flags) 312 { 313 struct msghdr hdr; 314 struct iovec iov; 315 struct sctp_sndrcvinfo *sinfo; 316 struct cmsghdr *cmsg; 317 char coutmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT]; 318 319 hdr.msg_name = (caddr_t)to; 320 hdr.msg_namelen = tolen; 321 hdr.msg_iov = &iov; 322 hdr.msg_iovlen = 1; 323 hdr.msg_control = (void *)_CMSG_HDR_ALIGN(coutmsg); 324 hdr.msg_controllen = sizeof (*cmsg) + sizeof (*sinfo); 325 326 iov.iov_len = len; 327 iov.iov_base = (caddr_t)msg; 328 329 cmsg = CMSG_FIRSTHDR(&hdr); 330 cmsg->cmsg_level = IPPROTO_SCTP; 331 cmsg->cmsg_type = SCTP_SNDRCV; 332 cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sinfo); 333 334 sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); 335 sinfo->sinfo_stream = stream_no; 336 sinfo->sinfo_ssn = 0; 337 sinfo->sinfo_flags = sinfo_flags; 338 sinfo->sinfo_ppid = ppid; 339 sinfo->sinfo_context = context; 340 sinfo->sinfo_timetolive = timetolive; 341 sinfo->sinfo_tsn = 0; 342 sinfo->sinfo_cumtsn = 0; 343 sinfo->sinfo_assoc_id = aid; 344 345 return (sendmsg(s, &hdr, flags)); 346 } 347 348 ssize_t 349 sctp_send(int s, const void *msg, size_t len, 350 const struct sctp_sndrcvinfo *sinfo, int flags) 351 { 352 /* Note that msg can be NULL for pure control message. */ 353 if (sinfo == NULL) { 354 errno = EINVAL; 355 return (-1); 356 } 357 return (sctp_send_common(s, msg, len, NULL, 0, sinfo->sinfo_ppid, 358 sinfo->sinfo_flags, sinfo->sinfo_stream, sinfo->sinfo_timetolive, 359 sinfo->sinfo_context, sinfo->sinfo_assoc_id, flags)); 360 } 361 362 ssize_t 363 sctp_sendmsg(int s, const void *msg, size_t len, const struct sockaddr *to, 364 socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream_no, 365 uint32_t timetolive, uint32_t context) 366 { 367 return (sctp_send_common(s, msg, len, to, tolen, ppid, flags, 368 stream_no, timetolive, context, 0, 0)); 369 } 370