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