sctp_sys_calls.c revision 223154
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 223154 2011-06-16 17:30:50Z 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 ret, 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#else
155	sd = -1;
156#endif
157	if (sd == -1) {
158		return (-1);
159	}
160	ret = getsockopt(sd, IPPROTO_SCTP, SCTP_GET_ADDR_LEN, &av, &siz);
161	close(sd);
162	if (ret == 0) {
163		return ((int)av.assoc_value);
164	} else {
165		return (-1);
166	}
167}
168
169int
170sctp_connectx(int sd, const struct sockaddr *addrs, int addrcnt,
171    sctp_assoc_t * id)
172{
173	char buf[SCTP_STACK_BUF_SIZE];
174	int i, ret, cnt, *aa;
175	char *cpto;
176	const struct sockaddr *at;
177	sctp_assoc_t *p_id;
178	size_t len = sizeof(int);
179
180	/* validate the address count and list */
181	if ((addrs == NULL) || (addrcnt <= 0)) {
182		errno = EINVAL;
183		return (-1);
184	}
185	at = addrs;
186	cnt = 0;
187	cpto = ((caddr_t)buf + sizeof(int));
188	/* validate all the addresses and get the size */
189	for (i = 0; i < addrcnt; i++) {
190		if (at->sa_family == AF_INET) {
191			if (at->sa_len != sizeof(struct sockaddr_in)) {
192				errno = EINVAL;
193				return (-1);
194			}
195			memcpy(cpto, at, at->sa_len);
196			cpto = ((caddr_t)cpto + at->sa_len);
197			len += at->sa_len;
198		} else if (at->sa_family == AF_INET6) {
199			if (at->sa_len != sizeof(struct sockaddr_in6)) {
200				errno = EINVAL;
201				return (-1);
202			}
203			if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) {
204				len += sizeof(struct sockaddr_in);
205				in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at);
206				cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
207				len += sizeof(struct sockaddr_in);
208			} else {
209				memcpy(cpto, at, at->sa_len);
210				cpto = ((caddr_t)cpto + at->sa_len);
211				len += at->sa_len;
212			}
213		} else {
214			errno = EINVAL;
215			return (-1);
216		}
217		if (len > (sizeof(buf) - sizeof(int))) {
218			/* Never enough memory */
219			errno = E2BIG;
220			return (-1);
221		}
222		at = (struct sockaddr *)((caddr_t)at + at->sa_len);
223		cnt++;
224	}
225	/* do we have any? */
226	if (cnt == 0) {
227		errno = EINVAL;
228		return (-1);
229	}
230	aa = (int *)buf;
231	*aa = cnt;
232	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf,
233	    (socklen_t) len);
234	if ((ret == 0) && id) {
235		p_id = (sctp_assoc_t *) buf;
236		*id = *p_id;
237	}
238	return (ret);
239}
240
241int
242sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
243{
244	struct sctp_getaddresses *gaddrs;
245	struct sockaddr *sa;
246	struct sockaddr_in *sin;
247	struct sockaddr_in6 *sin6;
248	int i, sz, argsz;
249	uint16_t sport = 0;
250
251	/* validate the flags */
252	if ((flags != SCTP_BINDX_ADD_ADDR) &&
253	    (flags != SCTP_BINDX_REM_ADDR)) {
254		errno = EFAULT;
255		return (-1);
256	}
257	/* validate the address count and list */
258	if ((addrcnt <= 0) || (addrs == NULL)) {
259		errno = EINVAL;
260		return (-1);
261	}
262	argsz = (sizeof(struct sockaddr_storage) +
263	    sizeof(struct sctp_getaddresses));
264	gaddrs = (struct sctp_getaddresses *)calloc(1, argsz);
265	if (gaddrs == NULL) {
266		errno = ENOMEM;
267		return (-1);
268	}
269	/* First pre-screen the addresses */
270	sa = addrs;
271	for (i = 0; i < addrcnt; i++) {
272		sz = sa->sa_len;
273		if (sa->sa_family == AF_INET) {
274			if (sa->sa_len != sizeof(struct sockaddr_in))
275				goto out_error;
276			sin = (struct sockaddr_in *)sa;
277			if (sin->sin_port) {
278				/* non-zero port, check or save */
279				if (sport) {
280					/* Check against our port */
281					if (sport != sin->sin_port) {
282						goto out_error;
283					}
284				} else {
285					/* save off the port */
286					sport = sin->sin_port;
287				}
288			}
289		} else if (sa->sa_family == AF_INET6) {
290			if (sa->sa_len != sizeof(struct sockaddr_in6))
291				goto out_error;
292			sin6 = (struct sockaddr_in6 *)sa;
293			if (sin6->sin6_port) {
294				/* non-zero port, check or save */
295				if (sport) {
296					/* Check against our port */
297					if (sport != sin6->sin6_port) {
298						goto out_error;
299					}
300				} else {
301					/* save off the port */
302					sport = sin6->sin6_port;
303				}
304			}
305		} else {
306			/* invalid address family specified */
307			goto out_error;
308		}
309
310		sa = (struct sockaddr *)((caddr_t)sa + sz);
311	}
312	sa = addrs;
313	/*
314	 * Now if there was a port mentioned, assure that the first address
315	 * has that port to make sure it fails or succeeds correctly.
316	 */
317	if (sport) {
318		sin = (struct sockaddr_in *)sa;
319		sin->sin_port = sport;
320	}
321	for (i = 0; i < addrcnt; i++) {
322		sz = sa->sa_len;
323		if (sa->sa_family == AF_INET) {
324			if (sa->sa_len != sizeof(struct sockaddr_in))
325				goto out_error;
326		} else if (sa->sa_family == AF_INET6) {
327			if (sa->sa_len != sizeof(struct sockaddr_in6))
328				goto out_error;
329		} else {
330			/* invalid address family specified */
331	out_error:
332			free(gaddrs);
333			errno = EINVAL;
334			return (-1);
335		}
336		memset(gaddrs, 0, argsz);
337		gaddrs->sget_assoc_id = 0;
338		memcpy(gaddrs->addr, sa, sz);
339		if (setsockopt(sd, IPPROTO_SCTP, flags, gaddrs,
340		    (socklen_t) argsz) != 0) {
341			free(gaddrs);
342			return (-1);
343		}
344		sa = (struct sockaddr *)((caddr_t)sa + sz);
345	}
346	free(gaddrs);
347	return (0);
348}
349
350
351int
352sctp_opt_info(int sd, sctp_assoc_t id, int opt, void *arg, socklen_t * size)
353{
354	if (arg == NULL) {
355		errno = EINVAL;
356		return (-1);
357	}
358	switch (opt) {
359	case SCTP_RTOINFO:
360		((struct sctp_rtoinfo *)arg)->srto_assoc_id = id;
361		break;
362	case SCTP_ASSOCINFO:
363		((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
364		break;
365	case SCTP_DEFAULT_SEND_PARAM:
366		((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
367		break;
368	case SCTP_SET_PEER_PRIMARY_ADDR:
369		((struct sctp_setpeerprim *)arg)->sspp_assoc_id = id;
370		break;
371	case SCTP_PRIMARY_ADDR:
372		((struct sctp_setprim *)arg)->ssp_assoc_id = id;
373		break;
374	case SCTP_PEER_ADDR_PARAMS:
375		((struct sctp_paddrparams *)arg)->spp_assoc_id = id;
376		break;
377	case SCTP_MAXSEG:
378		((struct sctp_assoc_value *)arg)->assoc_id = id;
379		break;
380	case SCTP_AUTH_KEY:
381		((struct sctp_authkey *)arg)->sca_assoc_id = id;
382		break;
383	case SCTP_AUTH_ACTIVE_KEY:
384		((struct sctp_authkeyid *)arg)->scact_assoc_id = id;
385		break;
386	case SCTP_DELAYED_SACK:
387		((struct sctp_sack_info *)arg)->sack_assoc_id = id;
388		break;
389	case SCTP_CONTEXT:
390		((struct sctp_assoc_value *)arg)->assoc_id = id;
391		break;
392	case SCTP_STATUS:
393		((struct sctp_status *)arg)->sstat_assoc_id = id;
394		break;
395	case SCTP_GET_PEER_ADDR_INFO:
396		((struct sctp_paddrinfo *)arg)->spinfo_assoc_id = id;
397		break;
398	case SCTP_PEER_AUTH_CHUNKS:
399		((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
400		break;
401	case SCTP_LOCAL_AUTH_CHUNKS:
402		((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
403		break;
404	case SCTP_TIMEOUTS:
405		((struct sctp_timeouts *)arg)->stimo_assoc_id = id;
406		break;
407	case SCTP_EVENT:
408		((struct sctp_event *)arg)->se_assoc_id = id;
409		break;
410	default:
411		break;
412	}
413	return (getsockopt(sd, IPPROTO_SCTP, opt, arg, size));
414}
415
416int
417sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
418{
419	struct sctp_getaddresses *addrs;
420	struct sockaddr *sa;
421	struct sockaddr *re;
422	sctp_assoc_t asoc;
423	caddr_t lim;
424	socklen_t siz;
425	int cnt;
426
427	if (raddrs == NULL) {
428		errno = EFAULT;
429		return (-1);
430	}
431	asoc = id;
432	siz = sizeof(sctp_assoc_t);
433	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_REMOTE_ADDR_SIZE,
434	    &asoc, &siz) != 0) {
435		return (-1);
436	}
437	/* size required is returned in 'asoc' */
438	siz = (size_t)asoc;
439	siz += sizeof(struct sctp_getaddresses);
440	addrs = calloc(1, siz);
441	if (addrs == NULL) {
442		return (-1);
443	}
444	addrs->sget_assoc_id = id;
445	/* Now lets get the array of addresses */
446	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_PEER_ADDRESSES,
447	    addrs, &siz) != 0) {
448		free(addrs);
449		return (-1);
450	}
451	re = (struct sockaddr *)&addrs->addr[0];
452	*raddrs = re;
453	cnt = 0;
454	sa = (struct sockaddr *)&addrs->addr[0];
455	lim = (caddr_t)addrs + siz;
456	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
457		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
458		cnt++;
459	}
460	return (cnt);
461}
462
463void
464sctp_freepaddrs(struct sockaddr *addrs)
465{
466	/* Take away the hidden association id */
467	void *fr_addr;
468
469	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
470	/* Now free it */
471	free(fr_addr);
472}
473
474int
475sctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
476{
477	struct sctp_getaddresses *addrs;
478	struct sockaddr *re;
479	caddr_t lim;
480	struct sockaddr *sa;
481	int size_of_addresses;
482	socklen_t siz;
483	int cnt;
484
485	if (raddrs == NULL) {
486		errno = EFAULT;
487		return (-1);
488	}
489	size_of_addresses = 0;
490	siz = sizeof(int);
491	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDR_SIZE,
492	    &size_of_addresses, &siz) != 0) {
493		errno = ENOMEM;
494		return (-1);
495	}
496	if (size_of_addresses == 0) {
497		errno = ENOTCONN;
498		return (-1);
499	}
500	siz = size_of_addresses + sizeof(struct sockaddr_storage);
501	siz += sizeof(struct sctp_getaddresses);
502	addrs = calloc(1, siz);
503	if (addrs == NULL) {
504		errno = ENOMEM;
505		return (-1);
506	}
507	addrs->sget_assoc_id = id;
508	/* Now lets get the array of addresses */
509	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDRESSES, addrs,
510	    &siz) != 0) {
511		free(addrs);
512		errno = ENOMEM;
513		return (-1);
514	}
515	re = (struct sockaddr *)&addrs->addr[0];
516	*raddrs = re;
517	cnt = 0;
518	sa = (struct sockaddr *)&addrs->addr[0];
519	lim = (caddr_t)addrs + siz;
520	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
521		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
522		cnt++;
523	}
524	return (cnt);
525}
526
527void
528sctp_freeladdrs(struct sockaddr *addrs)
529{
530	/* Take away the hidden association id */
531	void *fr_addr;
532
533	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
534	/* Now free it */
535	free(fr_addr);
536}
537
538
539ssize_t
540sctp_sendmsg(int s,
541    const void *data,
542    size_t len,
543    const struct sockaddr *to,
544    socklen_t tolen,
545    uint32_t ppid,
546    uint32_t flags,
547    uint16_t stream_no,
548    uint32_t timetolive,
549    uint32_t context)
550{
551#ifdef SYS_sctp_generic_sendmsg
552	struct sctp_sndrcvinfo sinfo;
553
554	sinfo.sinfo_ppid = ppid;
555	sinfo.sinfo_flags = flags;
556	sinfo.sinfo_stream = stream_no;
557	sinfo.sinfo_timetolive = timetolive;
558	sinfo.sinfo_context = context;
559	sinfo.sinfo_assoc_id = 0;
560	return (syscall(SYS_sctp_generic_sendmsg, s,
561	    data, len, to, tolen, &sinfo, 0));
562#else
563	ssize_t sz;
564	struct msghdr msg;
565	struct sctp_sndrcvinfo *s_info;
566	struct iovec iov;
567	char controlVector[SCTP_CONTROL_VEC_SIZE_RCV];
568	struct cmsghdr *cmsg;
569	struct sockaddr *who = NULL;
570	union {
571		struct sockaddr_in in;
572		struct sockaddr_in6 in6;
573	}     addr;
574
575	if ((tolen > 0) &&
576	    ((to == NULL) || (tolen < sizeof(struct sockaddr)))) {
577		errno = EINVAL;
578		return -1;
579	}
580	if (to && (tolen > 0)) {
581		if (to->sa_family == AF_INET) {
582			if (tolen != sizeof(struct sockaddr_in)) {
583				errno = EINVAL;
584				return -1;
585			}
586			if ((to->sa_len > 0) &&
587			    (to->sa_len != sizeof(struct sockaddr_in))) {
588				errno = EINVAL;
589				return -1;
590			}
591			memcpy(&addr, to, sizeof(struct sockaddr_in));
592			addr.in.sin_len = sizeof(struct sockaddr_in);
593		} else if (to->sa_family == AF_INET6) {
594			if (tolen != sizeof(struct sockaddr_in6)) {
595				errno = EINVAL;
596				return -1;
597			}
598			if ((to->sa_len > 0) &&
599			    (to->sa_len != sizeof(struct sockaddr_in6))) {
600				errno = EINVAL;
601				return -1;
602			}
603			memcpy(&addr, to, sizeof(struct sockaddr_in6));
604			addr.in6.sin6_len = sizeof(struct sockaddr_in6);
605		} else {
606			errno = EAFNOSUPPORT;
607			return -1;
608		}
609		who = (struct sockaddr *)&addr;
610	}
611	iov.iov_base = (char *)data;
612	iov.iov_len = len;
613
614	if (who) {
615		msg.msg_name = (caddr_t)who;
616		msg.msg_namelen = who->sa_len;
617	} else {
618		msg.msg_name = (caddr_t)NULL;
619		msg.msg_namelen = 0;
620	}
621	msg.msg_iov = &iov;
622	msg.msg_iovlen = 1;
623	msg.msg_control = (caddr_t)controlVector;
624
625	cmsg = (struct cmsghdr *)controlVector;
626
627	cmsg->cmsg_level = IPPROTO_SCTP;
628	cmsg->cmsg_type = SCTP_SNDRCV;
629	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
630	s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
631
632	s_info->sinfo_stream = stream_no;
633	s_info->sinfo_ssn = 0;
634	s_info->sinfo_flags = flags;
635	s_info->sinfo_ppid = ppid;
636	s_info->sinfo_context = context;
637	s_info->sinfo_assoc_id = 0;
638	s_info->sinfo_timetolive = timetolive;
639	errno = 0;
640	msg.msg_controllen = cmsg->cmsg_len;
641	sz = sendmsg(s, &msg, 0);
642	return (sz);
643#endif
644}
645
646
647sctp_assoc_t
648sctp_getassocid(int sd, struct sockaddr *sa)
649{
650	struct sctp_paddrinfo sp;
651	socklen_t siz;
652
653	/* First get the assoc id */
654	siz = sizeof(sp);
655	memset(&sp, 0, sizeof(sp));
656	memcpy((caddr_t)&sp.spinfo_address, sa, sa->sa_len);
657	errno = 0;
658	if (getsockopt(sd, IPPROTO_SCTP,
659	    SCTP_GET_PEER_ADDR_INFO, &sp, &siz) != 0) {
660		return ((sctp_assoc_t) 0);
661	}
662	/* We depend on the fact that 0 can never be returned */
663	return (sp.spinfo_assoc_id);
664}
665
666ssize_t
667sctp_send(int sd, const void *data, size_t len,
668    const struct sctp_sndrcvinfo *sinfo,
669    int flags)
670{
671
672#ifdef SYS_sctp_generic_sendmsg
673	struct sockaddr *to = NULL;
674
675	return (syscall(SYS_sctp_generic_sendmsg, sd,
676	    data, len, to, 0, sinfo, flags));
677#else
678	ssize_t sz;
679	struct msghdr msg;
680	struct iovec iov;
681	struct sctp_sndrcvinfo *s_info;
682	char controlVector[SCTP_CONTROL_VEC_SIZE_SND];
683	struct cmsghdr *cmsg;
684
685	if (sinfo == NULL) {
686		errno = EINVAL;
687		return (-1);
688	}
689	iov.iov_base = (char *)data;
690	iov.iov_len = len;
691
692	msg.msg_name = 0;
693	msg.msg_namelen = 0;
694	msg.msg_iov = &iov;
695	msg.msg_iovlen = 1;
696	msg.msg_control = (caddr_t)controlVector;
697
698	cmsg = (struct cmsghdr *)controlVector;
699
700	cmsg->cmsg_level = IPPROTO_SCTP;
701	cmsg->cmsg_type = SCTP_SNDRCV;
702	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
703	s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
704	/* copy in the data */
705	*s_info = *sinfo;
706	errno = 0;
707	msg.msg_controllen = cmsg->cmsg_len;
708	sz = sendmsg(sd, &msg, flags);
709	return (sz);
710#endif
711}
712
713
714
715ssize_t
716sctp_sendx(int sd, const void *msg, size_t msg_len,
717    struct sockaddr *addrs, int addrcnt,
718    struct sctp_sndrcvinfo *sinfo,
719    int flags)
720{
721	struct sctp_sndrcvinfo __sinfo;
722	ssize_t ret;
723	int i, cnt, *aa, saved_errno;
724	char *buf;
725	int add_len, len, no_end_cx = 0;
726	struct sockaddr *at;
727
728	if (addrs == NULL) {
729		errno = EINVAL;
730		return (-1);
731	}
732#ifdef SYS_sctp_generic_sendmsg
733	if (addrcnt == 1) {
734		socklen_t l;
735
736		/*
737		 * Quick way, we don't need to do a connectx so lets use the
738		 * syscall directly.
739		 */
740		l = addrs->sa_len;
741		return (syscall(SYS_sctp_generic_sendmsg, sd,
742		    msg, msg_len, addrs, l, sinfo, flags));
743	}
744#endif
745
746	len = sizeof(int);
747	at = addrs;
748	cnt = 0;
749	/* validate all the addresses and get the size */
750	for (i = 0; i < addrcnt; i++) {
751		if (at->sa_family == AF_INET) {
752			add_len = sizeof(struct sockaddr_in);
753		} else if (at->sa_family == AF_INET6) {
754			add_len = sizeof(struct sockaddr_in6);
755		} else {
756			errno = EINVAL;
757			return (-1);
758		}
759		len += add_len;
760		at = (struct sockaddr *)((caddr_t)at + add_len);
761		cnt++;
762	}
763	/* do we have any? */
764	if (cnt == 0) {
765		errno = EINVAL;
766		return (-1);
767	}
768	buf = malloc(len);
769	if (buf == NULL) {
770		return (-1);
771	}
772	aa = (int *)buf;
773	*aa = cnt;
774	aa++;
775	memcpy((caddr_t)aa, addrs, (len - sizeof(int)));
776	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_DELAYED, (void *)buf,
777	    (socklen_t) len);
778
779	free(buf);
780	if (ret != 0) {
781		if (errno == EALREADY) {
782			no_end_cx = 1;
783			goto continue_send;
784		}
785		return (ret);
786	}
787continue_send:
788	if (sinfo == NULL) {
789		sinfo = &__sinfo;
790		memset(&__sinfo, 0, sizeof(__sinfo));
791	}
792	sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
793	if (sinfo->sinfo_assoc_id == 0) {
794		printf("Huh, can't get associd? TSNH!\n");
795		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
796		    (socklen_t) addrs->sa_len);
797		errno = ENOENT;
798		return (-1);
799	}
800	ret = sctp_send(sd, msg, msg_len, sinfo, flags);
801	saved_errno = errno;
802	if (no_end_cx == 0)
803		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
804		    (socklen_t) addrs->sa_len);
805
806	errno = saved_errno;
807	return (ret);
808}
809
810ssize_t
811sctp_sendmsgx(int sd,
812    const void *msg,
813    size_t len,
814    struct sockaddr *addrs,
815    int addrcnt,
816    uint32_t ppid,
817    uint32_t flags,
818    uint16_t stream_no,
819    uint32_t timetolive,
820    uint32_t context)
821{
822	struct sctp_sndrcvinfo sinfo;
823
824	memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
825	sinfo.sinfo_ppid = ppid;
826	sinfo.sinfo_flags = flags;
827	sinfo.sinfo_ssn = stream_no;
828	sinfo.sinfo_timetolive = timetolive;
829	sinfo.sinfo_context = context;
830	return sctp_sendx(sd, msg, len, addrs, addrcnt, &sinfo, 0);
831}
832
833ssize_t
834sctp_recvmsg(int s,
835    void *dbuf,
836    size_t len,
837    struct sockaddr *from,
838    socklen_t * fromlen,
839    struct sctp_sndrcvinfo *sinfo,
840    int *msg_flags)
841{
842#ifdef SYS_sctp_generic_recvmsg
843	struct iovec iov;
844
845	iov.iov_base = dbuf;
846	iov.iov_len = len;
847	return (syscall(SYS_sctp_generic_recvmsg, s,
848	    &iov, 1, from, fromlen, sinfo, msg_flags));
849#else
850	struct sctp_sndrcvinfo *s_info;
851	ssize_t sz;
852	int sinfo_found = 0;
853	struct msghdr msg;
854	struct iovec iov;
855	char controlVector[SCTP_CONTROL_VEC_SIZE_RCV];
856	struct cmsghdr *cmsg;
857
858	if (msg_flags == NULL) {
859		errno = EINVAL;
860		return (-1);
861	}
862	msg.msg_flags = 0;
863	iov.iov_base = dbuf;
864	iov.iov_len = len;
865	msg.msg_name = (caddr_t)from;
866	if (fromlen == NULL)
867		msg.msg_namelen = 0;
868	else
869		msg.msg_namelen = *fromlen;
870	msg.msg_iov = &iov;
871	msg.msg_iovlen = 1;
872	msg.msg_control = (caddr_t)controlVector;
873	msg.msg_controllen = sizeof(controlVector);
874	errno = 0;
875	sz = recvmsg(s, &msg, *msg_flags);
876	*msg_flags = msg.msg_flags;
877	if (sz <= 0) {
878		return (sz);
879	}
880	s_info = NULL;
881	len = sz;
882	if (sinfo) {
883		sinfo->sinfo_assoc_id = 0;
884	}
885	if ((msg.msg_controllen) && sinfo) {
886		/*
887		 * parse through and see if we find the sctp_sndrcvinfo (if
888		 * the user wants it).
889		 */
890		cmsg = (struct cmsghdr *)controlVector;
891		while (cmsg) {
892			if ((cmsg->cmsg_len == 0) || (cmsg->cmsg_len > msg.msg_controllen)) {
893				break;
894			}
895			if (cmsg->cmsg_level == IPPROTO_SCTP) {
896				if (cmsg->cmsg_type == SCTP_SNDRCV) {
897					/* Got it */
898					s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
899					/* Copy it to the user */
900					if (sinfo)
901						*sinfo = *s_info;
902					sinfo_found = 1;
903					break;
904				} else if (cmsg->cmsg_type == SCTP_EXTRCV) {
905					/*
906					 * Got it, presumably the user has
907					 * asked for this extra info, so the
908					 * structure holds more room :-D
909					 */
910					s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
911					/* Copy it to the user */
912					if (sinfo) {
913						memcpy(sinfo, s_info, sizeof(struct sctp_extrcvinfo));
914					}
915					sinfo_found = 1;
916					break;
917
918				}
919			}
920			cmsg = CMSG_NXTHDR(&msg, cmsg);
921		}
922	}
923	return (sz);
924#endif
925}
926
927ssize_t
928sctp_recvv(int sd,
929    const struct iovec *iov,
930    int iovlen,
931    struct sockaddr *from,
932    socklen_t * fromlen,
933    void *info,
934    socklen_t * infolen,
935    unsigned int *infotype,
936    int *flags)
937{
938	char ctlbuf[SCTP_CONTROL_VEC_SIZE_RCV];
939	struct msghdr msg;
940	struct cmsghdr *cmsg;
941	ssize_t n;
942	struct sctp_rcvinfo *rcvinfo;
943	struct sctp_nxtinfo *nxtinfo;
944
945	if (((info != NULL) && (infolen == NULL)) |
946	    ((info == NULL) && (infolen != NULL) && (*infolen != 0)) ||
947	    ((info != NULL) && (infotype == NULL))) {
948		errno = EINVAL;
949		return (-1);
950	}
951	if (infotype) {
952		*infotype = SCTP_RECVV_NOINFO;
953	}
954	msg.msg_name = from;
955	if (fromlen == NULL) {
956		msg.msg_namelen = 0;
957	} else {
958		msg.msg_namelen = *fromlen;
959	}
960	msg.msg_iov = (struct iovec *)iov;
961	msg.msg_iovlen = iovlen;
962	msg.msg_control = ctlbuf;
963	msg.msg_controllen = sizeof(ctlbuf);
964	errno = 0;
965	n = recvmsg(sd, &msg, *flags);
966	*flags = msg.msg_flags;
967	if ((n > 0) &&
968	    (msg.msg_controllen > 0) &&
969	    (infotype != NULL) &&
970	    (infolen != NULL) &&
971	    (*infolen > 0)) {
972		rcvinfo = NULL;
973		nxtinfo = NULL;
974		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
975			if (cmsg->cmsg_level != IPPROTO_SCTP) {
976				continue;
977			}
978			if (cmsg->cmsg_type == SCTP_RCVINFO) {
979				rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmsg);
980			}
981			if (cmsg->cmsg_type == SCTP_NXTINFO) {
982				nxtinfo = (struct sctp_nxtinfo *)CMSG_DATA(cmsg);
983			}
984			if (rcvinfo && nxtinfo) {
985				break;
986			}
987		}
988		if (rcvinfo) {
989			if (nxtinfo) {
990				if (*infolen >= sizeof(struct sctp_recvv_rn)) {
991					struct sctp_recvv_rn *rn_info;
992
993					rn_info = (struct sctp_recvv_rn *)info;
994					rn_info->recvv_rcvinfo = *rcvinfo;
995					rn_info->recvv_nxtinfo = *nxtinfo;
996					*infolen = (socklen_t) sizeof(struct sctp_recvv_rn);
997					*infotype = SCTP_RECVV_RN;
998				}
999			} else {
1000				if (*infolen >= sizeof(struct sctp_rcvinfo)) {
1001					memcpy(info, rcvinfo, sizeof(struct sctp_rcvinfo));
1002					*infolen = (socklen_t) sizeof(struct sctp_rcvinfo);
1003					*infotype = SCTP_RECVV_RCVINFO;
1004				}
1005			}
1006		} else if (nxtinfo) {
1007			if (*infolen >= sizeof(struct sctp_rcvinfo)) {
1008				memcpy(info, nxtinfo, sizeof(struct sctp_nxtinfo));
1009				*infolen = (socklen_t) sizeof(struct sctp_nxtinfo);
1010				*infotype = SCTP_RECVV_NXTINFO;
1011			}
1012		}
1013	}
1014	return (n);
1015}
1016
1017ssize_t
1018sctp_sendv(int sd,
1019    const struct iovec *iov, int iovcnt,
1020    struct sockaddr *addrs, int addrcnt,
1021    void *info, socklen_t infolen, unsigned int infotype,
1022    int flags)
1023{
1024	ssize_t ret;
1025	int i;
1026	socklen_t addr_len;
1027	struct msghdr msg;
1028	in_port_t port;
1029	struct sctp_sendv_spa *spa_info;
1030	struct cmsghdr *cmsg;
1031	char *cmsgbuf;
1032	struct sockaddr *addr;
1033	struct sockaddr_in *addr_in;
1034	struct sockaddr_in6 *addr_in6;
1035
1036	if ((addrcnt < 0) ||
1037	    (iovcnt < 0) ||
1038	    ((addrs == NULL) && (addrcnt > 0)) ||
1039	    ((addrs != NULL) && (addrcnt == 0)) ||
1040	    ((iov == NULL) && (iovcnt > 0)) ||
1041	    ((iov != NULL) && (iovcnt == 0))) {
1042		errno = EINVAL;
1043		return (-1);
1044	}
1045	cmsgbuf = malloc(CMSG_SPACE(sizeof(struct sctp_sndinfo)) +
1046	    CMSG_SPACE(sizeof(struct sctp_prinfo)) +
1047	    CMSG_SPACE(sizeof(struct sctp_authinfo)) +
1048	    addrcnt * CMSG_SPACE(sizeof(struct in6_addr)));
1049	if (cmsgbuf == NULL) {
1050		errno = ENOBUFS;
1051		return (-1);
1052	}
1053	msg.msg_control = cmsgbuf;
1054	msg.msg_controllen = 0;
1055	cmsg = (struct cmsghdr *)cmsgbuf;
1056	switch (infotype) {
1057	case SCTP_SENDV_NOINFO:
1058		if ((infolen != 0) || (info != NULL)) {
1059			free(cmsgbuf);
1060			errno = EINVAL;
1061			return (-1);
1062		}
1063		break;
1064	case SCTP_SENDV_SNDINFO:
1065		if ((info == NULL) || (infolen < sizeof(struct sctp_sndinfo))) {
1066			free(cmsgbuf);
1067			errno = EINVAL;
1068			return (-1);
1069		}
1070		cmsg->cmsg_level = IPPROTO_SCTP;
1071		cmsg->cmsg_type = SCTP_SNDINFO;
1072		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
1073		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_sndinfo));
1074		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
1075		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
1076		break;
1077	case SCTP_SENDV_PRINFO:
1078		if ((info == NULL) || (infolen < sizeof(struct sctp_prinfo))) {
1079			free(cmsgbuf);
1080			errno = EINVAL;
1081			return (-1);
1082		}
1083		cmsg->cmsg_level = IPPROTO_SCTP;
1084		cmsg->cmsg_type = SCTP_PRINFO;
1085		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
1086		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_prinfo));
1087		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
1088		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
1089		break;
1090	case SCTP_SENDV_AUTHINFO:
1091		if ((info == NULL) || (infolen < sizeof(struct sctp_authinfo))) {
1092			free(cmsgbuf);
1093			errno = EINVAL;
1094			return (-1);
1095		}
1096		cmsg->cmsg_level = IPPROTO_SCTP;
1097		cmsg->cmsg_type = SCTP_AUTHINFO;
1098		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo));
1099		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_authinfo));
1100		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo));
1101		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
1102		break;
1103	case SCTP_SENDV_SPA:
1104		if ((info == NULL) || (infolen < sizeof(struct sctp_sendv_spa))) {
1105			free(cmsgbuf);
1106			errno = EINVAL;
1107			return (-1);
1108		}
1109		spa_info = (struct sctp_sendv_spa *)info;
1110		if (spa_info->sendv_flags & SCTP_SEND_SNDINFO_VALID) {
1111			cmsg->cmsg_level = IPPROTO_SCTP;
1112			cmsg->cmsg_type = SCTP_SNDINFO;
1113			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
1114			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_sndinfo, sizeof(struct sctp_sndinfo));
1115			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
1116			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
1117		}
1118		if (spa_info->sendv_flags & SCTP_SEND_PRINFO_VALID) {
1119			cmsg->cmsg_level = IPPROTO_SCTP;
1120			cmsg->cmsg_type = SCTP_PRINFO;
1121			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
1122			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_prinfo, sizeof(struct sctp_prinfo));
1123			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
1124			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
1125		}
1126		if (spa_info->sendv_flags & SCTP_SEND_AUTHINFO_VALID) {
1127			cmsg->cmsg_level = IPPROTO_SCTP;
1128			cmsg->cmsg_type = SCTP_AUTHINFO;
1129			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo));
1130			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_authinfo, sizeof(struct sctp_authinfo));
1131			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo));
1132			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
1133		}
1134		break;
1135	default:
1136		free(cmsgbuf);
1137		errno = EINVAL;
1138		return (-1);
1139	}
1140	addr = addrs;
1141	msg.msg_name = NULL;
1142	msg.msg_namelen = 0;
1143
1144	for (i = 0; i < addrcnt; i++) {
1145		switch (addr->sa_family) {
1146		case AF_INET:
1147			addr_len = (socklen_t) sizeof(struct sockaddr_in);
1148			addr_in = (struct sockaddr_in *)addr;
1149			if (addr_in->sin_len != addr_len) {
1150				free(cmsgbuf);
1151				errno = EINVAL;
1152				return (-1);
1153			}
1154			if (i == 0) {
1155				port = addr_in->sin_port;
1156			} else {
1157				if (port == addr_in->sin_port) {
1158					cmsg->cmsg_level = IPPROTO_SCTP;
1159					cmsg->cmsg_type = SCTP_DSTADDRV4;
1160					cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
1161					memcpy(CMSG_DATA(cmsg), &addr_in->sin_addr, sizeof(struct in_addr));
1162					msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr));
1163					cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in_addr)));
1164				} else {
1165					free(cmsgbuf);
1166					errno = EINVAL;
1167					return (-1);
1168				}
1169			}
1170			break;
1171		case AF_INET6:
1172			addr_len = (socklen_t) sizeof(struct sockaddr_in6);
1173			addr_in6 = (struct sockaddr_in6 *)addr;
1174			if (addr_in6->sin6_len != addr_len) {
1175				free(cmsgbuf);
1176				errno = EINVAL;
1177				return (-1);
1178			}
1179			if (i == 0) {
1180				port = addr_in6->sin6_port;
1181			} else {
1182				if (port == addr_in6->sin6_port) {
1183					cmsg->cmsg_level = IPPROTO_SCTP;
1184					cmsg->cmsg_type = SCTP_DSTADDRV6;
1185					cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_addr));
1186					memcpy(CMSG_DATA(cmsg), &addr_in6->sin6_addr, sizeof(struct in6_addr));
1187					msg.msg_controllen += CMSG_SPACE(sizeof(struct in6_addr));
1188					cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in6_addr)));
1189				} else {
1190					free(cmsgbuf);
1191					errno = EINVAL;
1192					return (-1);
1193				}
1194			}
1195			break;
1196		default:
1197			free(cmsgbuf);
1198			errno = EINVAL;
1199			return (-1);
1200		}
1201		if (i == 0) {
1202			msg.msg_name = addr;
1203			msg.msg_namelen = addr_len;
1204		}
1205		addr = (struct sockaddr *)((caddr_t)addr + addr_len);
1206	}
1207	if (msg.msg_controllen == 0) {
1208		msg.msg_control = NULL;
1209	}
1210	msg.msg_iov = (struct iovec *)iov;
1211	msg.msg_iovlen = iovcnt;
1212	msg.msg_flags = 0;
1213	ret = sendmsg(sd, &msg, flags);
1214	free(cmsgbuf);
1215	return (ret);
1216}
1217
1218
1219#if !defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
1220
1221int
1222sctp_peeloff(int sd, sctp_assoc_t assoc_id)
1223{
1224	/* NOT supported, return invalid sd */
1225	errno = ENOTSUP;
1226	return (-1);
1227}
1228
1229#endif
1230#if defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
1231int
1232sctp_peeloff(int sd, sctp_assoc_t assoc_id)
1233{
1234	return (syscall(SYS_sctp_peeloff, sd, assoc_id));
1235}
1236
1237#endif
1238
1239
1240#undef SCTP_CONTROL_VEC_SIZE_SND
1241#undef SCTP_CONTROL_VEC_SIZE_RCV
1242#undef SCTP_STACK_BUF_SIZE
1243