1221512Stuexen/*-
2221512Stuexen * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved.
3235827Stuexen * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
4235827Stuexen * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
5165242Srrs *
6165242Srrs * Redistribution and use in source and binary forms, with or without
7221512Stuexen * modification, are permitted provided that the following conditions are met:
8165242Srrs *
9221512Stuexen * a) Redistributions of source code must retain the above copyright notice,
10221512Stuexen *    this list of conditions and the following disclaimer.
11221512Stuexen *
12221512Stuexen * b) Redistributions in binary form must reproduce the above copyright
13221512Stuexen *    notice, this list of conditions and the following disclaimer in
14221512Stuexen *    the documentation and/or other materials provided with the distribution.
15221512Stuexen *
16221512Stuexen * c) Neither the name of Cisco Systems, Inc. nor the names of its
17221512Stuexen *    contributors may be used to endorse or promote products derived
18221512Stuexen *    from this software without specific prior written permission.
19221512Stuexen *
20221512Stuexen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21221512Stuexen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22221512Stuexen * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23221512Stuexen * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24221512Stuexen * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25221512Stuexen * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26221512Stuexen * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27221512Stuexen * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28221512Stuexen * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29221512Stuexen * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30221512Stuexen * THE POSSIBILITY OF SUCH DAMAGE.
31165242Srrs */
32221512Stuexen
33165242Srrs#include <sys/cdefs.h>
34165242Srrs__FBSDID("$FreeBSD: releng/10.3/lib/libc/net/sctp_sys_calls.c 294911 2016-01-27 14:01:21Z tuexen $");
35235827Stuexen
36165242Srrs#include <stdio.h>
37165242Srrs#include <string.h>
38165242Srrs#include <errno.h>
39165242Srrs#include <stdlib.h>
40165242Srrs#include <unistd.h>
41165242Srrs#include <sys/types.h>
42165242Srrs#include <sys/socket.h>
43165242Srrs#include <sys/errno.h>
44165242Srrs#include <sys/syscall.h>
45165242Srrs#include <sys/uio.h>
46165242Srrs#include <netinet/in.h>
47165242Srrs#include <arpa/inet.h>
48165242Srrs#include <netinet/sctp_uio.h>
49165242Srrs#include <netinet/sctp.h>
50165242Srrs
51165242Srrs#ifndef IN6_IS_ADDR_V4MAPPED
52165242Srrs#define IN6_IS_ADDR_V4MAPPED(a)		      \
53209684Sbrucec	((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) &&	\
54209684Sbrucec	 (*(const uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) &&	\
55209684Sbrucec	 (*(const uint32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)))
56165242Srrs#endif
57165242Srrs
58165242Srrs#define SCTP_CONTROL_VEC_SIZE_RCV  16384
59165242Srrs
60166884Srrs
61165242Srrsstatic void
62165242Srrsin6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
63165242Srrs{
64165242Srrs	bzero(sin, sizeof(*sin));
65165242Srrs	sin->sin_len = sizeof(struct sockaddr_in);
66165242Srrs	sin->sin_family = AF_INET;
67165242Srrs	sin->sin_port = sin6->sin6_port;
68165242Srrs	sin->sin_addr.s_addr = sin6->sin6_addr.__u6_addr.__u6_addr32[3];
69165242Srrs}
70165242Srrs
71165242Srrsint
72165242Srrssctp_getaddrlen(sa_family_t family)
73165242Srrs{
74223132Stuexen	int ret, sd;
75165242Srrs	socklen_t siz;
76165242Srrs	struct sctp_assoc_value av;
77165242Srrs
78165242Srrs	av.assoc_value = family;
79165242Srrs	siz = sizeof(av);
80165242Srrs#if defined(AF_INET)
81165242Srrs	sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
82165242Srrs#elif defined(AF_INET6)
83165242Srrs	sd = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP);
84223132Stuexen#else
85223132Stuexen	sd = -1;
86165242Srrs#endif
87165242Srrs	if (sd == -1) {
88170993Srrs		return (-1);
89165242Srrs	}
90223132Stuexen	ret = getsockopt(sd, IPPROTO_SCTP, SCTP_GET_ADDR_LEN, &av, &siz);
91165242Srrs	close(sd);
92223132Stuexen	if (ret == 0) {
93165242Srrs		return ((int)av.assoc_value);
94165242Srrs	} else {
95170993Srrs		return (-1);
96165242Srrs	}
97165242Srrs}
98165242Srrs
99165242Srrsint
100170580Srrssctp_connectx(int sd, const struct sockaddr *addrs, int addrcnt,
101170580Srrs    sctp_assoc_t * id)
102165242Srrs{
103249333Stuexen	char *buf;
104255695Stuexen	int i, ret, *aa;
105165242Srrs	char *cpto;
106165242Srrs	const struct sockaddr *at;
107255695Stuexen	size_t len;
108165242Srrs
109170580Srrs	/* validate the address count and list */
110170580Srrs	if ((addrs == NULL) || (addrcnt <= 0)) {
111170580Srrs		errno = EINVAL;
112170580Srrs		return (-1);
113170580Srrs	}
114249333Stuexen	if ((buf = malloc(sizeof(int) + (size_t)addrcnt * sizeof(struct sockaddr_in6))) == NULL) {
115249333Stuexen		errno = E2BIG;
116249333Stuexen		return (-1);
117249333Stuexen	}
118255695Stuexen	len = sizeof(int);
119165242Srrs	at = addrs;
120249333Stuexen	cpto = buf + sizeof(int);
121165242Srrs	/* validate all the addresses and get the size */
122165242Srrs	for (i = 0; i < addrcnt; i++) {
123243302Stuexen		switch (at->sa_family) {
124243302Stuexen		case AF_INET:
125171031Srrs			if (at->sa_len != sizeof(struct sockaddr_in)) {
126249333Stuexen				free(buf);
127171031Srrs				errno = EINVAL;
128171031Srrs				return (-1);
129171031Srrs			}
130243302Stuexen			memcpy(cpto, at, sizeof(struct sockaddr_in));
131243302Stuexen			cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
132243302Stuexen			len += sizeof(struct sockaddr_in);
133243302Stuexen			break;
134243302Stuexen		case AF_INET6:
135171031Srrs			if (at->sa_len != sizeof(struct sockaddr_in6)) {
136249333Stuexen				free(buf);
137171031Srrs				errno = EINVAL;
138171031Srrs				return (-1);
139171031Srrs			}
140165242Srrs			if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) {
141165242Srrs				in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at);
142165242Srrs				cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
143165242Srrs				len += sizeof(struct sockaddr_in);
144165242Srrs			} else {
145243302Stuexen				memcpy(cpto, at, sizeof(struct sockaddr_in6));
146243302Stuexen				cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in6));
147243302Stuexen				len += sizeof(struct sockaddr_in6);
148165242Srrs			}
149243302Stuexen			break;
150243302Stuexen		default:
151249333Stuexen			free(buf);
152165242Srrs			errno = EINVAL;
153165242Srrs			return (-1);
154165242Srrs		}
155249333Stuexen		at = (struct sockaddr *)((caddr_t)at + at->sa_len);
156165242Srrs	}
157165242Srrs	aa = (int *)buf;
158249333Stuexen	*aa = addrcnt;
159165242Srrs	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf,
160169623Srrs	    (socklen_t) len);
161249333Stuexen	if ((ret == 0) && (id != NULL)) {
162249333Stuexen		*id = *(sctp_assoc_t *) buf;
163167598Srrs	}
164255695Stuexen	free(buf);
165165242Srrs	return (ret);
166165242Srrs}
167165242Srrs
168165242Srrsint
169165242Srrssctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
170165242Srrs{
171165242Srrs	struct sctp_getaddresses *gaddrs;
172165242Srrs	struct sockaddr *sa;
173171572Srrs	struct sockaddr_in *sin;
174171572Srrs	struct sockaddr_in6 *sin6;
175228630Stuexen	int i;
176228630Stuexen	size_t argsz;
177171572Srrs	uint16_t sport = 0;
178165242Srrs
179170580Srrs	/* validate the flags */
180165242Srrs	if ((flags != SCTP_BINDX_ADD_ADDR) &&
181165242Srrs	    (flags != SCTP_BINDX_REM_ADDR)) {
182165242Srrs		errno = EFAULT;
183165242Srrs		return (-1);
184165242Srrs	}
185170580Srrs	/* validate the address count and list */
186170580Srrs	if ((addrcnt <= 0) || (addrs == NULL)) {
187170580Srrs		errno = EINVAL;
188170580Srrs		return (-1);
189170580Srrs	}
190171572Srrs	/* First pre-screen the addresses */
191165242Srrs	sa = addrs;
192165242Srrs	for (i = 0; i < addrcnt; i++) {
193243302Stuexen		switch (sa->sa_family) {
194243302Stuexen		case AF_INET:
195243302Stuexen			if (sa->sa_len != sizeof(struct sockaddr_in)) {
196243302Stuexen				errno = EINVAL;
197243302Stuexen				return (-1);
198243302Stuexen			}
199171572Srrs			sin = (struct sockaddr_in *)sa;
200171572Srrs			if (sin->sin_port) {
201171572Srrs				/* non-zero port, check or save */
202171572Srrs				if (sport) {
203171572Srrs					/* Check against our port */
204171572Srrs					if (sport != sin->sin_port) {
205243302Stuexen						errno = EINVAL;
206243302Stuexen						return (-1);
207171572Srrs					}
208171572Srrs				} else {
209171572Srrs					/* save off the port */
210171572Srrs					sport = sin->sin_port;
211171572Srrs				}
212171572Srrs			}
213243302Stuexen			break;
214243302Stuexen		case AF_INET6:
215243302Stuexen			if (sa->sa_len != sizeof(struct sockaddr_in6)) {
216243302Stuexen				errno = EINVAL;
217243302Stuexen				return (-1);
218243302Stuexen			}
219171572Srrs			sin6 = (struct sockaddr_in6 *)sa;
220171572Srrs			if (sin6->sin6_port) {
221171572Srrs				/* non-zero port, check or save */
222171572Srrs				if (sport) {
223171572Srrs					/* Check against our port */
224171572Srrs					if (sport != sin6->sin6_port) {
225243302Stuexen						errno = EINVAL;
226243302Stuexen						return (-1);
227171572Srrs					}
228171572Srrs				} else {
229171572Srrs					/* save off the port */
230171572Srrs					sport = sin6->sin6_port;
231171572Srrs				}
232171572Srrs			}
233243302Stuexen			break;
234243302Stuexen		default:
235243302Stuexen			/* Invalid address family specified. */
236260428Stuexen			errno = EAFNOSUPPORT;
237243302Stuexen			return (-1);
238171572Srrs		}
239228630Stuexen		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
240171572Srrs	}
241243302Stuexen	argsz = sizeof(struct sctp_getaddresses) +
242243302Stuexen	    sizeof(struct sockaddr_storage);
243243302Stuexen	if ((gaddrs = (struct sctp_getaddresses *)malloc(argsz)) == NULL) {
244243302Stuexen		errno = ENOMEM;
245243302Stuexen		return (-1);
246243302Stuexen	}
247243302Stuexen	sa = addrs;
248171572Srrs	for (i = 0; i < addrcnt; i++) {
249171031Srrs		memset(gaddrs, 0, argsz);
250171031Srrs		gaddrs->sget_assoc_id = 0;
251228630Stuexen		memcpy(gaddrs->addr, sa, sa->sa_len);
252260428Stuexen		/*
253260428Stuexen		 * Now, if there was a port mentioned, assure that the first
254260428Stuexen		 * address has that port to make sure it fails or succeeds
255260428Stuexen		 * correctly.
256260428Stuexen		 */
257260428Stuexen		if ((i == 0) && (sport != 0)) {
258260428Stuexen			switch (gaddrs->addr->sa_family) {
259260428Stuexen			case AF_INET:
260260428Stuexen				sin = (struct sockaddr_in *)gaddrs->addr;
261260428Stuexen				sin->sin_port = sport;
262260428Stuexen				break;
263260428Stuexen			case AF_INET6:
264260428Stuexen				sin6 = (struct sockaddr_in6 *)gaddrs->addr;
265260428Stuexen				sin6->sin6_port = sport;
266260428Stuexen				break;
267260428Stuexen			}
268260428Stuexen		}
269171031Srrs		if (setsockopt(sd, IPPROTO_SCTP, flags, gaddrs,
270171031Srrs		    (socklen_t) argsz) != 0) {
271165242Srrs			free(gaddrs);
272165242Srrs			return (-1);
273165242Srrs		}
274228630Stuexen		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
275165242Srrs	}
276165242Srrs	free(gaddrs);
277165242Srrs	return (0);
278165242Srrs}
279165242Srrs
280165242Srrsint
281165242Srrssctp_opt_info(int sd, sctp_assoc_t id, int opt, void *arg, socklen_t * size)
282165242Srrs{
283165242Srrs	if (arg == NULL) {
284171152Srrs		errno = EINVAL;
285171152Srrs		return (-1);
286165242Srrs	}
287253105Stuexen	if ((id == SCTP_CURRENT_ASSOC) ||
288253105Stuexen	    (id == SCTP_ALL_ASSOC)) {
289253105Stuexen		errno = EINVAL;
290253105Stuexen		return (-1);
291253105Stuexen	}
292171572Srrs	switch (opt) {
293171572Srrs	case SCTP_RTOINFO:
294171572Srrs		((struct sctp_rtoinfo *)arg)->srto_assoc_id = id;
295171572Srrs		break;
296171572Srrs	case SCTP_ASSOCINFO:
297171572Srrs		((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
298171572Srrs		break;
299171572Srrs	case SCTP_DEFAULT_SEND_PARAM:
300171572Srrs		((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
301171572Srrs		break;
302171572Srrs	case SCTP_PRIMARY_ADDR:
303171572Srrs		((struct sctp_setprim *)arg)->ssp_assoc_id = id;
304171572Srrs		break;
305171572Srrs	case SCTP_PEER_ADDR_PARAMS:
306171572Srrs		((struct sctp_paddrparams *)arg)->spp_assoc_id = id;
307171572Srrs		break;
308171572Srrs	case SCTP_MAXSEG:
309171572Srrs		((struct sctp_assoc_value *)arg)->assoc_id = id;
310171572Srrs		break;
311171572Srrs	case SCTP_AUTH_KEY:
312171572Srrs		((struct sctp_authkey *)arg)->sca_assoc_id = id;
313171572Srrs		break;
314171572Srrs	case SCTP_AUTH_ACTIVE_KEY:
315171572Srrs		((struct sctp_authkeyid *)arg)->scact_assoc_id = id;
316171572Srrs		break;
317171572Srrs	case SCTP_DELAYED_SACK:
318171572Srrs		((struct sctp_sack_info *)arg)->sack_assoc_id = id;
319171572Srrs		break;
320171572Srrs	case SCTP_CONTEXT:
321171572Srrs		((struct sctp_assoc_value *)arg)->assoc_id = id;
322171572Srrs		break;
323171572Srrs	case SCTP_STATUS:
324171572Srrs		((struct sctp_status *)arg)->sstat_assoc_id = id;
325171572Srrs		break;
326171572Srrs	case SCTP_GET_PEER_ADDR_INFO:
327171572Srrs		((struct sctp_paddrinfo *)arg)->spinfo_assoc_id = id;
328171572Srrs		break;
329171572Srrs	case SCTP_PEER_AUTH_CHUNKS:
330171572Srrs		((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
331171572Srrs		break;
332171572Srrs	case SCTP_LOCAL_AUTH_CHUNKS:
333171572Srrs		((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
334171572Srrs		break;
335221512Stuexen	case SCTP_TIMEOUTS:
336221512Stuexen		((struct sctp_timeouts *)arg)->stimo_assoc_id = id;
337221512Stuexen		break;
338223132Stuexen	case SCTP_EVENT:
339223132Stuexen		((struct sctp_event *)arg)->se_assoc_id = id;
340223132Stuexen		break;
341223178Stuexen	case SCTP_DEFAULT_SNDINFO:
342223178Stuexen		((struct sctp_sndinfo *)arg)->snd_assoc_id = id;
343223178Stuexen		break;
344223178Stuexen	case SCTP_DEFAULT_PRINFO:
345223178Stuexen		((struct sctp_default_prinfo *)arg)->pr_assoc_id = id;
346223178Stuexen		break;
347224641Stuexen	case SCTP_PEER_ADDR_THLDS:
348224641Stuexen		((struct sctp_paddrthlds *)arg)->spt_assoc_id = id;
349224641Stuexen		break;
350227755Stuexen	case SCTP_REMOTE_UDP_ENCAPS_PORT:
351227755Stuexen		((struct sctp_udpencaps *)arg)->sue_assoc_id = id;
352227755Stuexen		break;
353270356Stuexen	case SCTP_ECN_SUPPORTED:
354270356Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
355270356Stuexen		break;
356270357Stuexen	case SCTP_PR_SUPPORTED:
357270357Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
358270357Stuexen		break;
359270362Stuexen	case SCTP_AUTH_SUPPORTED:
360270362Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
361270362Stuexen		break;
362270362Stuexen	case SCTP_ASCONF_SUPPORTED:
363270362Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
364270362Stuexen		break;
365270361Stuexen	case SCTP_RECONFIG_SUPPORTED:
366270361Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
367270361Stuexen		break;
368270359Stuexen	case SCTP_NRSACK_SUPPORTED:
369270359Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
370270359Stuexen		break;
371270360Stuexen	case SCTP_PKTDROP_SUPPORTED:
372270360Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
373270360Stuexen		break;
374223180Stuexen	case SCTP_MAX_BURST:
375223180Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
376223180Stuexen		break;
377253104Stuexen	case SCTP_ENABLE_STREAM_RESET:
378253104Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
379253104Stuexen		break;
380270363Stuexen	case SCTP_PR_STREAM_STATUS:
381270363Stuexen		((struct sctp_prstatus *)arg)->sprstat_assoc_id = id;
382270363Stuexen		break;
383270363Stuexen	case SCTP_PR_ASSOC_STATUS:
384270363Stuexen		((struct sctp_prstatus *)arg)->sprstat_assoc_id = id;
385270363Stuexen		break;
386283724Stuexen	case SCTP_MAX_CWND:
387283724Stuexen		((struct sctp_assoc_value *)arg)->assoc_id = id;
388283724Stuexen		break;
389171572Srrs	default:
390171572Srrs		break;
391171572Srrs	}
392169623Srrs	return (getsockopt(sd, IPPROTO_SCTP, opt, arg, size));
393165242Srrs}
394165242Srrs
395165242Srrsint
396165242Srrssctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
397165242Srrs{
398165242Srrs	struct sctp_getaddresses *addrs;
399165242Srrs	struct sockaddr *sa;
400165242Srrs	sctp_assoc_t asoc;
401165242Srrs	caddr_t lim;
402228630Stuexen	socklen_t opt_len;
403165242Srrs	int cnt;
404165242Srrs
405165242Srrs	if (raddrs == NULL) {
406165242Srrs		errno = EFAULT;
407165242Srrs		return (-1);
408165242Srrs	}
409165242Srrs	asoc = id;
410228630Stuexen	opt_len = (socklen_t) sizeof(sctp_assoc_t);
411165242Srrs	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_REMOTE_ADDR_SIZE,
412228630Stuexen	    &asoc, &opt_len) != 0) {
413165242Srrs		return (-1);
414165242Srrs	}
415165242Srrs	/* size required is returned in 'asoc' */
416228630Stuexen	opt_len = (socklen_t) ((size_t)asoc + sizeof(struct sctp_getaddresses));
417228630Stuexen	addrs = calloc(1, (size_t)opt_len);
418165242Srrs	if (addrs == NULL) {
419242512Stuexen		errno = ENOMEM;
420165242Srrs		return (-1);
421165242Srrs	}
422165242Srrs	addrs->sget_assoc_id = id;
423165242Srrs	/* Now lets get the array of addresses */
424165242Srrs	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_PEER_ADDRESSES,
425228630Stuexen	    addrs, &opt_len) != 0) {
426165242Srrs		free(addrs);
427165242Srrs		return (-1);
428165242Srrs	}
429228630Stuexen	*raddrs = (struct sockaddr *)&addrs->addr[0];
430165242Srrs	cnt = 0;
431165242Srrs	sa = (struct sockaddr *)&addrs->addr[0];
432228630Stuexen	lim = (caddr_t)addrs + opt_len;
433165242Srrs	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
434165242Srrs		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
435165242Srrs		cnt++;
436165242Srrs	}
437165242Srrs	return (cnt);
438165242Srrs}
439165242Srrs
440167598Srrsvoid
441165242Srrssctp_freepaddrs(struct sockaddr *addrs)
442165242Srrs{
443165242Srrs	void *fr_addr;
444165242Srrs
445249333Stuexen	/* Take away the hidden association id */
446165242Srrs	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
447165242Srrs	/* Now free it */
448165242Srrs	free(fr_addr);
449165242Srrs}
450165242Srrs
451165242Srrsint
452165242Srrssctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
453165242Srrs{
454165242Srrs	struct sctp_getaddresses *addrs;
455165242Srrs	caddr_t lim;
456165242Srrs	struct sockaddr *sa;
457228630Stuexen	size_t size_of_addresses;
458228630Stuexen	socklen_t opt_len;
459165242Srrs	int cnt;
460165242Srrs
461165242Srrs	if (raddrs == NULL) {
462165242Srrs		errno = EFAULT;
463165242Srrs		return (-1);
464165242Srrs	}
465165242Srrs	size_of_addresses = 0;
466228630Stuexen	opt_len = (socklen_t) sizeof(int);
467165242Srrs	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDR_SIZE,
468228630Stuexen	    &size_of_addresses, &opt_len) != 0) {
469165242Srrs		errno = ENOMEM;
470165242Srrs		return (-1);
471165242Srrs	}
472165242Srrs	if (size_of_addresses == 0) {
473165242Srrs		errno = ENOTCONN;
474165242Srrs		return (-1);
475165242Srrs	}
476228630Stuexen	opt_len = (socklen_t) (size_of_addresses +
477228630Stuexen	    sizeof(struct sockaddr_storage) +
478228630Stuexen	    sizeof(struct sctp_getaddresses));
479228630Stuexen	addrs = calloc(1, (size_t)opt_len);
480165242Srrs	if (addrs == NULL) {
481165242Srrs		errno = ENOMEM;
482165242Srrs		return (-1);
483165242Srrs	}
484165242Srrs	addrs->sget_assoc_id = id;
485165242Srrs	/* Now lets get the array of addresses */
486165242Srrs	if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDRESSES, addrs,
487228630Stuexen	    &opt_len) != 0) {
488165242Srrs		free(addrs);
489165242Srrs		errno = ENOMEM;
490165242Srrs		return (-1);
491165242Srrs	}
492228630Stuexen	*raddrs = (struct sockaddr *)&addrs->addr[0];
493165242Srrs	cnt = 0;
494165242Srrs	sa = (struct sockaddr *)&addrs->addr[0];
495228630Stuexen	lim = (caddr_t)addrs + opt_len;
496165242Srrs	while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
497165242Srrs		sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
498165242Srrs		cnt++;
499165242Srrs	}
500165242Srrs	return (cnt);
501165242Srrs}
502165242Srrs
503167598Srrsvoid
504165242Srrssctp_freeladdrs(struct sockaddr *addrs)
505165242Srrs{
506165242Srrs	void *fr_addr;
507165242Srrs
508249333Stuexen	/* Take away the hidden association id */
509165242Srrs	fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
510165242Srrs	/* Now free it */
511165242Srrs	free(fr_addr);
512165242Srrs}
513165242Srrs
514165242Srrsssize_t
515165242Srrssctp_sendmsg(int s,
516165242Srrs    const void *data,
517165242Srrs    size_t len,
518165242Srrs    const struct sockaddr *to,
519169623Srrs    socklen_t tolen,
520209684Sbrucec    uint32_t ppid,
521209684Sbrucec    uint32_t flags,
522209684Sbrucec    uint16_t stream_no,
523209684Sbrucec    uint32_t timetolive,
524209684Sbrucec    uint32_t context)
525165242Srrs{
526165242Srrs#ifdef SYS_sctp_generic_sendmsg
527165242Srrs	struct sctp_sndrcvinfo sinfo;
528165242Srrs
529228531Stuexen	memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
530165242Srrs	sinfo.sinfo_ppid = ppid;
531165242Srrs	sinfo.sinfo_flags = flags;
532165242Srrs	sinfo.sinfo_stream = stream_no;
533165242Srrs	sinfo.sinfo_timetolive = timetolive;
534165242Srrs	sinfo.sinfo_context = context;
535165242Srrs	sinfo.sinfo_assoc_id = 0;
536165242Srrs	return (syscall(SYS_sctp_generic_sendmsg, s,
537165242Srrs	    data, len, to, tolen, &sinfo, 0));
538165242Srrs#else
539165242Srrs	struct msghdr msg;
540249333Stuexen	struct sctp_sndrcvinfo *sinfo;
541221512Stuexen	struct iovec iov;
542249333Stuexen	char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
543165242Srrs	struct cmsghdr *cmsg;
544165242Srrs	struct sockaddr *who = NULL;
545165242Srrs	union {
546165242Srrs		struct sockaddr_in in;
547165242Srrs		struct sockaddr_in6 in6;
548165242Srrs	}     addr;
549165242Srrs
550221512Stuexen	if ((tolen > 0) &&
551221512Stuexen	    ((to == NULL) || (tolen < sizeof(struct sockaddr)))) {
552169623Srrs		errno = EINVAL;
553246629Stuexen		return (-1);
554169623Srrs	}
555249333Stuexen	if ((to != NULL) && (tolen > 0)) {
556249333Stuexen		switch (to->sa_family) {
557249333Stuexen		case AF_INET:
558169623Srrs			if (tolen != sizeof(struct sockaddr_in)) {
559169623Srrs				errno = EINVAL;
560246629Stuexen				return (-1);
561165242Srrs			}
562221512Stuexen			if ((to->sa_len > 0) &&
563221512Stuexen			    (to->sa_len != sizeof(struct sockaddr_in))) {
564169623Srrs				errno = EINVAL;
565246629Stuexen				return (-1);
566169623Srrs			}
567169623Srrs			memcpy(&addr, to, sizeof(struct sockaddr_in));
568169623Srrs			addr.in.sin_len = sizeof(struct sockaddr_in);
569249333Stuexen			break;
570249333Stuexen		case AF_INET6:
571169623Srrs			if (tolen != sizeof(struct sockaddr_in6)) {
572169623Srrs				errno = EINVAL;
573246629Stuexen				return (-1);
574169623Srrs			}
575221512Stuexen			if ((to->sa_len > 0) &&
576221512Stuexen			    (to->sa_len != sizeof(struct sockaddr_in6))) {
577169623Srrs				errno = EINVAL;
578246629Stuexen				return (-1);
579169623Srrs			}
580169623Srrs			memcpy(&addr, to, sizeof(struct sockaddr_in6));
581169623Srrs			addr.in6.sin6_len = sizeof(struct sockaddr_in6);
582249333Stuexen			break;
583249333Stuexen		default:
584169623Srrs			errno = EAFNOSUPPORT;
585246629Stuexen			return (-1);
586165242Srrs		}
587165242Srrs		who = (struct sockaddr *)&addr;
588165242Srrs	}
589221512Stuexen	iov.iov_base = (char *)data;
590221512Stuexen	iov.iov_len = len;
591165242Srrs
592169623Srrs	if (who) {
593165242Srrs		msg.msg_name = (caddr_t)who;
594165242Srrs		msg.msg_namelen = who->sa_len;
595165242Srrs	} else {
596165242Srrs		msg.msg_name = (caddr_t)NULL;
597165242Srrs		msg.msg_namelen = 0;
598165242Srrs	}
599221512Stuexen	msg.msg_iov = &iov;
600165242Srrs	msg.msg_iovlen = 1;
601249333Stuexen	msg.msg_control = cmsgbuf;
602249333Stuexen	msg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
603283702Stuexen	msg.msg_flags = 0;
604249333Stuexen	cmsg = (struct cmsghdr *)cmsgbuf;
605165242Srrs	cmsg->cmsg_level = IPPROTO_SCTP;
606165242Srrs	cmsg->cmsg_type = SCTP_SNDRCV;
607165242Srrs	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
608249333Stuexen	sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
609270362Stuexen	memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
610249333Stuexen	sinfo->sinfo_stream = stream_no;
611249333Stuexen	sinfo->sinfo_ssn = 0;
612249333Stuexen	sinfo->sinfo_flags = flags;
613249333Stuexen	sinfo->sinfo_ppid = ppid;
614249333Stuexen	sinfo->sinfo_context = context;
615249333Stuexen	sinfo->sinfo_assoc_id = 0;
616249333Stuexen	sinfo->sinfo_timetolive = timetolive;
617249333Stuexen	return (sendmsg(s, &msg, 0));
618165242Srrs#endif
619165242Srrs}
620165242Srrs
621165242Srrs
622165242Srrssctp_assoc_t
623165242Srrssctp_getassocid(int sd, struct sockaddr *sa)
624165242Srrs{
625171440Srrs	struct sctp_paddrinfo sp;
626165270Srodrigc	socklen_t siz;
627165242Srrs
628165242Srrs	/* First get the assoc id */
629171440Srrs	siz = sizeof(sp);
630165242Srrs	memset(&sp, 0, sizeof(sp));
631171440Srrs	memcpy((caddr_t)&sp.spinfo_address, sa, sa->sa_len);
632165242Srrs	if (getsockopt(sd, IPPROTO_SCTP,
633171440Srrs	    SCTP_GET_PEER_ADDR_INFO, &sp, &siz) != 0) {
634249333Stuexen		/* We depend on the fact that 0 can never be returned */
635165242Srrs		return ((sctp_assoc_t) 0);
636165242Srrs	}
637171440Srrs	return (sp.spinfo_assoc_id);
638165242Srrs}
639165242Srrs
640165242Srrsssize_t
641165242Srrssctp_send(int sd, const void *data, size_t len,
642165242Srrs    const struct sctp_sndrcvinfo *sinfo,
643165242Srrs    int flags)
644165242Srrs{
645165242Srrs
646165242Srrs#ifdef SYS_sctp_generic_sendmsg
647165242Srrs	struct sockaddr *to = NULL;
648165242Srrs
649165242Srrs	return (syscall(SYS_sctp_generic_sendmsg, sd,
650165242Srrs	    data, len, to, 0, sinfo, flags));
651165242Srrs#else
652165242Srrs	struct msghdr msg;
653221512Stuexen	struct iovec iov;
654249333Stuexen	char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
655165242Srrs	struct cmsghdr *cmsg;
656165242Srrs
657165242Srrs	if (sinfo == NULL) {
658171440Srrs		errno = EINVAL;
659171440Srrs		return (-1);
660165242Srrs	}
661221512Stuexen	iov.iov_base = (char *)data;
662221512Stuexen	iov.iov_len = len;
663165242Srrs
664249333Stuexen	msg.msg_name = NULL;
665165242Srrs	msg.msg_namelen = 0;
666221512Stuexen	msg.msg_iov = &iov;
667165242Srrs	msg.msg_iovlen = 1;
668249333Stuexen	msg.msg_control = cmsgbuf;
669249333Stuexen	msg.msg_controllen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
670283702Stuexen	msg.msg_flags = 0;
671249333Stuexen	cmsg = (struct cmsghdr *)cmsgbuf;
672165242Srrs	cmsg->cmsg_level = IPPROTO_SCTP;
673165242Srrs	cmsg->cmsg_type = SCTP_SNDRCV;
674165242Srrs	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
675249333Stuexen	memcpy(CMSG_DATA(cmsg), sinfo, sizeof(struct sctp_sndrcvinfo));
676249333Stuexen	return (sendmsg(sd, &msg, flags));
677165242Srrs#endif
678165242Srrs}
679165242Srrs
680165242Srrs
681165242Srrs
682165242Srrsssize_t
683165242Srrssctp_sendx(int sd, const void *msg, size_t msg_len,
684165242Srrs    struct sockaddr *addrs, int addrcnt,
685165242Srrs    struct sctp_sndrcvinfo *sinfo,
686165242Srrs    int flags)
687165242Srrs{
688209760Srrs	struct sctp_sndrcvinfo __sinfo;
689165242Srrs	ssize_t ret;
690165242Srrs	int i, cnt, *aa, saved_errno;
691165242Srrs	char *buf;
692228630Stuexen	int no_end_cx = 0;
693228630Stuexen	size_t len, add_len;
694165242Srrs	struct sockaddr *at;
695165242Srrs
696171572Srrs	if (addrs == NULL) {
697171572Srrs		errno = EINVAL;
698171572Srrs		return (-1);
699171572Srrs	}
700166884Srrs#ifdef SYS_sctp_generic_sendmsg
701221512Stuexen	if (addrcnt == 1) {
702166884Srrs		socklen_t l;
703294911Stuexen		ssize_t ret;
704166884Srrs
705166884Srrs		/*
706166884Srrs		 * Quick way, we don't need to do a connectx so lets use the
707166884Srrs		 * syscall directly.
708166884Srrs		 */
709166884Srrs		l = addrs->sa_len;
710294911Stuexen		ret = syscall(SYS_sctp_generic_sendmsg, sd,
711294911Stuexen		    msg, msg_len, addrs, l, sinfo, flags);
712294911Stuexen		if ((ret >= 0) && (sinfo != NULL)) {
713294911Stuexen			sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
714294911Stuexen		}
715294911Stuexen		return (ret);
716166884Srrs	}
717166884Srrs#endif
718171440Srrs
719165242Srrs	len = sizeof(int);
720165242Srrs	at = addrs;
721165242Srrs	cnt = 0;
722165242Srrs	/* validate all the addresses and get the size */
723165242Srrs	for (i = 0; i < addrcnt; i++) {
724165242Srrs		if (at->sa_family == AF_INET) {
725165242Srrs			add_len = sizeof(struct sockaddr_in);
726165242Srrs		} else if (at->sa_family == AF_INET6) {
727165242Srrs			add_len = sizeof(struct sockaddr_in6);
728165242Srrs		} else {
729165242Srrs			errno = EINVAL;
730165242Srrs			return (-1);
731165242Srrs		}
732165242Srrs		len += add_len;
733165242Srrs		at = (struct sockaddr *)((caddr_t)at + add_len);
734165242Srrs		cnt++;
735165242Srrs	}
736165242Srrs	/* do we have any? */
737165242Srrs	if (cnt == 0) {
738165242Srrs		errno = EINVAL;
739165242Srrs		return (-1);
740165242Srrs	}
741165242Srrs	buf = malloc(len);
742165242Srrs	if (buf == NULL) {
743242512Stuexen		errno = ENOMEM;
744171440Srrs		return (-1);
745165242Srrs	}
746165242Srrs	aa = (int *)buf;
747165242Srrs	*aa = cnt;
748165242Srrs	aa++;
749228630Stuexen	memcpy((caddr_t)aa, addrs, (size_t)(len - sizeof(int)));
750165242Srrs	ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_DELAYED, (void *)buf,
751165242Srrs	    (socklen_t) len);
752165242Srrs
753165242Srrs	free(buf);
754165242Srrs	if (ret != 0) {
755165242Srrs		if (errno == EALREADY) {
756203323Sbrucec			no_end_cx = 1;
757165242Srrs			goto continue_send;
758165242Srrs		}
759165242Srrs		return (ret);
760165242Srrs	}
761165242Srrscontinue_send:
762209760Srrs	if (sinfo == NULL) {
763209760Srrs		sinfo = &__sinfo;
764209760Srrs		memset(&__sinfo, 0, sizeof(__sinfo));
765209760Srrs	}
766165242Srrs	sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
767165242Srrs	if (sinfo->sinfo_assoc_id == 0) {
768165242Srrs		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
769165242Srrs		    (socklen_t) addrs->sa_len);
770165242Srrs		errno = ENOENT;
771165242Srrs		return (-1);
772165242Srrs	}
773165242Srrs	ret = sctp_send(sd, msg, msg_len, sinfo, flags);
774165242Srrs	saved_errno = errno;
775165242Srrs	if (no_end_cx == 0)
776165242Srrs		(void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
777165242Srrs		    (socklen_t) addrs->sa_len);
778165242Srrs
779165242Srrs	errno = saved_errno;
780165242Srrs	return (ret);
781165242Srrs}
782165242Srrs
783165242Srrsssize_t
784165242Srrssctp_sendmsgx(int sd,
785165242Srrs    const void *msg,
786165242Srrs    size_t len,
787165242Srrs    struct sockaddr *addrs,
788165242Srrs    int addrcnt,
789209684Sbrucec    uint32_t ppid,
790209684Sbrucec    uint32_t flags,
791209684Sbrucec    uint16_t stream_no,
792209684Sbrucec    uint32_t timetolive,
793209684Sbrucec    uint32_t context)
794165242Srrs{
795165242Srrs	struct sctp_sndrcvinfo sinfo;
796165242Srrs
797165242Srrs	memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
798165242Srrs	sinfo.sinfo_ppid = ppid;
799165242Srrs	sinfo.sinfo_flags = flags;
800165242Srrs	sinfo.sinfo_ssn = stream_no;
801165242Srrs	sinfo.sinfo_timetolive = timetolive;
802165242Srrs	sinfo.sinfo_context = context;
803246629Stuexen	return (sctp_sendx(sd, msg, len, addrs, addrcnt, &sinfo, 0));
804165242Srrs}
805165242Srrs
806165242Srrsssize_t
807165242Srrssctp_recvmsg(int s,
808165242Srrs    void *dbuf,
809165242Srrs    size_t len,
810165242Srrs    struct sockaddr *from,
811165242Srrs    socklen_t * fromlen,
812165242Srrs    struct sctp_sndrcvinfo *sinfo,
813165242Srrs    int *msg_flags)
814165242Srrs{
815165242Srrs#ifdef SYS_sctp_generic_recvmsg
816221512Stuexen	struct iovec iov;
817165242Srrs
818221512Stuexen	iov.iov_base = dbuf;
819221512Stuexen	iov.iov_len = len;
820165242Srrs	return (syscall(SYS_sctp_generic_recvmsg, s,
821221512Stuexen	    &iov, 1, from, fromlen, sinfo, msg_flags));
822165242Srrs#else
823165242Srrs	ssize_t sz;
824165242Srrs	struct msghdr msg;
825221512Stuexen	struct iovec iov;
826249333Stuexen	char cmsgbuf[SCTP_CONTROL_VEC_SIZE_RCV];
827165242Srrs	struct cmsghdr *cmsg;
828165242Srrs
829165242Srrs	if (msg_flags == NULL) {
830165242Srrs		errno = EINVAL;
831165242Srrs		return (-1);
832165242Srrs	}
833221512Stuexen	iov.iov_base = dbuf;
834221512Stuexen	iov.iov_len = len;
835165242Srrs	msg.msg_name = (caddr_t)from;
836165242Srrs	if (fromlen == NULL)
837165242Srrs		msg.msg_namelen = 0;
838165242Srrs	else
839165242Srrs		msg.msg_namelen = *fromlen;
840221512Stuexen	msg.msg_iov = &iov;
841165242Srrs	msg.msg_iovlen = 1;
842249333Stuexen	msg.msg_control = cmsgbuf;
843249333Stuexen	msg.msg_controllen = sizeof(cmsgbuf);
844283702Stuexen	msg.msg_flags = 0;
845170580Srrs	sz = recvmsg(s, &msg, *msg_flags);
846221512Stuexen	*msg_flags = msg.msg_flags;
847221512Stuexen	if (sz <= 0) {
848165242Srrs		return (sz);
849221512Stuexen	}
850221512Stuexen	if (sinfo) {
851165242Srrs		sinfo->sinfo_assoc_id = 0;
852221512Stuexen	}
853249333Stuexen	if ((msg.msg_controllen > 0) && (sinfo != NULL)) {
854165242Srrs		/*
855165242Srrs		 * parse through and see if we find the sctp_sndrcvinfo (if
856165242Srrs		 * the user wants it).
857165242Srrs		 */
858249333Stuexen		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
859249333Stuexen			if (cmsg->cmsg_level != IPPROTO_SCTP) {
860249333Stuexen				continue;
861249333Stuexen			}
862249333Stuexen			if (cmsg->cmsg_type == SCTP_SNDRCV) {
863249333Stuexen				memcpy(sinfo, CMSG_DATA(cmsg), sizeof(struct sctp_sndrcvinfo));
864165242Srrs				break;
865165242Srrs			}
866249333Stuexen			if (cmsg->cmsg_type == SCTP_EXTRCV) {
867249333Stuexen				/*
868249333Stuexen				 * Let's hope that the user provided enough
869249333Stuexen				 * enough memory. At least he asked for more
870249333Stuexen				 * information.
871249333Stuexen				 */
872249333Stuexen				memcpy(sinfo, CMSG_DATA(cmsg), sizeof(struct sctp_extrcvinfo));
873249333Stuexen				break;
874165242Srrs			}
875165242Srrs		}
876165242Srrs	}
877165242Srrs	return (sz);
878165242Srrs#endif
879165242Srrs}
880165242Srrs
881223132Stuexenssize_t
882223132Stuexensctp_recvv(int sd,
883223132Stuexen    const struct iovec *iov,
884223132Stuexen    int iovlen,
885223132Stuexen    struct sockaddr *from,
886223132Stuexen    socklen_t * fromlen,
887223132Stuexen    void *info,
888223132Stuexen    socklen_t * infolen,
889223132Stuexen    unsigned int *infotype,
890223132Stuexen    int *flags)
891223132Stuexen{
892249333Stuexen	char cmsgbuf[SCTP_CONTROL_VEC_SIZE_RCV];
893223132Stuexen	struct msghdr msg;
894223132Stuexen	struct cmsghdr *cmsg;
895249333Stuexen	ssize_t ret;
896223132Stuexen	struct sctp_rcvinfo *rcvinfo;
897223132Stuexen	struct sctp_nxtinfo *nxtinfo;
898165242Srrs
899283701Stuexen	if (((info != NULL) && (infolen == NULL)) ||
900223152Stuexen	    ((info == NULL) && (infolen != NULL) && (*infolen != 0)) ||
901223152Stuexen	    ((info != NULL) && (infotype == NULL))) {
902223152Stuexen		errno = EINVAL;
903223152Stuexen		return (-1);
904223152Stuexen	}
905223132Stuexen	if (infotype) {
906223132Stuexen		*infotype = SCTP_RECVV_NOINFO;
907223132Stuexen	}
908223132Stuexen	msg.msg_name = from;
909223132Stuexen	if (fromlen == NULL) {
910223132Stuexen		msg.msg_namelen = 0;
911223132Stuexen	} else {
912223132Stuexen		msg.msg_namelen = *fromlen;
913223132Stuexen	}
914223132Stuexen	msg.msg_iov = (struct iovec *)iov;
915223132Stuexen	msg.msg_iovlen = iovlen;
916249333Stuexen	msg.msg_control = cmsgbuf;
917249333Stuexen	msg.msg_controllen = sizeof(cmsgbuf);
918283702Stuexen	msg.msg_flags = 0;
919249333Stuexen	ret = recvmsg(sd, &msg, *flags);
920223132Stuexen	*flags = msg.msg_flags;
921249333Stuexen	if ((ret > 0) &&
922223132Stuexen	    (msg.msg_controllen > 0) &&
923223132Stuexen	    (infotype != NULL) &&
924223132Stuexen	    (infolen != NULL) &&
925223132Stuexen	    (*infolen > 0)) {
926223132Stuexen		rcvinfo = NULL;
927223132Stuexen		nxtinfo = NULL;
928223132Stuexen		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
929223132Stuexen			if (cmsg->cmsg_level != IPPROTO_SCTP) {
930223132Stuexen				continue;
931223132Stuexen			}
932223132Stuexen			if (cmsg->cmsg_type == SCTP_RCVINFO) {
933223132Stuexen				rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmsg);
934249333Stuexen				if (nxtinfo != NULL) {
935249333Stuexen					break;
936249333Stuexen				} else {
937249333Stuexen					continue;
938249333Stuexen				}
939223132Stuexen			}
940223132Stuexen			if (cmsg->cmsg_type == SCTP_NXTINFO) {
941223132Stuexen				nxtinfo = (struct sctp_nxtinfo *)CMSG_DATA(cmsg);
942249333Stuexen				if (rcvinfo != NULL) {
943249333Stuexen					break;
944249333Stuexen				} else {
945249333Stuexen					continue;
946249333Stuexen				}
947223132Stuexen			}
948223132Stuexen		}
949249333Stuexen		if (rcvinfo != NULL) {
950249333Stuexen			if ((nxtinfo != NULL) && (*infolen >= sizeof(struct sctp_recvv_rn))) {
951249333Stuexen				struct sctp_recvv_rn *rn_info;
952165242Srrs
953249333Stuexen				rn_info = (struct sctp_recvv_rn *)info;
954249333Stuexen				rn_info->recvv_rcvinfo = *rcvinfo;
955249333Stuexen				rn_info->recvv_nxtinfo = *nxtinfo;
956249333Stuexen				*infolen = (socklen_t) sizeof(struct sctp_recvv_rn);
957249333Stuexen				*infotype = SCTP_RECVV_RN;
958249333Stuexen			} else if (*infolen >= sizeof(struct sctp_rcvinfo)) {
959249333Stuexen				memcpy(info, rcvinfo, sizeof(struct sctp_rcvinfo));
960249333Stuexen				*infolen = (socklen_t) sizeof(struct sctp_rcvinfo);
961249333Stuexen				*infotype = SCTP_RECVV_RCVINFO;
962223132Stuexen			}
963249333Stuexen		} else if (nxtinfo != NULL) {
964249333Stuexen			if (*infolen >= sizeof(struct sctp_nxtinfo)) {
965223132Stuexen				memcpy(info, nxtinfo, sizeof(struct sctp_nxtinfo));
966223132Stuexen				*infolen = (socklen_t) sizeof(struct sctp_nxtinfo);
967223132Stuexen				*infotype = SCTP_RECVV_NXTINFO;
968223132Stuexen			}
969223132Stuexen		}
970223132Stuexen	}
971249333Stuexen	return (ret);
972223132Stuexen}
973223132Stuexen
974223132Stuexenssize_t
975223132Stuexensctp_sendv(int sd,
976223132Stuexen    const struct iovec *iov, int iovcnt,
977223132Stuexen    struct sockaddr *addrs, int addrcnt,
978223132Stuexen    void *info, socklen_t infolen, unsigned int infotype,
979223132Stuexen    int flags)
980165242Srrs{
981223132Stuexen	ssize_t ret;
982223132Stuexen	int i;
983223152Stuexen	socklen_t addr_len;
984223152Stuexen	struct msghdr msg;
985223152Stuexen	in_port_t port;
986223132Stuexen	struct sctp_sendv_spa *spa_info;
987223132Stuexen	struct cmsghdr *cmsg;
988223132Stuexen	char *cmsgbuf;
989223132Stuexen	struct sockaddr *addr;
990223132Stuexen	struct sockaddr_in *addr_in;
991223132Stuexen	struct sockaddr_in6 *addr_in6;
992294910Stuexen	sctp_assoc_t *assoc_id;
993165242Srrs
994223152Stuexen	if ((addrcnt < 0) ||
995223152Stuexen	    (iovcnt < 0) ||
996223154Stuexen	    ((addrs == NULL) && (addrcnt > 0)) ||
997223154Stuexen	    ((addrs != NULL) && (addrcnt == 0)) ||
998223152Stuexen	    ((iov == NULL) && (iovcnt > 0)) ||
999223152Stuexen	    ((iov != NULL) && (iovcnt == 0))) {
1000223132Stuexen		errno = EINVAL;
1001165242Srrs		return (-1);
1002223132Stuexen	}
1003223132Stuexen	cmsgbuf = malloc(CMSG_SPACE(sizeof(struct sctp_sndinfo)) +
1004223132Stuexen	    CMSG_SPACE(sizeof(struct sctp_prinfo)) +
1005223132Stuexen	    CMSG_SPACE(sizeof(struct sctp_authinfo)) +
1006228630Stuexen	    (size_t)addrcnt * CMSG_SPACE(sizeof(struct in6_addr)));
1007223132Stuexen	if (cmsgbuf == NULL) {
1008242512Stuexen		errno = ENOMEM;
1009223132Stuexen		return (-1);
1010223132Stuexen	}
1011294910Stuexen	assoc_id = NULL;
1012223132Stuexen	msg.msg_control = cmsgbuf;
1013223132Stuexen	msg.msg_controllen = 0;
1014223132Stuexen	cmsg = (struct cmsghdr *)cmsgbuf;
1015223132Stuexen	switch (infotype) {
1016223152Stuexen	case SCTP_SENDV_NOINFO:
1017223152Stuexen		if ((infolen != 0) || (info != NULL)) {
1018223152Stuexen			free(cmsgbuf);
1019223152Stuexen			errno = EINVAL;
1020223152Stuexen			return (-1);
1021223152Stuexen		}
1022223152Stuexen		break;
1023223132Stuexen	case SCTP_SENDV_SNDINFO:
1024223152Stuexen		if ((info == NULL) || (infolen < sizeof(struct sctp_sndinfo))) {
1025223132Stuexen			free(cmsgbuf);
1026223132Stuexen			errno = EINVAL;
1027223132Stuexen			return (-1);
1028223132Stuexen		}
1029223132Stuexen		cmsg->cmsg_level = IPPROTO_SCTP;
1030223132Stuexen		cmsg->cmsg_type = SCTP_SNDINFO;
1031223132Stuexen		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
1032223132Stuexen		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_sndinfo));
1033223132Stuexen		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
1034223132Stuexen		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
1035294910Stuexen		assoc_id = &(((struct sctp_sndinfo *)info)->snd_assoc_id);
1036223132Stuexen		break;
1037223132Stuexen	case SCTP_SENDV_PRINFO:
1038223152Stuexen		if ((info == NULL) || (infolen < sizeof(struct sctp_prinfo))) {
1039223132Stuexen			free(cmsgbuf);
1040223132Stuexen			errno = EINVAL;
1041223132Stuexen			return (-1);
1042223132Stuexen		}
1043223132Stuexen		cmsg->cmsg_level = IPPROTO_SCTP;
1044223132Stuexen		cmsg->cmsg_type = SCTP_PRINFO;
1045223132Stuexen		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
1046223132Stuexen		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_prinfo));
1047223132Stuexen		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
1048223132Stuexen		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
1049223132Stuexen		break;
1050223132Stuexen	case SCTP_SENDV_AUTHINFO:
1051223152Stuexen		if ((info == NULL) || (infolen < sizeof(struct sctp_authinfo))) {
1052223132Stuexen			free(cmsgbuf);
1053223132Stuexen			errno = EINVAL;
1054223132Stuexen			return (-1);
1055223132Stuexen		}
1056223132Stuexen		cmsg->cmsg_level = IPPROTO_SCTP;
1057223132Stuexen		cmsg->cmsg_type = SCTP_AUTHINFO;
1058223132Stuexen		cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo));
1059223132Stuexen		memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_authinfo));
1060223132Stuexen		msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo));
1061223132Stuexen		cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
1062223132Stuexen		break;
1063223132Stuexen	case SCTP_SENDV_SPA:
1064223152Stuexen		if ((info == NULL) || (infolen < sizeof(struct sctp_sendv_spa))) {
1065223132Stuexen			free(cmsgbuf);
1066223132Stuexen			errno = EINVAL;
1067223132Stuexen			return (-1);
1068223132Stuexen		}
1069223132Stuexen		spa_info = (struct sctp_sendv_spa *)info;
1070223132Stuexen		if (spa_info->sendv_flags & SCTP_SEND_SNDINFO_VALID) {
1071223132Stuexen			cmsg->cmsg_level = IPPROTO_SCTP;
1072223132Stuexen			cmsg->cmsg_type = SCTP_SNDINFO;
1073223132Stuexen			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
1074223132Stuexen			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_sndinfo, sizeof(struct sctp_sndinfo));
1075223132Stuexen			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
1076223132Stuexen			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo)));
1077294910Stuexen			assoc_id = &(spa_info->sendv_sndinfo.snd_assoc_id);
1078223132Stuexen		}
1079223132Stuexen		if (spa_info->sendv_flags & SCTP_SEND_PRINFO_VALID) {
1080223132Stuexen			cmsg->cmsg_level = IPPROTO_SCTP;
1081223132Stuexen			cmsg->cmsg_type = SCTP_PRINFO;
1082223132Stuexen			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
1083223132Stuexen			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_prinfo, sizeof(struct sctp_prinfo));
1084223132Stuexen			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
1085223132Stuexen			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo)));
1086223132Stuexen		}
1087223132Stuexen		if (spa_info->sendv_flags & SCTP_SEND_AUTHINFO_VALID) {
1088223132Stuexen			cmsg->cmsg_level = IPPROTO_SCTP;
1089223132Stuexen			cmsg->cmsg_type = SCTP_AUTHINFO;
1090223132Stuexen			cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo));
1091223132Stuexen			memcpy(CMSG_DATA(cmsg), &spa_info->sendv_authinfo, sizeof(struct sctp_authinfo));
1092223132Stuexen			msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo));
1093223132Stuexen			cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo)));
1094223132Stuexen		}
1095223132Stuexen		break;
1096223132Stuexen	default:
1097223132Stuexen		free(cmsgbuf);
1098223132Stuexen		errno = EINVAL;
1099223132Stuexen		return (-1);
1100223132Stuexen	}
1101223132Stuexen	addr = addrs;
1102223152Stuexen	msg.msg_name = NULL;
1103223152Stuexen	msg.msg_namelen = 0;
1104223152Stuexen
1105223152Stuexen	for (i = 0; i < addrcnt; i++) {
1106223132Stuexen		switch (addr->sa_family) {
1107223132Stuexen		case AF_INET:
1108223152Stuexen			addr_len = (socklen_t) sizeof(struct sockaddr_in);
1109223152Stuexen			addr_in = (struct sockaddr_in *)addr;
1110223152Stuexen			if (addr_in->sin_len != addr_len) {
1111223152Stuexen				free(cmsgbuf);
1112223152Stuexen				errno = EINVAL;
1113223152Stuexen				return (-1);
1114223152Stuexen			}
1115223152Stuexen			if (i == 0) {
1116223152Stuexen				port = addr_in->sin_port;
1117223152Stuexen			} else {
1118223152Stuexen				if (port == addr_in->sin_port) {
1119223152Stuexen					cmsg->cmsg_level = IPPROTO_SCTP;
1120223152Stuexen					cmsg->cmsg_type = SCTP_DSTADDRV4;
1121223152Stuexen					cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
1122223152Stuexen					memcpy(CMSG_DATA(cmsg), &addr_in->sin_addr, sizeof(struct in_addr));
1123223152Stuexen					msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr));
1124223152Stuexen					cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in_addr)));
1125223152Stuexen				} else {
1126223152Stuexen					free(cmsgbuf);
1127223152Stuexen					errno = EINVAL;
1128223152Stuexen					return (-1);
1129223152Stuexen				}
1130223152Stuexen			}
1131223132Stuexen			break;
1132223132Stuexen		case AF_INET6:
1133223152Stuexen			addr_len = (socklen_t) sizeof(struct sockaddr_in6);
1134223152Stuexen			addr_in6 = (struct sockaddr_in6 *)addr;
1135223152Stuexen			if (addr_in6->sin6_len != addr_len) {
1136223152Stuexen				free(cmsgbuf);
1137223152Stuexen				errno = EINVAL;
1138223152Stuexen				return (-1);
1139223152Stuexen			}
1140223152Stuexen			if (i == 0) {
1141223152Stuexen				port = addr_in6->sin6_port;
1142223152Stuexen			} else {
1143223152Stuexen				if (port == addr_in6->sin6_port) {
1144223152Stuexen					cmsg->cmsg_level = IPPROTO_SCTP;
1145223152Stuexen					cmsg->cmsg_type = SCTP_DSTADDRV6;
1146223152Stuexen					cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_addr));
1147223152Stuexen					memcpy(CMSG_DATA(cmsg), &addr_in6->sin6_addr, sizeof(struct in6_addr));
1148223152Stuexen					msg.msg_controllen += CMSG_SPACE(sizeof(struct in6_addr));
1149223152Stuexen					cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in6_addr)));
1150223152Stuexen				} else {
1151223152Stuexen					free(cmsgbuf);
1152223152Stuexen					errno = EINVAL;
1153223152Stuexen					return (-1);
1154223152Stuexen				}
1155223152Stuexen			}
1156223132Stuexen			break;
1157223132Stuexen		default:
1158223132Stuexen			free(cmsgbuf);
1159223132Stuexen			errno = EINVAL;
1160223132Stuexen			return (-1);
1161223132Stuexen		}
1162223152Stuexen		if (i == 0) {
1163223152Stuexen			msg.msg_name = addr;
1164223152Stuexen			msg.msg_namelen = addr_len;
1165223132Stuexen		}
1166223152Stuexen		addr = (struct sockaddr *)((caddr_t)addr + addr_len);
1167165242Srrs	}
1168223132Stuexen	if (msg.msg_controllen == 0) {
1169223132Stuexen		msg.msg_control = NULL;
1170223132Stuexen	}
1171223132Stuexen	msg.msg_iov = (struct iovec *)iov;
1172223132Stuexen	msg.msg_iovlen = iovcnt;
1173223132Stuexen	msg.msg_flags = 0;
1174223132Stuexen	ret = sendmsg(sd, &msg, flags);
1175223132Stuexen	free(cmsgbuf);
1176294910Stuexen	if ((ret >= 0) && (addrs != NULL) && (assoc_id != NULL)) {
1177294910Stuexen		*assoc_id = sctp_getassocid(sd, addrs);
1178294910Stuexen	}
1179223132Stuexen	return (ret);
1180165242Srrs}
1181165242Srrs
1182165242Srrs
1183165242Srrs#if !defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
1184165242Srrs
1185165242Srrsint
1186165242Srrssctp_peeloff(int sd, sctp_assoc_t assoc_id)
1187165242Srrs{
1188165242Srrs	/* NOT supported, return invalid sd */
1189165242Srrs	errno = ENOTSUP;
1190165242Srrs	return (-1);
1191165242Srrs}
1192165242Srrs
1193165242Srrs#endif
1194165242Srrs#if defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
1195165242Srrsint
1196165242Srrssctp_peeloff(int sd, sctp_assoc_t assoc_id)
1197165242Srrs{
1198165242Srrs	return (syscall(SYS_sctp_peeloff, sd, assoc_id));
1199165242Srrs}
1200165242Srrs
1201165242Srrs#endif
1202166884Srrs
1203166885Srrs#undef SCTP_CONTROL_VEC_SIZE_RCV
1204