xref: /freebsd/lib/libc/net/sctp_sys_calls.c (revision 0bb263df82e129f5f8c82da6deb55dfe10daa677)
1 /*	$KAME: sctp_sys_calls.c,v 1.9 2004/08/17 06:08:53 itojun Exp $ */
2 
3 /*
4  * Copyright (C) 2002-2007 Cisco Systems Inc,
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/errno.h>
41 #include <sys/syscall.h>
42 #include <sys/uio.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <netinet/sctp_uio.h>
46 #include <netinet/sctp.h>
47 
48 #include <net/if_dl.h>
49 
50 #ifndef IN6_IS_ADDR_V4MAPPED
51 #define IN6_IS_ADDR_V4MAPPED(a)		      \
52 	((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) &&	\
53 	 (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) &&	\
54 	 (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)))
55 #endif
56 
57 
58 #define SCTP_CONTROL_VEC_SIZE_SND   8192
59 #define SCTP_CONTROL_VEC_SIZE_RCV  16384
60 #define SCTP_STACK_BUF_SIZE         2048
61 #define SCTP_SMALL_IOVEC_SIZE          2
62 
63 #ifdef SCTP_DEBUG_PRINT_ADDRESS
64 
65 #define SCTP_STRING_BUF_SZ 256
66 
67 static void
68 SCTPPrintAnAddress(struct sockaddr *a)
69 {
70 	char stringToPrint[SCTP_STRING_BUF_SZ];
71 	u_short prt;
72 	char *srcaddr, *txt;
73 
74 	if (a == NULL) {
75 		printf("NULL\n");
76 		return;
77 	}
78 	if (a->sa_family == AF_INET) {
79 		srcaddr = (char *)&((struct sockaddr_in *)a)->sin_addr;
80 		txt = "IPv4 Address: ";
81 		prt = ntohs(((struct sockaddr_in *)a)->sin_port);
82 	} else if (a->sa_family == AF_INET6) {
83 		srcaddr = (char *)&((struct sockaddr_in6 *)a)->sin6_addr;
84 		prt = ntohs(((struct sockaddr_in6 *)a)->sin6_port);
85 		txt = "IPv6 Address: ";
86 	} else if (a->sa_family == AF_LINK) {
87 		int i;
88 		char tbuf[SCTP_STRING_BUF_SZ];
89 		u_char adbuf[SCTP_STRING_BUF_SZ];
90 		struct sockaddr_dl *dl;
91 
92 		dl = (struct sockaddr_dl *)a;
93 		strncpy(tbuf, dl->sdl_data, dl->sdl_nlen);
94 		tbuf[dl->sdl_nlen] = 0;
95 		printf("Intf:%s (len:%d)Interface index:%d type:%x(%d) ll-len:%d ",
96 		    tbuf,
97 		    dl->sdl_nlen,
98 		    dl->sdl_index,
99 		    dl->sdl_type,
100 		    dl->sdl_type,
101 		    dl->sdl_alen
102 		    );
103 		memcpy(adbuf, LLADDR(dl), dl->sdl_alen);
104 		for (i = 0; i < dl->sdl_alen; i++) {
105 			printf("%2.2x", adbuf[i]);
106 			if (i < (dl->sdl_alen - 1))
107 				printf(":");
108 		}
109 		printf("\n");
110 		return;
111 	} else {
112 		return;
113 	}
114 	if (inet_ntop(a->sa_family, srcaddr, stringToPrint, sizeof(stringToPrint))) {
115 		if (a->sa_family == AF_INET6) {
116 			printf("%s%s:%d scope:%d\n",
117 			    txt, stringToPrint, prt,
118 			    ((struct sockaddr_in6 *)a)->sin6_scope_id);
119 		} else {
120 			printf("%s%s:%d\n", txt, stringToPrint, prt);
121 		}
122 
123 	} else {
124 		printf("%s unprintable?\n", txt);
125 	}
126 }
127 
128 #endif				/* SCTP_DEBUG_PRINT_ADDRESS */
129 
130 static void
131 in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
132 {
133 	bzero(sin, sizeof(*sin));
134 	sin->sin_len = sizeof(struct sockaddr_in);
135 	sin->sin_family = AF_INET;
136 	sin->sin_port = sin6->sin6_port;
137 	sin->sin_addr.s_addr = sin6->sin6_addr.__u6_addr.__u6_addr32[3];
138 }
139 
140 int
141 sctp_getaddrlen(sa_family_t family)
142 {
143 	int error, sd;
144 	socklen_t siz;
145 	struct sctp_assoc_value av;
146 
147 	av.assoc_value = family;
148 	siz = sizeof(av);
149 #if defined(AF_INET)
150 	sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
151 #elif defined(AF_INET6)
152 	sd = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP);
153 #endif
154 	if (sd == -1) {
155 		return (errno);
156 	}
157 	error = getsockopt(sd, IPPROTO_SCTP, SCTP_GET_ADDR_LEN, &av, &siz);
158 	close(sd);
159 	if (error == 0) {
160 		return ((int)av.assoc_value);
161 	} else {
162 		return (error);
163 	}
164 }
165 
166 int
167 sctp_connectx(int sd, const struct sockaddr *addrs, int addrcnt,
168     sctp_assoc_t * id)
169 {
170 	char buf[SCTP_STACK_BUF_SIZE];
171 	int i, ret, cnt, *aa;
172 	char *cpto;
173 	const struct sockaddr *at;
174 	sctp_assoc_t *p_id;
175 	size_t len = sizeof(int);
176 
177 	/* validate the address count and list */
178 	if ((addrs == NULL) || (addrcnt <= 0)) {
179 		errno = EINVAL;
180 		return (-1);
181 	}
182 	at = addrs;
183 	cnt = 0;
184 	cpto = ((caddr_t)buf + sizeof(int));
185 	/* validate all the addresses and get the size */
186 	for (i = 0; i < addrcnt; i++) {
187 		if (at->sa_family == AF_INET) {
188 			memcpy(cpto, at, at->sa_len);
189 			cpto = ((caddr_t)cpto + at->sa_len);
190 			len += at->sa_len;
191 		} else if (at->sa_family == AF_INET6) {
192 			if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) {
193 				len += sizeof(struct sockaddr_in);
194 				in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at);
195 				cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
196 				len += sizeof(struct sockaddr_in);
197 			} else {
198 				memcpy(cpto, at, at->sa_len);
199 				cpto = ((caddr_t)cpto + at->sa_len);
200 				len += at->sa_len;
201 			}
202 		} else {
203 			errno = EINVAL;
204 			return (-1);
205 		}
206 		if (len > (sizeof(buf) - sizeof(int))) {
207 			/* Never enough memory */
208 			return (E2BIG);
209 		}
210 		at = (struct sockaddr *)((caddr_t)at + at->sa_len);
211 		cnt++;
212 	}
213 	/* do we have any? */
214 	if (cnt == 0) {
215 		errno = EINVAL;
216 		return (-1);
217 	}
218 	aa = (int *)buf;
219 	*aa = cnt;
220 	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf,
221 	    (socklen_t) len);
222 	if ((ret == 0) && id) {
223 		p_id = (sctp_assoc_t *) buf;
224 		*id = *p_id;
225 	}
226 	return (ret);
227 }
228 
229 int
230 sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
231 {
232 	struct sctp_getaddresses *gaddrs;
233 	struct sockaddr *sa;
234 	int i, sz, fam, argsz;
235 
236 	/* validate the flags */
237 	if ((flags != SCTP_BINDX_ADD_ADDR) &&
238 	    (flags != SCTP_BINDX_REM_ADDR)) {
239 		errno = EFAULT;
240 		return (-1);
241 	}
242 	/* validate the address count and list */
243 	if ((addrcnt <= 0) || (addrs == NULL)) {
244 		errno = EINVAL;
245 		return (-1);
246 	}
247 	argsz = (sizeof(struct sockaddr_storage) +
248 	    sizeof(struct sctp_getaddresses));
249 	gaddrs = (struct sctp_getaddresses *)calloc(1, argsz);
250 	if (gaddrs == NULL) {
251 		errno = ENOMEM;
252 		return (-1);
253 	}
254 	gaddrs->sget_assoc_id = 0;
255 	sa = addrs;
256 	for (i = 0; i < addrcnt; i++) {
257 		sz = sa->sa_len;
258 		fam = sa->sa_family;
259 		((struct sockaddr_in *)&addrs[i])->sin_port = ((struct sockaddr_in *)sa)->sin_port;
260 		if ((fam != AF_INET) && (fam != AF_INET6)) {
261 			errno = EINVAL;
262 			return (-1);
263 		}
264 		memcpy(gaddrs->addr, sa, sz);
265 		if (setsockopt(sd, IPPROTO_SCTP, flags,
266 		    gaddrs, (socklen_t) argsz) != 0) {
267 			free(gaddrs);
268 			return (-1);
269 		}
270 		memset(gaddrs, 0, argsz);
271 		sa = (struct sockaddr *)((caddr_t)sa + sz);
272 	}
273 	free(gaddrs);
274 	return (0);
275 }
276 
277 
278 int
279 sctp_opt_info(int sd, sctp_assoc_t id, int opt, void *arg, socklen_t * size)
280 {
281 	if (arg == NULL) {
282 		return (EINVAL);
283 	}
284 	*(sctp_assoc_t *) arg = id;
285 	return (getsockopt(sd, IPPROTO_SCTP, opt, arg, size));
286 }
287 
288 int
289 sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
290 {
291 	struct sctp_getaddresses *addrs;
292 	struct sockaddr *sa;
293 	struct sockaddr *re;
294 	sctp_assoc_t asoc;
295 	caddr_t lim;
296 	socklen_t siz;
297 	int cnt;
298 
299 	if (raddrs == NULL) {
300 		errno = EFAULT;
301 		return (-1);
302 	}
303 	asoc = id;
304 	siz = sizeof(sctp_assoc_t);
305 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_REMOTE_ADDR_SIZE,
306 	    &asoc, &siz) != 0) {
307 		errno = ENOMEM;
308 		return (-1);
309 	}
310 	/* size required is returned in 'asoc' */
311 	siz = (size_t)asoc;
312 	siz += sizeof(struct sctp_getaddresses);
313 	addrs = calloc(1, siz);
314 	if (addrs == NULL) {
315 		errno = ENOMEM;
316 		return (-1);
317 	}
318 	memset(addrs, 0, siz);
319 	addrs->sget_assoc_id = id;
320 	/* Now lets get the array of addresses */
321 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_PEER_ADDRESSES,
322 	    addrs, &siz) != 0) {
323 		free(addrs);
324 		errno = ENOMEM;
325 		return (-1);
326 	}
327 	re = (struct sockaddr *)&addrs->addr[0];
328 	*raddrs = re;
329 	cnt = 0;
330 	sa = (struct sockaddr *)&addrs->addr[0];
331 	lim = (caddr_t)addrs + siz;
332 	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
333 		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
334 		cnt++;
335 	}
336 	return (cnt);
337 }
338 
339 void
340 sctp_freepaddrs(struct sockaddr *addrs)
341 {
342 	/* Take away the hidden association id */
343 	void *fr_addr;
344 
345 	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
346 	/* Now free it */
347 	free(fr_addr);
348 }
349 
350 int
351 sctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
352 {
353 	struct sctp_getaddresses *addrs;
354 	struct sockaddr *re;
355 	caddr_t lim;
356 	struct sockaddr *sa;
357 	int size_of_addresses;
358 	socklen_t siz;
359 	int cnt;
360 
361 	if (raddrs == NULL) {
362 		errno = EFAULT;
363 		return (-1);
364 	}
365 	size_of_addresses = 0;
366 	siz = sizeof(int);
367 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDR_SIZE,
368 	    &size_of_addresses, &siz) != 0) {
369 		errno = ENOMEM;
370 		return (-1);
371 	}
372 	if (size_of_addresses == 0) {
373 		errno = ENOTCONN;
374 		return (-1);
375 	}
376 	siz = size_of_addresses + sizeof(struct sockaddr_storage);
377 	siz += sizeof(struct sctp_getaddresses);
378 	addrs = calloc(1, siz);
379 	if (addrs == NULL) {
380 		errno = ENOMEM;
381 		return (-1);
382 	}
383 	memset(addrs, 0, siz);
384 	addrs->sget_assoc_id = id;
385 	/* Now lets get the array of addresses */
386 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDRESSES, addrs,
387 	    &siz) != 0) {
388 		free(addrs);
389 		errno = ENOMEM;
390 		return (-1);
391 	}
392 	re = (struct sockaddr *)&addrs->addr[0];
393 	*raddrs = re;
394 	cnt = 0;
395 	sa = (struct sockaddr *)&addrs->addr[0];
396 	lim = (caddr_t)addrs + siz;
397 	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
398 		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
399 		cnt++;
400 	}
401 	return (cnt);
402 }
403 
404 void
405 sctp_freeladdrs(struct sockaddr *addrs)
406 {
407 	/* Take away the hidden association id */
408 	void *fr_addr;
409 
410 	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
411 	/* Now free it */
412 	free(fr_addr);
413 }
414 
415 
416 ssize_t
417 sctp_sendmsg(int s,
418     const void *data,
419     size_t len,
420     const struct sockaddr *to,
421     socklen_t tolen,
422     u_int32_t ppid,
423     u_int32_t flags,
424     u_int16_t stream_no,
425     u_int32_t timetolive,
426     u_int32_t context)
427 {
428 #ifdef SYS_sctp_generic_sendmsg
429 	struct sctp_sndrcvinfo sinfo;
430 
431 	sinfo.sinfo_ppid = ppid;
432 	sinfo.sinfo_flags = flags;
433 	sinfo.sinfo_stream = stream_no;
434 	sinfo.sinfo_timetolive = timetolive;
435 	sinfo.sinfo_context = context;
436 	sinfo.sinfo_assoc_id = 0;
437 	return (syscall(SYS_sctp_generic_sendmsg, s,
438 	    data, len, to, tolen, &sinfo, 0));
439 #else
440 
441 	ssize_t sz;
442 	struct msghdr msg;
443 	struct sctp_sndrcvinfo *s_info;
444 	struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
445 	char controlVector[SCTP_CONTROL_VEC_SIZE_RCV];
446 	struct cmsghdr *cmsg;
447 	struct sockaddr *who = NULL;
448 	union {
449 		struct sockaddr_in in;
450 		struct sockaddr_in6 in6;
451 	}     addr;
452 
453 /*
454   fprintf(io, "sctp_sendmsg(sd:%d, data:%x, len:%d, to:%x, tolen:%d, ppid:%x, flags:%x str:%d ttl:%d ctx:%x\n",
455   s,
456   (u_int)data,
457   (int)len,
458   (u_int)to,
459   (int)tolen,
460   ppid, flags,
461   (int)stream_no,
462   (int)timetolive,
463   (u_int)context);
464   fflush(io);
465 */
466 	if ((tolen > 0) && ((to == NULL) || (tolen < sizeof(struct sockaddr)))) {
467 		errno = EINVAL;
468 		return -1;
469 	}
470 	if (to && (tolen > 0)) {
471 		if (to->sa_family == AF_INET) {
472 			if (tolen != sizeof(struct sockaddr_in)) {
473 				errno = EINVAL;
474 				return -1;
475 			}
476 			if ((to->sa_len > 0) && (to->sa_len != sizeof(struct sockaddr_in))) {
477 				errno = EINVAL;
478 				return -1;
479 			}
480 			memcpy(&addr, to, sizeof(struct sockaddr_in));
481 			addr.in.sin_len = sizeof(struct sockaddr_in);
482 		} else if (to->sa_family == AF_INET6) {
483 			if (tolen != sizeof(struct sockaddr_in6)) {
484 				errno = EINVAL;
485 				return -1;
486 			}
487 			if ((to->sa_len > 0) && (to->sa_len != sizeof(struct sockaddr_in6))) {
488 				errno = EINVAL;
489 				return -1;
490 			}
491 			memcpy(&addr, to, sizeof(struct sockaddr_in6));
492 			addr.in6.sin6_len = sizeof(struct sockaddr_in6);
493 		} else {
494 			errno = EAFNOSUPPORT;
495 			return -1;
496 		}
497 		who = (struct sockaddr *)&addr;
498 	}
499 	iov[0].iov_base = (char *)data;
500 	iov[0].iov_len = len;
501 	iov[1].iov_base = NULL;
502 	iov[1].iov_len = 0;
503 
504 	if (who) {
505 		msg.msg_name = (caddr_t)who;
506 		msg.msg_namelen = who->sa_len;
507 	} else {
508 		msg.msg_name = (caddr_t)NULL;
509 		msg.msg_namelen = 0;
510 	}
511 	msg.msg_iov = iov;
512 	msg.msg_iovlen = 1;
513 	msg.msg_control = (caddr_t)controlVector;
514 
515 	cmsg = (struct cmsghdr *)controlVector;
516 
517 	cmsg->cmsg_level = IPPROTO_SCTP;
518 	cmsg->cmsg_type = SCTP_SNDRCV;
519 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
520 	s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
521 
522 	s_info->sinfo_stream = stream_no;
523 	s_info->sinfo_ssn = 0;
524 	s_info->sinfo_flags = flags;
525 	s_info->sinfo_ppid = ppid;
526 	s_info->sinfo_context = context;
527 	s_info->sinfo_assoc_id = 0;
528 	s_info->sinfo_timetolive = timetolive;
529 	errno = 0;
530 	msg.msg_controllen = cmsg->cmsg_len;
531 	sz = sendmsg(s, &msg, 0);
532 	return (sz);
533 #endif
534 }
535 
536 
537 sctp_assoc_t
538 sctp_getassocid(int sd, struct sockaddr *sa)
539 {
540 	struct sctp_paddrparams sp;
541 	socklen_t siz;
542 
543 	/* First get the assoc id */
544 	siz = sizeof(struct sctp_paddrparams);
545 	memset(&sp, 0, sizeof(sp));
546 	memcpy((caddr_t)&sp.spp_address, sa, sa->sa_len);
547 	errno = 0;
548 	if (getsockopt(sd, IPPROTO_SCTP,
549 	    SCTP_PEER_ADDR_PARAMS, &sp, &siz) != 0) {
550 		return ((sctp_assoc_t) 0);
551 	}
552 	/* We depend on the fact that 0 can never be returned */
553 	return (sp.spp_assoc_id);
554 }
555 
556 ssize_t
557 sctp_send(int sd, const void *data, size_t len,
558     const struct sctp_sndrcvinfo *sinfo,
559     int flags)
560 {
561 
562 #ifdef SYS_sctp_generic_sendmsg
563 	struct sockaddr *to = NULL;
564 
565 	return (syscall(SYS_sctp_generic_sendmsg, sd,
566 	    data, len, to, 0, sinfo, flags));
567 #else
568 	ssize_t sz;
569 	struct msghdr msg;
570 	struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
571 	struct sctp_sndrcvinfo *s_info;
572 	char controlVector[SCTP_CONTROL_VEC_SIZE_SND];
573 	struct cmsghdr *cmsg;
574 
575 	if (sinfo == NULL) {
576 		return (EINVAL);
577 	}
578 	iov[0].iov_base = (char *)data;
579 	iov[0].iov_len = len;
580 	iov[1].iov_base = NULL;
581 	iov[1].iov_len = 0;
582 
583 	msg.msg_name = 0;
584 	msg.msg_namelen = 0;
585 	msg.msg_iov = iov;
586 	msg.msg_iovlen = 1;
587 	msg.msg_control = (caddr_t)controlVector;
588 
589 	cmsg = (struct cmsghdr *)controlVector;
590 
591 	cmsg->cmsg_level = IPPROTO_SCTP;
592 	cmsg->cmsg_type = SCTP_SNDRCV;
593 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
594 	s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
595 	/* copy in the data */
596 	*s_info = *sinfo;
597 	errno = 0;
598 	msg.msg_controllen = cmsg->cmsg_len;
599 	sz = sendmsg(sd, &msg, flags);
600 	return (sz);
601 #endif
602 }
603 
604 
605 
606 ssize_t
607 sctp_sendx(int sd, const void *msg, size_t msg_len,
608     struct sockaddr *addrs, int addrcnt,
609     struct sctp_sndrcvinfo *sinfo,
610     int flags)
611 {
612 	ssize_t ret;
613 	int i, cnt, *aa, saved_errno;
614 	char *buf;
615 	int add_len, len, no_end_cx = 0;
616 	struct sockaddr *at;
617 
618 
619 #ifdef SYS_sctp_generic_sendmsg
620 	if (addrcnt < SCTP_SMALL_IOVEC_SIZE) {
621 		socklen_t l;
622 
623 		/*
624 		 * Quick way, we don't need to do a connectx so lets use the
625 		 * syscall directly.
626 		 */
627 		l = addrs->sa_len;
628 		return (syscall(SYS_sctp_generic_sendmsg, sd,
629 		    msg, msg_len, addrs, l, sinfo, flags));
630 	}
631 #endif
632 	len = sizeof(int);
633 	at = addrs;
634 	cnt = 0;
635 	/* validate all the addresses and get the size */
636 	for (i = 0; i < addrcnt; i++) {
637 		if (at->sa_family == AF_INET) {
638 			add_len = sizeof(struct sockaddr_in);
639 		} else if (at->sa_family == AF_INET6) {
640 			add_len = sizeof(struct sockaddr_in6);
641 		} else {
642 			errno = EINVAL;
643 			return (-1);
644 		}
645 		len += add_len;
646 		at = (struct sockaddr *)((caddr_t)at + add_len);
647 		cnt++;
648 	}
649 	/* do we have any? */
650 	if (cnt == 0) {
651 		errno = EINVAL;
652 		return (-1);
653 	}
654 	buf = malloc(len);
655 	if (buf == NULL) {
656 		return (ENOMEM);
657 	}
658 	aa = (int *)buf;
659 	*aa = cnt;
660 	aa++;
661 	memcpy((caddr_t)aa, addrs, (len - sizeof(int)));
662 	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_DELAYED, (void *)buf,
663 	    (socklen_t) len);
664 
665 	free(buf);
666 	if (ret != 0) {
667 		if (errno == EALREADY) {
668 			no_end_cx = 1;;
669 			goto continue_send;
670 		}
671 		return (ret);
672 	}
673 continue_send:
674 	sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
675 	if (sinfo->sinfo_assoc_id == 0) {
676 		printf("Huh, can't get associd? TSNH!\n");
677 		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
678 		    (socklen_t) addrs->sa_len);
679 		errno = ENOENT;
680 		return (-1);
681 	}
682 	ret = sctp_send(sd, msg, msg_len, sinfo, flags);
683 	saved_errno = errno;
684 	if (no_end_cx == 0)
685 		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
686 		    (socklen_t) addrs->sa_len);
687 
688 	errno = saved_errno;
689 	return (ret);
690 }
691 
692 ssize_t
693 sctp_sendmsgx(int sd,
694     const void *msg,
695     size_t len,
696     struct sockaddr *addrs,
697     int addrcnt,
698     u_int32_t ppid,
699     u_int32_t flags,
700     u_int16_t stream_no,
701     u_int32_t timetolive,
702     u_int32_t context)
703 {
704 	struct sctp_sndrcvinfo sinfo;
705 
706 	memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
707 	sinfo.sinfo_ppid = ppid;
708 	sinfo.sinfo_flags = flags;
709 	sinfo.sinfo_ssn = stream_no;
710 	sinfo.sinfo_timetolive = timetolive;
711 	sinfo.sinfo_context = context;
712 	return sctp_sendx(sd, msg, len, addrs, addrcnt, &sinfo, 0);
713 }
714 
715 ssize_t
716 sctp_recvmsg(int s,
717     void *dbuf,
718     size_t len,
719     struct sockaddr *from,
720     socklen_t * fromlen,
721     struct sctp_sndrcvinfo *sinfo,
722     int *msg_flags)
723 {
724 #ifdef SYS_sctp_generic_recvmsg
725 	struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
726 
727 	iov[0].iov_base = dbuf;
728 	iov[0].iov_len = len;
729 	return (syscall(SYS_sctp_generic_recvmsg, s,
730 	    iov, 1, from, fromlen, sinfo, msg_flags));
731 #else
732 	struct sctp_sndrcvinfo *s_info;
733 	ssize_t sz;
734 	int sinfo_found = 0;
735 	struct msghdr msg;
736 	struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
737 	char controlVector[SCTP_CONTROL_VEC_SIZE_RCV];
738 	struct cmsghdr *cmsg;
739 
740 	if (msg_flags == NULL) {
741 		errno = EINVAL;
742 		return (-1);
743 	}
744 	msg.msg_flags = 0;
745 	iov[0].iov_base = dbuf;
746 	iov[0].iov_len = len;
747 	iov[1].iov_base = NULL;
748 	iov[1].iov_len = 0;
749 	msg.msg_name = (caddr_t)from;
750 	if (fromlen == NULL)
751 		msg.msg_namelen = 0;
752 	else
753 		msg.msg_namelen = *fromlen;
754 	msg.msg_iov = iov;
755 	msg.msg_iovlen = 1;
756 	msg.msg_control = (caddr_t)controlVector;
757 	msg.msg_controllen = sizeof(controlVector);
758 	errno = 0;
759 	sz = recvmsg(s, &msg, *msg_flags);
760 	if (sz <= 0)
761 		return (sz);
762 
763 	s_info = NULL;
764 	len = sz;
765 	*msg_flags = msg.msg_flags;
766 	if (sinfo)
767 		sinfo->sinfo_assoc_id = 0;
768 
769 	if ((msg.msg_controllen) && sinfo) {
770 		/*
771 		 * parse through and see if we find the sctp_sndrcvinfo (if
772 		 * the user wants it).
773 		 */
774 		cmsg = (struct cmsghdr *)controlVector;
775 		while (cmsg) {
776 			if ((cmsg->cmsg_len == 0) || (cmsg->cmsg_len > msg.msg_controllen)) {
777 				break;
778 			}
779 			if (cmsg->cmsg_level == IPPROTO_SCTP) {
780 				if (cmsg->cmsg_type == SCTP_SNDRCV) {
781 					/* Got it */
782 					s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
783 					/* Copy it to the user */
784 					if (sinfo)
785 						*sinfo = *s_info;
786 					sinfo_found = 1;
787 					break;
788 				} else if (cmsg->cmsg_type == SCTP_EXTRCV) {
789 					/*
790 					 * Got it, presumably the user has
791 					 * asked for this extra info, so the
792 					 * structure holds more room :-D
793 					 */
794 					s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
795 					/* Copy it to the user */
796 					if (sinfo) {
797 						memcpy(sinfo, s_info, sizeof(struct sctp_extrcvinfo));
798 					}
799 					sinfo_found = 1;
800 					break;
801 
802 				}
803 			}
804 			cmsg = CMSG_NXTHDR(&msg, cmsg);
805 		}
806 	}
807 	return (sz);
808 #endif
809 }
810 
811 
812 #if defined(HAVE_SCTP_PEELOFF_SOCKOPT)
813 #include <netinet/sctp_peeloff.h>
814 
815 int
816 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
817 {
818 	struct sctp_peeloff_opt peeloff;
819 	int error;
820 	socklen_t optlen;
821 
822 	/* set in the socket option params */
823 	memset(&peeloff, 0, sizeof(peeloff));
824 	peeloff.s = sd;
825 	peeloff.assoc_id = assoc_id;
826 	optlen = sizeof(peeloff);
827 	error = getsockopt(sd, IPPROTO_SCTP, SCTP_PEELOFF, (void *)&peeloff,
828 	    &optlen);
829 	if (error) {
830 		errno = error;
831 		return (-1);
832 	} else {
833 		return (peeloff.new_sd);
834 	}
835 }
836 
837 #endif
838 
839 #if !defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
840 
841 int
842 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
843 {
844 	/* NOT supported, return invalid sd */
845 	errno = ENOTSUP;
846 	return (-1);
847 }
848 
849 #endif
850 #if defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
851 int
852 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
853 {
854 	return (syscall(SYS_sctp_peeloff, sd, assoc_id));
855 }
856 
857 #endif
858 
859 
860 #undef SCTP_CONTROL_VEC_SIZE_SND
861 #undef SCTP_CONTROL_VEC_SIZE_RCV
862 #undef SCTP_STACK_BUF_SIZE
863 #undef SCTP_SMALL_IOVEC_SIZE
864