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