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