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