1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#define	_XPG4_2
30#define	__EXTENSIONS__
31
32#include <assert.h>
33#include <sys/types.h>
34#include <sys/uio.h>
35#include <sys/socket.h>
36#include <sys/stropts.h>
37#include <sys/stream.h>
38#include <sys/socketvar.h>
39#include <sys/sockio.h>
40
41#include <errno.h>
42#include <stdlib.h>
43#include <unistd.h>
44#include <stropts.h>
45#include <stdio.h>
46#include <strings.h>
47#include <netinet/in.h>
48#include <netinet/sctp.h>
49
50/* This will hold either a v4 or a v6 sockaddr */
51union sockaddr_storage_v6 {
52	struct sockaddr_in in;
53	struct sockaddr_in6 in6;
54};
55
56/*
57 * This file implements all the libsctp calls.
58 */
59
60/*
61 * To bind a list of addresses to a socket.  If the socket is
62 * v4, the type of the list of addresses is (struct in_addr).
63 * If the socket is v6, the type is (struct in6_addr).
64 */
65int
66sctp_bindx(int sock, void *addrs, int addrcnt, int flags)
67{
68	socklen_t sz;
69
70	if (addrs == NULL || addrcnt == 0) {
71		errno = EINVAL;
72		return (-1);
73	}
74
75	/* Assume the caller uses the correct family type. */
76	switch (((struct sockaddr *)addrs)->sa_family) {
77	case AF_INET:
78		sz = sizeof (struct sockaddr_in);
79		break;
80	case AF_INET6:
81		sz = sizeof (struct sockaddr_in6);
82		break;
83	default:
84		errno = EAFNOSUPPORT;
85		return (-1);
86	}
87
88	switch (flags) {
89	case SCTP_BINDX_ADD_ADDR:
90		return (setsockopt(sock, IPPROTO_SCTP, SCTP_ADD_ADDR, addrs,
91		    sz * addrcnt));
92	case SCTP_BINDX_REM_ADDR:
93		return (setsockopt(sock, IPPROTO_SCTP, SCTP_REM_ADDR, addrs,
94		    sz * addrcnt));
95	default:
96		errno = EINVAL;
97		return (-1);
98	}
99}
100
101/*
102 * XXX currently not atomic -- need a better way to do this.
103 */
104int
105sctp_getpaddrs(int sock, sctp_assoc_t id, void **addrs)
106{
107	uint32_t naddrs;
108	socklen_t bufsz;
109	struct sctpopt opt;
110
111	if (addrs == NULL) {
112		errno = EINVAL;
113		return (-1);
114	}
115
116	/* First, find out how many peer addresses there are. */
117	*addrs = NULL;
118
119	opt.sopt_aid = id;
120	opt.sopt_name = SCTP_GET_NPADDRS;
121	opt.sopt_val = (caddr_t)&naddrs;
122	opt.sopt_len = sizeof (naddrs);
123	if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
124		return (-1);
125	}
126	if (naddrs == 0)
127		return (0);
128
129	/*
130	 * Now we can get all the peer addresses.  This will over allocate
131	 * space for v4 socket.  But it should be OK and save us
132	 * the job to find out if it is a v4 or v6 socket.
133	 */
134	bufsz = sizeof (union sockaddr_storage_v6) * naddrs;
135	if ((*addrs = malloc(bufsz)) == NULL) {
136		return (-1);
137	}
138	opt.sopt_name = SCTP_GET_PADDRS;
139	opt.sopt_val = *addrs;
140	opt.sopt_len = bufsz;
141	if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
142		free(*addrs);
143		*addrs = NULL;
144		return (-1);
145	}
146
147	/* Calculate the number of addresses returned. */
148	switch (((struct sockaddr *)*addrs)->sa_family) {
149	case AF_INET:
150		naddrs = opt.sopt_len / sizeof (struct sockaddr_in);
151		break;
152	case AF_INET6:
153		naddrs = opt.sopt_len / sizeof (struct sockaddr_in6);
154		break;
155	}
156	return (naddrs);
157}
158
159void
160sctp_freepaddrs(void *addrs)
161{
162	free(addrs);
163}
164
165int
166sctp_getladdrs(int sock, sctp_assoc_t id, void **addrs)
167{
168	uint32_t naddrs;
169	socklen_t bufsz;
170	struct sctpopt opt;
171
172	if (addrs == NULL) {
173		errno = EINVAL;
174		return (-1);
175	}
176
177	/* First, try to find out how many bound addresses there are. */
178	*addrs = NULL;
179
180	opt.sopt_aid = id;
181	opt.sopt_name = SCTP_GET_NLADDRS;
182	opt.sopt_val = (caddr_t)&naddrs;
183	opt.sopt_len = sizeof (naddrs);
184	if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
185		return (-1);
186	}
187	if (naddrs == 0)
188		return (0);
189
190	/*
191	 * Now we can get all the bound addresses.  This will over allocate
192	 * space for v4 socket.  But it should be OK and save us
193	 * the job to find out if it is a v4 or v6 socket.
194	 */
195	bufsz = sizeof (union sockaddr_storage_v6) * naddrs;
196	if ((*addrs = malloc(bufsz)) == NULL) {
197		return (-1);
198	}
199	opt.sopt_name = SCTP_GET_LADDRS;
200	opt.sopt_val = *addrs;
201	opt.sopt_len = bufsz;
202	if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
203		free(*addrs);
204		*addrs = NULL;
205		return (-1);
206	}
207
208	/* Calculate the number of addresses returned. */
209	switch (((struct sockaddr *)*addrs)->sa_family) {
210	case AF_INET:
211		naddrs = opt.sopt_len / sizeof (struct sockaddr_in);
212		break;
213	case AF_INET6:
214		naddrs = opt.sopt_len / sizeof (struct sockaddr_in6);
215		break;
216	}
217	return (naddrs);
218}
219
220void
221sctp_freeladdrs(void *addrs)
222{
223	free(addrs);
224}
225
226int
227sctp_opt_info(int sock, sctp_assoc_t id, int opt, void *arg, socklen_t *len)
228{
229	struct sctpopt sopt;
230
231	sopt.sopt_aid = id;
232	sopt.sopt_name = opt;
233	sopt.sopt_val = arg;
234	sopt.sopt_len = *len;
235
236	if (ioctl(sock, SIOCSCTPGOPT, &sopt) == -1) {
237		return (-1);
238	}
239	*len = sopt.sopt_len;
240	return (0);
241}
242
243/*
244 * Branch off an association to its own socket. ioctl() allocates and
245 * returns new fd.
246 */
247int
248sctp_peeloff(int sock, sctp_assoc_t id)
249{
250	int fd;
251
252	fd = id;
253	if (ioctl(sock, SIOCSCTPPEELOFF, &fd) == -1) {
254		return (-1);
255	}
256	return (fd);
257}
258
259
260ssize_t
261sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from,
262    socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags)
263{
264	struct msghdr hdr;
265	struct iovec iov;
266	struct cmsghdr *cmsg;
267	char cinmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT];
268	int err;
269
270	hdr.msg_name = from;
271	hdr.msg_namelen = (fromlen != NULL) ? *fromlen : 0;
272	hdr.msg_iov = &iov;
273	hdr.msg_iovlen = 1;
274	if (sinfo != NULL) {
275		hdr.msg_control = (void *)_CMSG_HDR_ALIGN(cinmsg);
276		hdr.msg_controllen = sizeof (cinmsg) -
277		    (_CMSG_HDR_ALIGN(cinmsg) - (uintptr_t)cinmsg);
278	} else {
279		hdr.msg_control = NULL;
280		hdr.msg_controllen = 0;
281	}
282
283	iov.iov_base = msg;
284	iov.iov_len = len;
285	err = recvmsg(s, &hdr, msg_flags == NULL ? 0 : *msg_flags);
286	if (err == -1) {
287		return (-1);
288	}
289	if (fromlen != NULL) {
290		*fromlen = hdr.msg_namelen;
291	}
292	if (msg_flags != NULL) {
293		*msg_flags = hdr.msg_flags;
294	}
295	if (sinfo != NULL) {
296		for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
297			cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
298			if (cmsg->cmsg_level == IPPROTO_SCTP &&
299			    cmsg->cmsg_type == SCTP_SNDRCV) {
300				bcopy(CMSG_DATA(cmsg), sinfo, sizeof (*sinfo));
301				break;
302			}
303		}
304	}
305	return (err);
306}
307
308static ssize_t
309sctp_send_common(int s, const void *msg, size_t len, const struct sockaddr *to,
310    socklen_t tolen, uint32_t ppid, uint32_t sinfo_flags, uint16_t stream_no,
311    uint32_t timetolive, uint32_t context, sctp_assoc_t aid, int flags)
312{
313	struct msghdr hdr;
314	struct iovec iov;
315	struct sctp_sndrcvinfo *sinfo;
316	struct cmsghdr *cmsg;
317	char coutmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT];
318
319	hdr.msg_name = (caddr_t)to;
320	hdr.msg_namelen = tolen;
321	hdr.msg_iov = &iov;
322	hdr.msg_iovlen = 1;
323	hdr.msg_control = (void *)_CMSG_HDR_ALIGN(coutmsg);
324	hdr.msg_controllen = sizeof (*cmsg) + sizeof (*sinfo);
325
326	iov.iov_len = len;
327	iov.iov_base = (caddr_t)msg;
328
329	cmsg = CMSG_FIRSTHDR(&hdr);
330	cmsg->cmsg_level = IPPROTO_SCTP;
331	cmsg->cmsg_type = SCTP_SNDRCV;
332	cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sinfo);
333
334	sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
335	sinfo->sinfo_stream = stream_no;
336	sinfo->sinfo_ssn = 0;
337	sinfo->sinfo_flags = sinfo_flags;
338	sinfo->sinfo_ppid = ppid;
339	sinfo->sinfo_context = context;
340	sinfo->sinfo_timetolive = timetolive;
341	sinfo->sinfo_tsn = 0;
342	sinfo->sinfo_cumtsn = 0;
343	sinfo->sinfo_assoc_id = aid;
344
345	return (sendmsg(s, &hdr, flags));
346}
347
348ssize_t
349sctp_send(int s, const void *msg, size_t len,
350    const struct sctp_sndrcvinfo *sinfo, int flags)
351{
352	/* Note that msg can be NULL for pure control message. */
353	if (sinfo == NULL) {
354		errno = EINVAL;
355		return (-1);
356	}
357	return (sctp_send_common(s, msg, len, NULL, 0, sinfo->sinfo_ppid,
358	    sinfo->sinfo_flags, sinfo->sinfo_stream, sinfo->sinfo_timetolive,
359	    sinfo->sinfo_context, sinfo->sinfo_assoc_id, flags));
360}
361
362ssize_t
363sctp_sendmsg(int s, const void *msg, size_t len, const struct sockaddr *to,
364    socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream_no,
365    uint32_t timetolive, uint32_t context)
366{
367	return (sctp_send_common(s, msg, len, to, tolen, ppid, flags,
368	    stream_no, timetolive, context, 0, 0));
369}
370