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