xref: /freebsd/lib/libc/net/sctp_sys_calls.c (revision 588ff6c0cc9aaf10ba19080d9f8acbd8be36abf3)
1 /*	$KAME: sctp_sys_calls.c,v 1.9 2004/08/17 06:08:53 itojun Exp $ */
2 
3 /*
4  * Copyright (C) 2002-2006 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 {
169 	char buf[SCTP_STACK_BUF_SIZE];
170 	int i, ret, cnt, *aa;
171 	char *cpto;
172 	const struct sockaddr *at;
173 	size_t len = sizeof(int);
174 
175 	at = addrs;
176 	cnt = 0;
177 	cpto = ((caddr_t)buf + sizeof(int));
178 	/* validate all the addresses and get the size */
179 	for (i = 0; i < addrcnt; i++) {
180 		if (at->sa_family == AF_INET) {
181 			memcpy(cpto, at, at->sa_len);
182 			cpto = ((caddr_t)cpto + at->sa_len);
183 			len += at->sa_len;
184 		} else if (at->sa_family == AF_INET6) {
185 			if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) {
186 				len += sizeof(struct sockaddr_in);
187 				in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at);
188 				cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
189 				len += sizeof(struct sockaddr_in);
190 			} else {
191 				memcpy(cpto, at, at->sa_len);
192 				cpto = ((caddr_t)cpto + at->sa_len);
193 				len += at->sa_len;
194 			}
195 		} else {
196 			errno = EINVAL;
197 			return (-1);
198 		}
199 		if (len > (sizeof(buf) - sizeof(int))) {
200 			/* Never enough memory */
201 			return (E2BIG);
202 		}
203 		at = (struct sockaddr *)((caddr_t)at + at->sa_len);
204 		cnt++;
205 	}
206 	/* do we have any? */
207 	if (cnt == 0) {
208 		errno = EINVAL;
209 		return (-1);
210 	}
211 	aa = (int *)buf;
212 	*aa = cnt;
213 	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf,
214 	    (socklen_t) len);
215 	return (ret);
216 }
217 
218 int
219 sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
220 {
221 	struct sctp_getaddresses *gaddrs;
222 	struct sockaddr *sa;
223 	int i, sz, fam, argsz;
224 
225 	if ((flags != SCTP_BINDX_ADD_ADDR) &&
226 	    (flags != SCTP_BINDX_REM_ADDR)) {
227 		errno = EFAULT;
228 		return (-1);
229 	}
230 	argsz = (sizeof(struct sockaddr_storage) +
231 	    sizeof(struct sctp_getaddresses));
232 	gaddrs = (struct sctp_getaddresses *)calloc(1, argsz);
233 	if (gaddrs == NULL) {
234 		errno = ENOMEM;
235 		return (-1);
236 	}
237 	gaddrs->sget_assoc_id = 0;
238 	sa = addrs;
239 	for (i = 0; i < addrcnt; i++) {
240 		sz = sa->sa_len;
241 		fam = sa->sa_family;
242 		((struct sockaddr_in *)&addrs[i])->sin_port = ((struct sockaddr_in *)sa)->sin_port;
243 		if ((fam != AF_INET) && (fam != AF_INET6)) {
244 			errno = EINVAL;
245 			return (-1);
246 		}
247 		memcpy(gaddrs->addr, sa, sz);
248 		if (setsockopt(sd, IPPROTO_SCTP, flags,
249 		    gaddrs, (socklen_t) argsz) != 0) {
250 			free(gaddrs);
251 			return (-1);
252 		}
253 		memset(gaddrs, 0, argsz);
254 		sa = (struct sockaddr *)((caddr_t)sa + sz);
255 	}
256 	free(gaddrs);
257 	return (0);
258 }
259 
260 
261 int
262 sctp_opt_info(int sd, sctp_assoc_t id, int opt, void *arg, socklen_t * size)
263 {
264 	if (arg == NULL) {
265 		return (EINVAL);
266 	}
267 	if ((opt == SCTP_RTOINFO) ||
268 	    (opt == SCTP_ASSOCINFO) ||
269 	    (opt == SCTP_PRIMARY_ADDR) ||
270 	    (opt == SCTP_SET_PEER_PRIMARY_ADDR) ||
271 	    (opt == SCTP_PEER_ADDR_PARAMS) ||
272 	    (opt == SCTP_STATUS) ||
273 	    (opt == SCTP_GET_PEER_ADDR_INFO) ||
274 	    (opt == SCTP_AUTH_ACTIVE_KEY) ||
275 	    (opt == SCTP_PEER_AUTH_CHUNKS) ||
276 	    (opt == SCTP_LOCAL_AUTH_CHUNKS)) {
277 		*(sctp_assoc_t *) arg = id;
278 		return (getsockopt(sd, IPPROTO_SCTP, opt, arg, size));
279 	} else {
280 		errno = EOPNOTSUPP;
281 		return (-1);
282 	}
283 }
284 
285 int
286 sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
287 {
288 	struct sctp_getaddresses *addrs;
289 	struct sockaddr *sa;
290 	struct sockaddr *re;
291 	sctp_assoc_t asoc;
292 	caddr_t lim;
293 	socklen_t siz;
294 	int cnt;
295 
296 	if (raddrs == NULL) {
297 		errno = EFAULT;
298 		return (-1);
299 	}
300 	asoc = id;
301 	siz = sizeof(sctp_assoc_t);
302 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_REMOTE_ADDR_SIZE,
303 	    &asoc,  &siz) != 0) {
304 		errno = ENOMEM;
305 		return (-1);
306 	}
307 	/* size required is returned in 'asoc' */
308 	siz = (size_t)asoc;
309 	siz += sizeof(struct sctp_getaddresses);
310 	addrs = calloc(1, siz);
311 	if (addrs == NULL) {
312 		errno = ENOMEM;
313 		return (-1);
314 	}
315 	memset(addrs, 0, siz);
316 	addrs->sget_assoc_id = id;
317 	/* Now lets get the array of addresses */
318 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_PEER_ADDRESSES,
319 	    addrs,  &siz) != 0) {
320 		free(addrs);
321 		errno = ENOMEM;
322 		return (-1);
323 	}
324 	re = (struct sockaddr *)&addrs->addr[0];
325 	*raddrs = re;
326 	cnt = 0;
327 	sa = (struct sockaddr *)&addrs->addr[0];
328 	lim = (caddr_t)addrs + siz;
329 	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
330 		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
331 		cnt++;
332 	}
333 	return (cnt);
334 }
335 
336 void
337 sctp_freepaddrs(struct sockaddr *addrs)
338 {
339 	/* Take away the hidden association id */
340 	void *fr_addr;
341 
342 	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
343 	/* Now free it */
344 	free(fr_addr);
345 }
346 
347 int
348 sctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
349 {
350 	struct sctp_getaddresses *addrs;
351 	struct sockaddr *re;
352 	caddr_t lim;
353 	struct sockaddr *sa;
354 	int size_of_addresses;
355 	socklen_t siz;
356 	int cnt;
357 
358 	if (raddrs == NULL) {
359 		errno = EFAULT;
360 		return (-1);
361 	}
362 	size_of_addresses = 0;
363 	siz = sizeof(int);
364 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDR_SIZE,
365 	    &size_of_addresses, &siz) != 0) {
366 		errno = ENOMEM;
367 		return (-1);
368 	}
369 	if (size_of_addresses == 0) {
370 		errno = ENOTCONN;
371 		return (-1);
372 	}
373 	siz = size_of_addresses + sizeof(struct sockaddr_storage);
374 	siz += sizeof(struct sctp_getaddresses);
375 	addrs = calloc(1, siz);
376 	if (addrs == NULL) {
377 		errno = ENOMEM;
378 		return (-1);
379 	}
380 	memset(addrs, 0, siz);
381 	addrs->sget_assoc_id = id;
382 	/* Now lets get the array of addresses */
383 	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDRESSES, addrs,
384 	    &siz) != 0) {
385 		free(addrs);
386 		errno = ENOMEM;
387 		return (-1);
388 	}
389 	re = (struct sockaddr *)&addrs->addr[0];
390 	*raddrs = re;
391 	cnt = 0;
392 	sa = (struct sockaddr *)&addrs->addr[0];
393 	lim = (caddr_t)addrs + siz;
394 	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
395 		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
396 		cnt++;
397 	}
398 	return (cnt);
399 }
400 
401 void
402 sctp_freeladdrs(struct sockaddr *addrs)
403 {
404 	/* Take away the hidden association id */
405 	void *fr_addr;
406 
407 	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
408 	/* Now free it */
409 	free(fr_addr);
410 }
411 
412 
413 ssize_t
414 sctp_sendmsg(int s,
415     const void *data,
416     size_t len,
417     const struct sockaddr *to,
418     socklen_t tolen __attribute__((unused)),
419     u_int32_t ppid,
420     u_int32_t flags,
421     u_int16_t stream_no,
422     u_int32_t timetolive,
423     u_int32_t context)
424 {
425 #ifdef SYS_sctp_generic_sendmsg
426 	struct sctp_sndrcvinfo sinfo;
427 
428 	sinfo.sinfo_ppid = ppid;
429 	sinfo.sinfo_flags = flags;
430 	sinfo.sinfo_stream = stream_no;
431 	sinfo.sinfo_timetolive = timetolive;
432 	sinfo.sinfo_context = context;
433 	sinfo.sinfo_assoc_id = 0;
434 	return (syscall(SYS_sctp_generic_sendmsg, s,
435 	    data, len, to, tolen, &sinfo, 0));
436 #else
437 
438 	ssize_t sz;
439 	struct msghdr msg;
440 	struct sctp_sndrcvinfo *s_info;
441 	struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
442 	char controlVector[SCTP_CONTROL_VEC_SIZE_RCV];
443 	struct cmsghdr *cmsg;
444 	struct sockaddr *who = NULL;
445 	union {
446 		struct sockaddr_in in;
447 		struct sockaddr_in6 in6;
448 	}     addr;
449 
450 /*
451   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",
452   s,
453   (u_int)data,
454   (int)len,
455   (u_int)to,
456   (int)tolen,
457   ppid, flags,
458   (int)stream_no,
459   (int)timetolive,
460   (u_int)context);
461   fflush(io);
462 */
463 	if (to) {
464 		if (to->sa_len == 0) {
465 			/*
466 			 * For the lazy app, that did not set sa_len, we
467 			 * attempt to set for them.
468 			 */
469 			if (to->sa_family == AF_INET) {
470 				memcpy(&addr, to, sizeof(struct sockaddr_in));
471 				addr.in.sin_len = sizeof(struct sockaddr_in);
472 			} else if (to->sa_family == AF_INET6) {
473 				memcpy(&addr, to, sizeof(struct sockaddr_in6));
474 				addr.in6.sin6_len = sizeof(struct sockaddr_in6);
475 			}
476 		} else {
477 			memcpy(&addr, to, to->sa_len);
478 		}
479 		who = (struct sockaddr *)&addr;
480 	}
481 	iov[0].iov_base = (char *)data;
482 	iov[0].iov_len = len;
483 	iov[1].iov_base = NULL;
484 	iov[1].iov_len = 0;
485 
486 	if (to) {
487 		msg.msg_name = (caddr_t)who;
488 		msg.msg_namelen = who->sa_len;
489 	} else {
490 		msg.msg_name = (caddr_t)NULL;
491 		msg.msg_namelen = 0;
492 	}
493 	msg.msg_iov = iov;
494 	msg.msg_iovlen = 1;
495 	msg.msg_control = (caddr_t)controlVector;
496 
497 	cmsg = (struct cmsghdr *)controlVector;
498 
499 	cmsg->cmsg_level = IPPROTO_SCTP;
500 	cmsg->cmsg_type = SCTP_SNDRCV;
501 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
502 	s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
503 
504 	s_info->sinfo_stream = stream_no;
505 	s_info->sinfo_ssn = 0;
506 	s_info->sinfo_flags = flags;
507 	s_info->sinfo_ppid = ppid;
508 	s_info->sinfo_context = context;
509 	s_info->sinfo_assoc_id = 0;
510 	s_info->sinfo_timetolive = timetolive;
511 	errno = 0;
512 	msg.msg_controllen = cmsg->cmsg_len;
513 	sz = sendmsg(s, &msg, 0);
514 	return (sz);
515 #endif
516 }
517 
518 
519 sctp_assoc_t
520 sctp_getassocid(int sd, struct sockaddr *sa)
521 {
522 	struct sctp_paddrparams sp;
523 	socklen_t siz;
524 
525 	/* First get the assoc id */
526 	siz = sizeof(struct sctp_paddrparams);
527 	memset(&sp, 0, sizeof(sp));
528 	memcpy((caddr_t)&sp.spp_address, sa, sa->sa_len);
529 	errno = 0;
530 	if (getsockopt(sd, IPPROTO_SCTP,
531 	    SCTP_PEER_ADDR_PARAMS, &sp, &siz) != 0) {
532 		return ((sctp_assoc_t) 0);
533 	}
534 	/* We depend on the fact that 0 can never be returned */
535 	return (sp.spp_assoc_id);
536 }
537 
538 ssize_t
539 sctp_send(int sd, const void *data, size_t len,
540     const struct sctp_sndrcvinfo *sinfo,
541     int flags)
542 {
543 
544 #ifdef SYS_sctp_generic_sendmsg
545 	struct sockaddr *to = NULL;
546 
547 	return (syscall(SYS_sctp_generic_sendmsg, sd,
548 	    data, len, to, 0, sinfo, flags));
549 #else
550 	ssize_t sz;
551 	struct msghdr msg;
552 	struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
553 	struct sctp_sndrcvinfo *s_info;
554 	char controlVector[SCTP_CONTROL_VEC_SIZE_SND];
555 	struct cmsghdr *cmsg;
556 
557 	if (sinfo == NULL) {
558 		return (EINVAL);
559 	}
560 	iov[0].iov_base = (char *)data;
561 	iov[0].iov_len = len;
562 	iov[1].iov_base = NULL;
563 	iov[1].iov_len = 0;
564 
565 	msg.msg_name = 0;
566 	msg.msg_namelen = 0;
567 	msg.msg_iov = iov;
568 	msg.msg_iovlen = 1;
569 	msg.msg_control = (caddr_t)controlVector;
570 
571 	cmsg = (struct cmsghdr *)controlVector;
572 
573 	cmsg->cmsg_level = IPPROTO_SCTP;
574 	cmsg->cmsg_type = SCTP_SNDRCV;
575 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
576 	s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
577 	/* copy in the data */
578 	*s_info = *sinfo;
579 	errno = 0;
580 	msg.msg_controllen = cmsg->cmsg_len;
581 	sz = sendmsg(sd, &msg, flags);
582 	return (sz);
583 #endif
584 }
585 
586 
587 
588 ssize_t
589 sctp_sendx(int sd, const void *msg, size_t msg_len,
590     struct sockaddr *addrs, int addrcnt,
591     struct sctp_sndrcvinfo *sinfo,
592     int flags)
593 {
594 	ssize_t ret;
595 	int i, cnt, *aa, saved_errno;
596 	char *buf;
597 	int add_len, len, no_end_cx = 0;
598 	struct sockaddr *at;
599 
600 
601 #ifdef SYS_sctp_generic_sendmsg
602 	if (addrcnt < SCTP_SMALL_IOVEC_SIZE) {
603 		socklen_t l;
604 
605 		/*
606 		 * Quick way, we don't need to do a connectx so lets use the
607 		 * syscall directly.
608 		 */
609 		l = addrs->sa_len;
610 		return (syscall(SYS_sctp_generic_sendmsg, sd,
611 		    msg, msg_len, addrs, l, sinfo, flags));
612 	}
613 #endif
614 	len = sizeof(int);
615 	at = addrs;
616 	cnt = 0;
617 	/* validate all the addresses and get the size */
618 	for (i = 0; i < addrcnt; i++) {
619 		if (at->sa_family == AF_INET) {
620 			add_len = sizeof(struct sockaddr_in);
621 		} else if (at->sa_family == AF_INET6) {
622 			add_len = sizeof(struct sockaddr_in6);
623 		} else {
624 			errno = EINVAL;
625 			return (-1);
626 		}
627 		len += add_len;
628 		at = (struct sockaddr *)((caddr_t)at + add_len);
629 		cnt++;
630 	}
631 	/* do we have any? */
632 	if (cnt == 0) {
633 		errno = EINVAL;
634 		return (-1);
635 	}
636 	buf = malloc(len);
637 	if (buf == NULL) {
638 		return (ENOMEM);
639 	}
640 	aa = (int *)buf;
641 	*aa = cnt;
642 	aa++;
643 	memcpy((caddr_t)aa, addrs, (len - sizeof(int)));
644 	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_DELAYED, (void *)buf,
645 	    (socklen_t) len);
646 
647 	free(buf);
648 	if (ret != 0) {
649 		if (errno == EALREADY) {
650 			no_end_cx = 1;;
651 			goto continue_send;
652 		}
653 		return (ret);
654 	}
655 continue_send:
656 	sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
657 	if (sinfo->sinfo_assoc_id == 0) {
658 		printf("Huh, can't get associd? TSNH!\n");
659 		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
660 		    (socklen_t) addrs->sa_len);
661 		errno = ENOENT;
662 		return (-1);
663 	}
664 	ret = sctp_send(sd, msg, msg_len, sinfo, flags);
665 	saved_errno = errno;
666 	if (no_end_cx == 0)
667 		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
668 		    (socklen_t) addrs->sa_len);
669 
670 	errno = saved_errno;
671 	return (ret);
672 }
673 
674 ssize_t
675 sctp_sendmsgx(int sd,
676     const void *msg,
677     size_t len,
678     struct sockaddr *addrs,
679     int addrcnt,
680     u_int32_t ppid,
681     u_int32_t flags,
682     u_int16_t stream_no,
683     u_int32_t timetolive,
684     u_int32_t context)
685 {
686 	struct sctp_sndrcvinfo sinfo;
687 
688 	memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
689 	sinfo.sinfo_ppid = ppid;
690 	sinfo.sinfo_flags = flags;
691 	sinfo.sinfo_ssn = stream_no;
692 	sinfo.sinfo_timetolive = timetolive;
693 	sinfo.sinfo_context = context;
694 	return sctp_sendx(sd, msg, len, addrs, addrcnt, &sinfo, 0);
695 }
696 
697 ssize_t
698 sctp_recvmsg(int s,
699     void *dbuf,
700     size_t len,
701     struct sockaddr *from,
702     socklen_t * fromlen,
703     struct sctp_sndrcvinfo *sinfo,
704     int *msg_flags)
705 {
706 
707 #ifdef SYS_sctp_generic_recvmsg
708 	struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
709 
710 	iov[0].iov_base = dbuf;
711 	iov[0].iov_len = len;
712 	return (syscall(SYS_sctp_generic_recvmsg, s,
713 	    iov, 1, from, fromlen, sinfo, msg_flags));
714 #else
715 	struct sctp_sndrcvinfo *s_info;
716 	ssize_t sz;
717 	int sinfo_found = 0;
718 	struct msghdr msg;
719 	struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
720 	char controlVector[SCTP_CONTROL_VEC_SIZE_RCV];
721 	struct cmsghdr *cmsg;
722 
723 	if (msg_flags == NULL) {
724 		errno = EINVAL;
725 		return (-1);
726 	}
727 	msg.msg_flags = 0;
728 	iov[0].iov_base = dbuf;
729 	iov[0].iov_len = len;
730 	iov[1].iov_base = NULL;
731 	iov[1].iov_len = 0;
732 	msg.msg_name = (caddr_t)from;
733 	if (fromlen == NULL)
734 		msg.msg_namelen = 0;
735 	else
736 		msg.msg_namelen = *fromlen;
737 	msg.msg_iov = iov;
738 	msg.msg_iovlen = 1;
739 	msg.msg_control = (caddr_t)controlVector;
740 	msg.msg_controllen = sizeof(controlVector);
741 	errno = 0;
742 	sz = recvmsg(s, &msg, 0);
743 	if (sz <= 0)
744 		return (sz);
745 
746 	s_info = NULL;
747 	len = sz;
748 	*msg_flags = msg.msg_flags;
749 	if (sinfo)
750 		sinfo->sinfo_assoc_id = 0;
751 
752 	if ((msg.msg_controllen) && sinfo) {
753 		/*
754 		 * parse through and see if we find the sctp_sndrcvinfo (if
755 		 * the user wants it).
756 		 */
757 		cmsg = (struct cmsghdr *)controlVector;
758 		while (cmsg) {
759 			if ((cmsg->cmsg_len == 0) || (cmsg->cmsg_len > msg.msg_controllen)) {
760 				break;
761 			}
762 			if (cmsg->cmsg_level == IPPROTO_SCTP) {
763 				if (cmsg->cmsg_type == SCTP_SNDRCV) {
764 					/* Got it */
765 					s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
766 					/* Copy it to the user */
767 					if (sinfo)
768 						*sinfo = *s_info;
769 					sinfo_found = 1;
770 					break;
771 				} else if (cmsg->cmsg_type == SCTP_EXTRCV) {
772 					/*
773 					 * Got it, presumably the user has
774 					 * asked for this extra info, so the
775 					 * structure holds more room :-D
776 					 */
777 					s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
778 					/* Copy it to the user */
779 					if (sinfo) {
780 						memcpy(sinfo, s_info, sizeof(struct sctp_extrcvinfo));
781 					}
782 					sinfo_found = 1;
783 					break;
784 
785 				}
786 			}
787 			cmsg = CMSG_NXTHDR(&msg, cmsg);
788 		}
789 	}
790 	return (sz);
791 #endif
792 }
793 
794 
795 #if defined(HAVE_SCTP_PEELOFF_SOCKOPT)
796 #include <netinet/sctp_peeloff.h>
797 
798 int
799 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
800 {
801 	struct sctp_peeloff_opt peeloff;
802 	int error;
803 	socklen_t optlen;
804 
805 	/* set in the socket option params */
806 	memset(&peeloff, 0, sizeof(peeloff));
807 	peeloff.s = sd;
808 	peeloff.assoc_id = assoc_id;
809 	optlen = sizeof(peeloff);
810 	error = getsockopt(sd, IPPROTO_SCTP, SCTP_PEELOFF, (void *)&peeloff,
811 	    &optlen);
812 	if (error) {
813 		errno = error;
814 		return (-1);
815 	} else {
816 		return (peeloff.new_sd);
817 	}
818 }
819 
820 #endif
821 
822 #if !defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
823 
824 int
825 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
826 {
827 	/* NOT supported, return invalid sd */
828 	errno = ENOTSUP;
829 	return (-1);
830 }
831 
832 #endif
833 #if defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
834 int
835 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
836 {
837 	return (syscall(SYS_sctp_peeloff, sd, assoc_id));
838 }
839 
840 #endif
841 
842 
843 #undef SCTP_CONTROL_VEC_SIZE_SND
844 #undef SCTP_CONTROL_VEC_SIZE_RCV
845 #undef SCTP_STACK_BUF_SIZE
846 #undef SCTP_SMALL_IOVEC_SIZE
847