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