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