xref: /freebsd/lib/libc/net/sctp_sys_calls.c (revision 7661de35d15f582ab33e3bd6b8d909601557e436)
1 /*-
2  * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved.
3  * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
4  * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * a) Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  *
12  * b) Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the distribution.
15  *
16  * c) Neither the name of Cisco Systems, Inc. nor the names of its
17  *    contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30  * THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <sys/errno.h>
44 #include <sys/syscall.h>
45 #include <sys/uio.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <netinet/sctp_uio.h>
49 #include <netinet/sctp.h>
50 
51 #ifndef IN6_IS_ADDR_V4MAPPED
52 #define IN6_IS_ADDR_V4MAPPED(a)		      \
53 	((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) &&	\
54 	 (*(const uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) &&	\
55 	 (*(const uint32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)))
56 #endif
57 
58 #define SCTP_CONTROL_VEC_SIZE_RCV  16384
59 
60 
61 static void
62 in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
63 {
64 	bzero(sin, sizeof(*sin));
65 	sin->sin_len = sizeof(struct sockaddr_in);
66 	sin->sin_family = AF_INET;
67 	sin->sin_port = sin6->sin6_port;
68 	sin->sin_addr.s_addr = sin6->sin6_addr.__u6_addr.__u6_addr32[3];
69 }
70 
71 int
72 sctp_getaddrlen(sa_family_t family)
73 {
74 	int ret, sd;
75 	socklen_t siz;
76 	struct sctp_assoc_value av;
77 
78 	av.assoc_value = family;
79 	siz = sizeof(av);
80 #if defined(AF_INET)
81 	sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
82 #elif defined(AF_INET6)
83 	sd = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP);
84 #else
85 	sd = -1;
86 #endif
87 	if (sd == -1) {
88 		return (-1);
89 	}
90 	ret = getsockopt(sd, IPPROTO_SCTP, SCTP_GET_ADDR_LEN, &av, &siz);
91 	close(sd);
92 	if (ret == 0) {
93 		return ((int)av.assoc_value);
94 	} else {
95 		return (-1);
96 	}
97 }
98 
99 int
100 sctp_connectx(int sd, const struct sockaddr *addrs, int addrcnt,
101     sctp_assoc_t * id)
102 {
103 	char *buf;
104 	int i, ret, *aa;
105 	char *cpto;
106 	const struct sockaddr *at;
107 	size_t len;
108 
109 	/* validate the address count and list */
110 	if ((addrs == NULL) || (addrcnt <= 0)) {
111 		errno = EINVAL;
112 		return (-1);
113 	}
114 	if ((buf = malloc(sizeof(int) + (size_t)addrcnt * sizeof(struct sockaddr_in6))) == NULL) {
115 		errno = E2BIG;
116 		return (-1);
117 	}
118 	len = sizeof(int);
119 	at = addrs;
120 	cpto = buf + sizeof(int);
121 	/* validate all the addresses and get the size */
122 	for (i = 0; i < addrcnt; i++) {
123 		switch (at->sa_family) {
124 		case AF_INET:
125 			if (at->sa_len != sizeof(struct sockaddr_in)) {
126 				free(buf);
127 				errno = EINVAL;
128 				return (-1);
129 			}
130 			memcpy(cpto, at, sizeof(struct sockaddr_in));
131 			cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
132 			len += sizeof(struct sockaddr_in);
133 			break;
134 		case AF_INET6:
135 			if (at->sa_len != sizeof(struct sockaddr_in6)) {
136 				free(buf);
137 				errno = EINVAL;
138 				return (-1);
139 			}
140 			if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) {
141 				in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at);
142 				cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
143 				len += sizeof(struct sockaddr_in);
144 			} else {
145 				memcpy(cpto, at, sizeof(struct sockaddr_in6));
146 				cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in6));
147 				len += sizeof(struct sockaddr_in6);
148 			}
149 			break;
150 		default:
151 			free(buf);
152 			errno = EINVAL;
153 			return (-1);
154 		}
155 		at = (struct sockaddr *)((caddr_t)at + at->sa_len);
156 	}
157 	aa = (int *)buf;
158 	*aa = addrcnt;
159 	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf,
160 	    (socklen_t) len);
161 	if ((ret == 0) && (id != NULL)) {
162 		*id = *(sctp_assoc_t *) buf;
163 	}
164 	free(buf);
165 	return (ret);
166 }
167 
168 int
169 sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
170 {
171 	struct sctp_getaddresses *gaddrs;
172 	struct sockaddr *sa;
173 	struct sockaddr_in *sin;
174 	struct sockaddr_in6 *sin6;
175 	int i;
176 	size_t argsz;
177 	uint16_t sport = 0;
178 
179 	/* validate the flags */
180 	if ((flags != SCTP_BINDX_ADD_ADDR) &&
181 	    (flags != SCTP_BINDX_REM_ADDR)) {
182 		errno = EFAULT;
183 		return (-1);
184 	}
185 	/* validate the address count and list */
186 	if ((addrcnt <= 0) || (addrs == NULL)) {
187 		errno = EINVAL;
188 		return (-1);
189 	}
190 	/* First pre-screen the addresses */
191 	sa = addrs;
192 	for (i = 0; i < addrcnt; i++) {
193 		switch (sa->sa_family) {
194 		case AF_INET:
195 			if (sa->sa_len != sizeof(struct sockaddr_in)) {
196 				errno = EINVAL;
197 				return (-1);
198 			}
199 			sin = (struct sockaddr_in *)sa;
200 			if (sin->sin_port) {
201 				/* non-zero port, check or save */
202 				if (sport) {
203 					/* Check against our port */
204 					if (sport != sin->sin_port) {
205 						errno = EINVAL;
206 						return (-1);
207 					}
208 				} else {
209 					/* save off the port */
210 					sport = sin->sin_port;
211 				}
212 			}
213 			break;
214 		case AF_INET6:
215 			if (sa->sa_len != sizeof(struct sockaddr_in6)) {
216 				errno = EINVAL;
217 				return (-1);
218 			}
219 			sin6 = (struct sockaddr_in6 *)sa;
220 			if (sin6->sin6_port) {
221 				/* non-zero port, check or save */
222 				if (sport) {
223 					/* Check against our port */
224 					if (sport != sin6->sin6_port) {
225 						errno = EINVAL;
226 						return (-1);
227 					}
228 				} else {
229 					/* save off the port */
230 					sport = sin6->sin6_port;
231 				}
232 			}
233 			break;
234 		default:
235 			/* Invalid address family specified. */
236 			errno = EAFNOSUPPORT;
237 			return (-1);
238 		}
239 		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
240 	}
241 	argsz = sizeof(struct sctp_getaddresses) +
242 	    sizeof(struct sockaddr_storage);
243 	if ((gaddrs = (struct sctp_getaddresses *)malloc(argsz)) == NULL) {
244 		errno = ENOMEM;
245 		return (-1);
246 	}
247 	sa = addrs;
248 	for (i = 0; i < addrcnt; i++) {
249 		memset(gaddrs, 0, argsz);
250 		gaddrs->sget_assoc_id = 0;
251 		memcpy(gaddrs->addr, sa, sa->sa_len);
252 		/*
253 		 * Now, if there was a port mentioned, assure that the first
254 		 * address has that port to make sure it fails or succeeds
255 		 * correctly.
256 		 */
257 		if ((i == 0) && (sport != 0)) {
258 			switch (gaddrs->addr->sa_family) {
259 			case AF_INET:
260 				sin = (struct sockaddr_in *)gaddrs->addr;
261 				sin->sin_port = sport;
262 				break;
263 			case AF_INET6:
264 				sin6 = (struct sockaddr_in6 *)gaddrs->addr;
265 				sin6->sin6_port = sport;
266 				break;
267 			}
268 		}
269 		if (setsockopt(sd, IPPROTO_SCTP, flags, gaddrs,
270 		    (socklen_t) argsz) != 0) {
271 			free(gaddrs);
272 			return (-1);
273 		}
274 		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
275 	}
276 	free(gaddrs);
277 	return (0);
278 }
279 
280 int
281 sctp_opt_info(int sd, sctp_assoc_t id, int opt, void *arg, socklen_t * size)
282 {
283 	if (arg == NULL) {
284 		errno = EINVAL;
285 		return (-1);
286 	}
287 	if ((id == SCTP_CURRENT_ASSOC) ||
288 	    (id == SCTP_ALL_ASSOC)) {
289 		errno = EINVAL;
290 		return (-1);
291 	}
292 	switch (opt) {
293 	case SCTP_RTOINFO:
294 		((struct sctp_rtoinfo *)arg)->srto_assoc_id = id;
295 		break;
296 	case SCTP_ASSOCINFO:
297 		((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
298 		break;
299 	case SCTP_DEFAULT_SEND_PARAM:
300 		((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
301 		break;
302 	case SCTP_PRIMARY_ADDR:
303 		((struct sctp_setprim *)arg)->ssp_assoc_id = id;
304 		break;
305 	case SCTP_PEER_ADDR_PARAMS:
306 		((struct sctp_paddrparams *)arg)->spp_assoc_id = id;
307 		break;
308 	case SCTP_MAXSEG:
309 		((struct sctp_assoc_value *)arg)->assoc_id = id;
310 		break;
311 	case SCTP_AUTH_KEY:
312 		((struct sctp_authkey *)arg)->sca_assoc_id = id;
313 		break;
314 	case SCTP_AUTH_ACTIVE_KEY:
315 		((struct sctp_authkeyid *)arg)->scact_assoc_id = id;
316 		break;
317 	case SCTP_DELAYED_SACK:
318 		((struct sctp_sack_info *)arg)->sack_assoc_id = id;
319 		break;
320 	case SCTP_CONTEXT:
321 		((struct sctp_assoc_value *)arg)->assoc_id = id;
322 		break;
323 	case SCTP_STATUS:
324 		((struct sctp_status *)arg)->sstat_assoc_id = id;
325 		break;
326 	case SCTP_GET_PEER_ADDR_INFO:
327 		((struct sctp_paddrinfo *)arg)->spinfo_assoc_id = id;
328 		break;
329 	case SCTP_PEER_AUTH_CHUNKS:
330 		((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
331 		break;
332 	case SCTP_LOCAL_AUTH_CHUNKS:
333 		((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
334 		break;
335 	case SCTP_TIMEOUTS:
336 		((struct sctp_timeouts *)arg)->stimo_assoc_id = id;
337 		break;
338 	case SCTP_EVENT:
339 		((struct sctp_event *)arg)->se_assoc_id = id;
340 		break;
341 	case SCTP_DEFAULT_SNDINFO:
342 		((struct sctp_sndinfo *)arg)->snd_assoc_id = id;
343 		break;
344 	case SCTP_DEFAULT_PRINFO:
345 		((struct sctp_default_prinfo *)arg)->pr_assoc_id = id;
346 		break;
347 	case SCTP_PEER_ADDR_THLDS:
348 		((struct sctp_paddrthlds *)arg)->spt_assoc_id = id;
349 		break;
350 	case SCTP_REMOTE_UDP_ENCAPS_PORT:
351 		((struct sctp_udpencaps *)arg)->sue_assoc_id = id;
352 		break;
353 	case SCTP_MAX_BURST:
354 		((struct sctp_assoc_value *)arg)->assoc_id = id;
355 		break;
356 	case SCTP_ENABLE_STREAM_RESET:
357 		((struct sctp_assoc_value *)arg)->assoc_id = id;
358 		break;
359 	default:
360 		break;
361 	}
362 	return (getsockopt(sd, IPPROTO_SCTP, opt, arg, size));
363 }
364 
365 int
366 sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
367 {
368 	struct sctp_getaddresses *addrs;
369 	struct sockaddr *sa;
370 	sctp_assoc_t asoc;
371 	caddr_t lim;
372 	socklen_t opt_len;
373 	int cnt;
374 
375 	if (raddrs == NULL) {
376 		errno = EFAULT;
377 		return (-1);
378 	}
379 	asoc = id;
380 	opt_len = (socklen_t) sizeof(sctp_assoc_t);
381 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_REMOTE_ADDR_SIZE,
382 	    &asoc, &opt_len) != 0) {
383 		return (-1);
384 	}
385 	/* size required is returned in 'asoc' */
386 	opt_len = (socklen_t) ((size_t)asoc + sizeof(struct sctp_getaddresses));
387 	addrs = calloc(1, (size_t)opt_len);
388 	if (addrs == NULL) {
389 		errno = ENOMEM;
390 		return (-1);
391 	}
392 	addrs->sget_assoc_id = id;
393 	/* Now lets get the array of addresses */
394 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_PEER_ADDRESSES,
395 	    addrs, &opt_len) != 0) {
396 		free(addrs);
397 		return (-1);
398 	}
399 	*raddrs = (struct sockaddr *)&addrs->addr[0];
400 	cnt = 0;
401 	sa = (struct sockaddr *)&addrs->addr[0];
402 	lim = (caddr_t)addrs + opt_len;
403 	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
404 		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
405 		cnt++;
406 	}
407 	return (cnt);
408 }
409 
410 void
411 sctp_freepaddrs(struct sockaddr *addrs)
412 {
413 	void *fr_addr;
414 
415 	/* Take away the hidden association id */
416 	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
417 	/* Now free it */
418 	free(fr_addr);
419 }
420 
421 int
422 sctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
423 {
424 	struct sctp_getaddresses *addrs;
425 	caddr_t lim;
426 	struct sockaddr *sa;
427 	size_t size_of_addresses;
428 	socklen_t opt_len;
429 	int cnt;
430 
431 	if (raddrs == NULL) {
432 		errno = EFAULT;
433 		return (-1);
434 	}
435 	size_of_addresses = 0;
436 	opt_len = (socklen_t) sizeof(int);
437 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDR_SIZE,
438 	    &size_of_addresses, &opt_len) != 0) {
439 		errno = ENOMEM;
440 		return (-1);
441 	}
442 	if (size_of_addresses == 0) {
443 		errno = ENOTCONN;
444 		return (-1);
445 	}
446 	opt_len = (socklen_t) (size_of_addresses +
447 	    sizeof(struct sockaddr_storage) +
448 	    sizeof(struct sctp_getaddresses));
449 	addrs = calloc(1, (size_t)opt_len);
450 	if (addrs == NULL) {
451 		errno = ENOMEM;
452 		return (-1);
453 	}
454 	addrs->sget_assoc_id = id;
455 	/* Now lets get the array of addresses */
456 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDRESSES, addrs,
457 	    &opt_len) != 0) {
458 		free(addrs);
459 		errno = ENOMEM;
460 		return (-1);
461 	}
462 	*raddrs = (struct sockaddr *)&addrs->addr[0];
463 	cnt = 0;
464 	sa = (struct sockaddr *)&addrs->addr[0];
465 	lim = (caddr_t)addrs + opt_len;
466 	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
467 		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
468 		cnt++;
469 	}
470 	return (cnt);
471 }
472 
473 void
474 sctp_freeladdrs(struct sockaddr *addrs)
475 {
476 	void *fr_addr;
477 
478 	/* Take away the hidden association id */
479 	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
480 	/* Now free it */
481 	free(fr_addr);
482 }
483 
484 ssize_t
485 sctp_sendmsg(int s,
486     const void *data,
487     size_t len,
488     const struct sockaddr *to,
489     socklen_t tolen,
490     uint32_t ppid,
491     uint32_t flags,
492     uint16_t stream_no,
493     uint32_t timetolive,
494     uint32_t context)
495 {
496 #ifdef SYS_sctp_generic_sendmsg
497 	struct sctp_sndrcvinfo sinfo;
498 
499 	memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
500 	sinfo.sinfo_ppid = ppid;
501 	sinfo.sinfo_flags = flags;
502 	sinfo.sinfo_stream = stream_no;
503 	sinfo.sinfo_timetolive = timetolive;
504 	sinfo.sinfo_context = context;
505 	sinfo.sinfo_assoc_id = 0;
506 	return (syscall(SYS_sctp_generic_sendmsg, s,
507 	    data, len, to, tolen, &sinfo, 0));
508 #else
509 	struct msghdr msg;
510 	struct sctp_sndrcvinfo *sinfo;
511 	struct iovec iov;
512 	char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
513 	struct cmsghdr *cmsg;
514 	struct sockaddr *who = NULL;
515 	union {
516 		struct sockaddr_in in;
517 		struct sockaddr_in6 in6;
518 	}     addr;
519 
520 	if ((tolen > 0) &&
521 	    ((to == NULL) || (tolen < sizeof(struct sockaddr)))) {
522 		errno = EINVAL;
523 		return (-1);
524 	}
525 	if ((to != NULL) && (tolen > 0)) {
526 		switch (to->sa_family) {
527 		case AF_INET:
528 			if (tolen != sizeof(struct sockaddr_in)) {
529 				errno = EINVAL;
530 				return (-1);
531 			}
532 			if ((to->sa_len > 0) &&
533 			    (to->sa_len != sizeof(struct sockaddr_in))) {
534 				errno = EINVAL;
535 				return (-1);
536 			}
537 			memcpy(&addr, to, sizeof(struct sockaddr_in));
538 			addr.in.sin_len = sizeof(struct sockaddr_in);
539 			break;
540 		case AF_INET6:
541 			if (tolen != sizeof(struct sockaddr_in6)) {
542 				errno = EINVAL;
543 				return (-1);
544 			}
545 			if ((to->sa_len > 0) &&
546 			    (to->sa_len != sizeof(struct sockaddr_in6))) {
547 				errno = EINVAL;
548 				return (-1);
549 			}
550 			memcpy(&addr, to, sizeof(struct sockaddr_in6));
551 			addr.in6.sin6_len = sizeof(struct sockaddr_in6);
552 			break;
553 		default:
554 			errno = EAFNOSUPPORT;
555 			return (-1);
556 		}
557 		who = (struct sockaddr *)&addr;
558 	}
559 	iov.iov_base = (char *)data;
560 	iov.iov_len = len;
561 
562 	if (who) {
563 		msg.msg_name = (caddr_t)who;
564 		msg.msg_namelen = who->sa_len;
565 	} else {
566 		msg.msg_name = (caddr_t)NULL;
567 		msg.msg_namelen = 0;
568 	}
569 	msg.msg_iov = &iov;
570 	msg.msg_iovlen = 1;
571 	msg.msg_control = cmsgbuf;
572 	msg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
573 	cmsg = (struct cmsghdr *)cmsgbuf;
574 	cmsg->cmsg_level = IPPROTO_SCTP;
575 	cmsg->cmsg_type = SCTP_SNDRCV;
576 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
577 	sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
578 	sinfo->sinfo_stream = stream_no;
579 	sinfo->sinfo_ssn = 0;
580 	sinfo->sinfo_flags = flags;
581 	sinfo->sinfo_ppid = ppid;
582 	sinfo->sinfo_context = context;
583 	sinfo->sinfo_assoc_id = 0;
584 	sinfo->sinfo_timetolive = timetolive;
585 	return (sendmsg(s, &msg, 0));
586 #endif
587 }
588 
589 
590 sctp_assoc_t
591 sctp_getassocid(int sd, struct sockaddr *sa)
592 {
593 	struct sctp_paddrinfo sp;
594 	socklen_t siz;
595 
596 	/* First get the assoc id */
597 	siz = sizeof(sp);
598 	memset(&sp, 0, sizeof(sp));
599 	memcpy((caddr_t)&sp.spinfo_address, sa, sa->sa_len);
600 	if (getsockopt(sd, IPPROTO_SCTP,
601 	    SCTP_GET_PEER_ADDR_INFO, &sp, &siz) != 0) {
602 		/* We depend on the fact that 0 can never be returned */
603 		return ((sctp_assoc_t) 0);
604 	}
605 	return (sp.spinfo_assoc_id);
606 }
607 
608 ssize_t
609 sctp_send(int sd, const void *data, size_t len,
610     const struct sctp_sndrcvinfo *sinfo,
611     int flags)
612 {
613 
614 #ifdef SYS_sctp_generic_sendmsg
615 	struct sockaddr *to = NULL;
616 
617 	return (syscall(SYS_sctp_generic_sendmsg, sd,
618 	    data, len, to, 0, sinfo, flags));
619 #else
620 	struct msghdr msg;
621 	struct iovec iov;
622 	char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
623 	struct cmsghdr *cmsg;
624 
625 	if (sinfo == NULL) {
626 		errno = EINVAL;
627 		return (-1);
628 	}
629 	iov.iov_base = (char *)data;
630 	iov.iov_len = len;
631 
632 	msg.msg_name = NULL;
633 	msg.msg_namelen = 0;
634 	msg.msg_iov = &iov;
635 	msg.msg_iovlen = 1;
636 	msg.msg_control = cmsgbuf;
637 	msg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
638 	cmsg = (struct cmsghdr *)cmsgbuf;
639 	cmsg->cmsg_level = IPPROTO_SCTP;
640 	cmsg->cmsg_type = SCTP_SNDRCV;
641 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
642 	memcpy(CMSG_DATA(cmsg), sinfo, sizeof(struct sctp_sndrcvinfo));
643 	return (sendmsg(sd, &msg, flags));
644 #endif
645 }
646 
647 
648 
649 ssize_t
650 sctp_sendx(int sd, const void *msg, size_t msg_len,
651     struct sockaddr *addrs, int addrcnt,
652     struct sctp_sndrcvinfo *sinfo,
653     int flags)
654 {
655 	struct sctp_sndrcvinfo __sinfo;
656 	ssize_t ret;
657 	int i, cnt, *aa, saved_errno;
658 	char *buf;
659 	int no_end_cx = 0;
660 	size_t len, add_len;
661 	struct sockaddr *at;
662 
663 	if (addrs == NULL) {
664 		errno = EINVAL;
665 		return (-1);
666 	}
667 #ifdef SYS_sctp_generic_sendmsg
668 	if (addrcnt == 1) {
669 		socklen_t l;
670 
671 		/*
672 		 * Quick way, we don't need to do a connectx so lets use the
673 		 * syscall directly.
674 		 */
675 		l = addrs->sa_len;
676 		return (syscall(SYS_sctp_generic_sendmsg, sd,
677 		    msg, msg_len, addrs, l, sinfo, flags));
678 	}
679 #endif
680 
681 	len = sizeof(int);
682 	at = addrs;
683 	cnt = 0;
684 	/* validate all the addresses and get the size */
685 	for (i = 0; i < addrcnt; i++) {
686 		if (at->sa_family == AF_INET) {
687 			add_len = sizeof(struct sockaddr_in);
688 		} else if (at->sa_family == AF_INET6) {
689 			add_len = sizeof(struct sockaddr_in6);
690 		} else {
691 			errno = EINVAL;
692 			return (-1);
693 		}
694 		len += add_len;
695 		at = (struct sockaddr *)((caddr_t)at + add_len);
696 		cnt++;
697 	}
698 	/* do we have any? */
699 	if (cnt == 0) {
700 		errno = EINVAL;
701 		return (-1);
702 	}
703 	buf = malloc(len);
704 	if (buf == NULL) {
705 		errno = ENOMEM;
706 		return (-1);
707 	}
708 	aa = (int *)buf;
709 	*aa = cnt;
710 	aa++;
711 	memcpy((caddr_t)aa, addrs, (size_t)(len - sizeof(int)));
712 	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_DELAYED, (void *)buf,
713 	    (socklen_t) len);
714 
715 	free(buf);
716 	if (ret != 0) {
717 		if (errno == EALREADY) {
718 			no_end_cx = 1;
719 			goto continue_send;
720 		}
721 		return (ret);
722 	}
723 continue_send:
724 	if (sinfo == NULL) {
725 		sinfo = &__sinfo;
726 		memset(&__sinfo, 0, sizeof(__sinfo));
727 	}
728 	sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
729 	if (sinfo->sinfo_assoc_id == 0) {
730 		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
731 		    (socklen_t) addrs->sa_len);
732 		errno = ENOENT;
733 		return (-1);
734 	}
735 	ret = sctp_send(sd, msg, msg_len, sinfo, flags);
736 	saved_errno = errno;
737 	if (no_end_cx == 0)
738 		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
739 		    (socklen_t) addrs->sa_len);
740 
741 	errno = saved_errno;
742 	return (ret);
743 }
744 
745 ssize_t
746 sctp_sendmsgx(int sd,
747     const void *msg,
748     size_t len,
749     struct sockaddr *addrs,
750     int addrcnt,
751     uint32_t ppid,
752     uint32_t flags,
753     uint16_t stream_no,
754     uint32_t timetolive,
755     uint32_t context)
756 {
757 	struct sctp_sndrcvinfo sinfo;
758 
759 	memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
760 	sinfo.sinfo_ppid = ppid;
761 	sinfo.sinfo_flags = flags;
762 	sinfo.sinfo_ssn = stream_no;
763 	sinfo.sinfo_timetolive = timetolive;
764 	sinfo.sinfo_context = context;
765 	return (sctp_sendx(sd, msg, len, addrs, addrcnt, &sinfo, 0));
766 }
767 
768 ssize_t
769 sctp_recvmsg(int s,
770     void *dbuf,
771     size_t len,
772     struct sockaddr *from,
773     socklen_t * fromlen,
774     struct sctp_sndrcvinfo *sinfo,
775     int *msg_flags)
776 {
777 #ifdef SYS_sctp_generic_recvmsg
778 	struct iovec iov;
779 
780 	iov.iov_base = dbuf;
781 	iov.iov_len = len;
782 	return (syscall(SYS_sctp_generic_recvmsg, s,
783 	    &iov, 1, from, fromlen, sinfo, msg_flags));
784 #else
785 	ssize_t sz;
786 	struct msghdr msg;
787 	struct iovec iov;
788 	char cmsgbuf[SCTP_CONTROL_VEC_SIZE_RCV];
789 	struct cmsghdr *cmsg;
790 
791 	if (msg_flags == NULL) {
792 		errno = EINVAL;
793 		return (-1);
794 	}
795 	msg.msg_flags = 0;
796 	iov.iov_base = dbuf;
797 	iov.iov_len = len;
798 	msg.msg_name = (caddr_t)from;
799 	if (fromlen == NULL)
800 		msg.msg_namelen = 0;
801 	else
802 		msg.msg_namelen = *fromlen;
803 	msg.msg_iov = &iov;
804 	msg.msg_iovlen = 1;
805 	msg.msg_control = cmsgbuf;
806 	msg.msg_controllen = sizeof(cmsgbuf);
807 	sz = recvmsg(s, &msg, *msg_flags);
808 	*msg_flags = msg.msg_flags;
809 	if (sz <= 0) {
810 		return (sz);
811 	}
812 	if (sinfo) {
813 		sinfo->sinfo_assoc_id = 0;
814 	}
815 	if ((msg.msg_controllen > 0) && (sinfo != NULL)) {
816 		/*
817 		 * parse through and see if we find the sctp_sndrcvinfo (if
818 		 * the user wants it).
819 		 */
820 		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
821 			if (cmsg->cmsg_level != IPPROTO_SCTP) {
822 				continue;
823 			}
824 			if (cmsg->cmsg_type == SCTP_SNDRCV) {
825 				memcpy(sinfo, CMSG_DATA(cmsg), sizeof(struct sctp_sndrcvinfo));
826 				break;
827 			}
828 			if (cmsg->cmsg_type == SCTP_EXTRCV) {
829 				/*
830 				 * Let's hope that the user provided enough
831 				 * enough memory. At least he asked for more
832 				 * information.
833 				 */
834 				memcpy(sinfo, CMSG_DATA(cmsg), sizeof(struct sctp_extrcvinfo));
835 				break;
836 			}
837 		}
838 	}
839 	return (sz);
840 #endif
841 }
842 
843 ssize_t
844 sctp_recvv(int sd,
845     const struct iovec *iov,
846     int iovlen,
847     struct sockaddr *from,
848     socklen_t * fromlen,
849     void *info,
850     socklen_t * infolen,
851     unsigned int *infotype,
852     int *flags)
853 {
854 	char cmsgbuf[SCTP_CONTROL_VEC_SIZE_RCV];
855 	struct msghdr msg;
856 	struct cmsghdr *cmsg;
857 	ssize_t ret;
858 	struct sctp_rcvinfo *rcvinfo;
859 	struct sctp_nxtinfo *nxtinfo;
860 
861 	if (((info != NULL) && (infolen == NULL)) |
862 	    ((info == NULL) && (infolen != NULL) && (*infolen != 0)) ||
863 	    ((info != NULL) && (infotype == NULL))) {
864 		errno = EINVAL;
865 		return (-1);
866 	}
867 	if (infotype) {
868 		*infotype = SCTP_RECVV_NOINFO;
869 	}
870 	msg.msg_name = from;
871 	if (fromlen == NULL) {
872 		msg.msg_namelen = 0;
873 	} else {
874 		msg.msg_namelen = *fromlen;
875 	}
876 	msg.msg_iov = (struct iovec *)iov;
877 	msg.msg_iovlen = iovlen;
878 	msg.msg_control = cmsgbuf;
879 	msg.msg_controllen = sizeof(cmsgbuf);
880 	ret = recvmsg(sd, &msg, *flags);
881 	*flags = msg.msg_flags;
882 	if ((ret > 0) &&
883 	    (msg.msg_controllen > 0) &&
884 	    (infotype != NULL) &&
885 	    (infolen != NULL) &&
886 	    (*infolen > 0)) {
887 		rcvinfo = NULL;
888 		nxtinfo = NULL;
889 		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
890 			if (cmsg->cmsg_level != IPPROTO_SCTP) {
891 				continue;
892 			}
893 			if (cmsg->cmsg_type == SCTP_RCVINFO) {
894 				rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmsg);
895 				if (nxtinfo != NULL) {
896 					break;
897 				} else {
898 					continue;
899 				}
900 			}
901 			if (cmsg->cmsg_type == SCTP_NXTINFO) {
902 				nxtinfo = (struct sctp_nxtinfo *)CMSG_DATA(cmsg);
903 				if (rcvinfo != NULL) {
904 					break;
905 				} else {
906 					continue;
907 				}
908 			}
909 		}
910 		if (rcvinfo != NULL) {
911 			if ((nxtinfo != NULL) && (*infolen >= sizeof(struct sctp_recvv_rn))) {
912 				struct sctp_recvv_rn *rn_info;
913 
914 				rn_info = (struct sctp_recvv_rn *)info;
915 				rn_info->recvv_rcvinfo = *rcvinfo;
916 				rn_info->recvv_nxtinfo = *nxtinfo;
917 				*infolen = (socklen_t) sizeof(struct sctp_recvv_rn);
918 				*infotype = SCTP_RECVV_RN;
919 			} else if (*infolen >= sizeof(struct sctp_rcvinfo)) {
920 				memcpy(info, rcvinfo, sizeof(struct sctp_rcvinfo));
921 				*infolen = (socklen_t) sizeof(struct sctp_rcvinfo);
922 				*infotype = SCTP_RECVV_RCVINFO;
923 			}
924 		} else if (nxtinfo != NULL) {
925 			if (*infolen >= sizeof(struct sctp_nxtinfo)) {
926 				memcpy(info, nxtinfo, sizeof(struct sctp_nxtinfo));
927 				*infolen = (socklen_t) sizeof(struct sctp_nxtinfo);
928 				*infotype = SCTP_RECVV_NXTINFO;
929 			}
930 		}
931 	}
932 	return (ret);
933 }
934 
935 ssize_t
936 sctp_sendv(int sd,
937     const struct iovec *iov, int iovcnt,
938     struct sockaddr *addrs, int addrcnt,
939     void *info, socklen_t infolen, unsigned int infotype,
940     int flags)
941 {
942 	ssize_t ret;
943 	int i;
944 	socklen_t addr_len;
945 	struct msghdr msg;
946 	in_port_t port;
947 	struct sctp_sendv_spa *spa_info;
948 	struct cmsghdr *cmsg;
949 	char *cmsgbuf;
950 	struct sockaddr *addr;
951 	struct sockaddr_in *addr_in;
952 	struct sockaddr_in6 *addr_in6;
953 
954 	if ((addrcnt < 0) ||
955 	    (iovcnt < 0) ||
956 	    ((addrs == NULL) && (addrcnt > 0)) ||
957 	    ((addrs != NULL) && (addrcnt == 0)) ||
958 	    ((iov == NULL) && (iovcnt > 0)) ||
959 	    ((iov != NULL) && (iovcnt == 0))) {
960 		errno = EINVAL;
961 		return (-1);
962 	}
963 	cmsgbuf = malloc(CMSG_SPACE(sizeof(struct sctp_sndinfo)) +
964 	    CMSG_SPACE(sizeof(struct sctp_prinfo)) +
965 	    CMSG_SPACE(sizeof(struct sctp_authinfo)) +
966 	    (size_t)addrcnt * CMSG_SPACE(sizeof(struct in6_addr)));
967 	if (cmsgbuf == NULL) {
968 		errno = ENOMEM;
969 		return (-1);
970 	}
971 	msg.msg_control = cmsgbuf;
972 	msg.msg_controllen = 0;
973 	cmsg = (struct cmsghdr *)cmsgbuf;
974 	switch (infotype) {
975 	case SCTP_SENDV_NOINFO:
976 		if ((infolen != 0) || (info != NULL)) {
977 			free(cmsgbuf);
978 			errno = EINVAL;
979 			return (-1);
980 		}
981 		break;
982 	case SCTP_SENDV_SNDINFO:
983 		if ((info == NULL) || (infolen < sizeof(struct sctp_sndinfo))) {
984 			free(cmsgbuf);
985 			errno = EINVAL;
986 			return (-1);
987 		}
988 		cmsg->cmsg_level = IPPROTO_SCTP;
989 		cmsg->cmsg_type = SCTP_SNDINFO;
990 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
991 		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_sndinfo));
992 		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
993 		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
994 		break;
995 	case SCTP_SENDV_PRINFO:
996 		if ((info == NULL) || (infolen < sizeof(struct sctp_prinfo))) {
997 			free(cmsgbuf);
998 			errno = EINVAL;
999 			return (-1);
1000 		}
1001 		cmsg->cmsg_level = IPPROTO_SCTP;
1002 		cmsg->cmsg_type = SCTP_PRINFO;
1003 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
1004 		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_prinfo));
1005 		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
1006 		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
1007 		break;
1008 	case SCTP_SENDV_AUTHINFO:
1009 		if ((info == NULL) || (infolen < sizeof(struct sctp_authinfo))) {
1010 			free(cmsgbuf);
1011 			errno = EINVAL;
1012 			return (-1);
1013 		}
1014 		cmsg->cmsg_level = IPPROTO_SCTP;
1015 		cmsg->cmsg_type = SCTP_AUTHINFO;
1016 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo));
1017 		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_authinfo));
1018 		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo));
1019 		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
1020 		break;
1021 	case SCTP_SENDV_SPA:
1022 		if ((info == NULL) || (infolen < sizeof(struct sctp_sendv_spa))) {
1023 			free(cmsgbuf);
1024 			errno = EINVAL;
1025 			return (-1);
1026 		}
1027 		spa_info = (struct sctp_sendv_spa *)info;
1028 		if (spa_info->sendv_flags & SCTP_SEND_SNDINFO_VALID) {
1029 			cmsg->cmsg_level = IPPROTO_SCTP;
1030 			cmsg->cmsg_type = SCTP_SNDINFO;
1031 			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
1032 			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_sndinfo, sizeof(struct sctp_sndinfo));
1033 			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
1034 			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
1035 		}
1036 		if (spa_info->sendv_flags & SCTP_SEND_PRINFO_VALID) {
1037 			cmsg->cmsg_level = IPPROTO_SCTP;
1038 			cmsg->cmsg_type = SCTP_PRINFO;
1039 			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
1040 			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_prinfo, sizeof(struct sctp_prinfo));
1041 			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
1042 			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
1043 		}
1044 		if (spa_info->sendv_flags & SCTP_SEND_AUTHINFO_VALID) {
1045 			cmsg->cmsg_level = IPPROTO_SCTP;
1046 			cmsg->cmsg_type = SCTP_AUTHINFO;
1047 			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo));
1048 			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_authinfo, sizeof(struct sctp_authinfo));
1049 			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo));
1050 			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
1051 		}
1052 		break;
1053 	default:
1054 		free(cmsgbuf);
1055 		errno = EINVAL;
1056 		return (-1);
1057 	}
1058 	addr = addrs;
1059 	msg.msg_name = NULL;
1060 	msg.msg_namelen = 0;
1061 
1062 	for (i = 0; i < addrcnt; i++) {
1063 		switch (addr->sa_family) {
1064 		case AF_INET:
1065 			addr_len = (socklen_t) sizeof(struct sockaddr_in);
1066 			addr_in = (struct sockaddr_in *)addr;
1067 			if (addr_in->sin_len != addr_len) {
1068 				free(cmsgbuf);
1069 				errno = EINVAL;
1070 				return (-1);
1071 			}
1072 			if (i == 0) {
1073 				port = addr_in->sin_port;
1074 			} else {
1075 				if (port == addr_in->sin_port) {
1076 					cmsg->cmsg_level = IPPROTO_SCTP;
1077 					cmsg->cmsg_type = SCTP_DSTADDRV4;
1078 					cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
1079 					memcpy(CMSG_DATA(cmsg), &addr_in->sin_addr, sizeof(struct in_addr));
1080 					msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr));
1081 					cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in_addr)));
1082 				} else {
1083 					free(cmsgbuf);
1084 					errno = EINVAL;
1085 					return (-1);
1086 				}
1087 			}
1088 			break;
1089 		case AF_INET6:
1090 			addr_len = (socklen_t) sizeof(struct sockaddr_in6);
1091 			addr_in6 = (struct sockaddr_in6 *)addr;
1092 			if (addr_in6->sin6_len != addr_len) {
1093 				free(cmsgbuf);
1094 				errno = EINVAL;
1095 				return (-1);
1096 			}
1097 			if (i == 0) {
1098 				port = addr_in6->sin6_port;
1099 			} else {
1100 				if (port == addr_in6->sin6_port) {
1101 					cmsg->cmsg_level = IPPROTO_SCTP;
1102 					cmsg->cmsg_type = SCTP_DSTADDRV6;
1103 					cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_addr));
1104 					memcpy(CMSG_DATA(cmsg), &addr_in6->sin6_addr, sizeof(struct in6_addr));
1105 					msg.msg_controllen += CMSG_SPACE(sizeof(struct in6_addr));
1106 					cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in6_addr)));
1107 				} else {
1108 					free(cmsgbuf);
1109 					errno = EINVAL;
1110 					return (-1);
1111 				}
1112 			}
1113 			break;
1114 		default:
1115 			free(cmsgbuf);
1116 			errno = EINVAL;
1117 			return (-1);
1118 		}
1119 		if (i == 0) {
1120 			msg.msg_name = addr;
1121 			msg.msg_namelen = addr_len;
1122 		}
1123 		addr = (struct sockaddr *)((caddr_t)addr + addr_len);
1124 	}
1125 	if (msg.msg_controllen == 0) {
1126 		msg.msg_control = NULL;
1127 	}
1128 	msg.msg_iov = (struct iovec *)iov;
1129 	msg.msg_iovlen = iovcnt;
1130 	msg.msg_flags = 0;
1131 	ret = sendmsg(sd, &msg, flags);
1132 	free(cmsgbuf);
1133 	return (ret);
1134 }
1135 
1136 
1137 #if !defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
1138 
1139 int
1140 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
1141 {
1142 	/* NOT supported, return invalid sd */
1143 	errno = ENOTSUP;
1144 	return (-1);
1145 }
1146 
1147 #endif
1148 #if defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
1149 int
1150 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
1151 {
1152 	return (syscall(SYS_sctp_peeloff, sd, assoc_id));
1153 }
1154 
1155 #endif
1156 
1157 #undef SCTP_CONTROL_VEC_SIZE_RCV
1158