xref: /illumos-gate/usr/src/lib/libsctp/common/sctp.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 */
51 union 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  */
65 int
66 sctp_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  */
104 int
105 sctp_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 
159 void
160 sctp_freepaddrs(void *addrs)
161 {
162 	free(addrs);
163 }
164 
165 int
166 sctp_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 
220 void
221 sctp_freeladdrs(void *addrs)
222 {
223 	free(addrs);
224 }
225 
226 int
227 sctp_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  */
247 int
248 sctp_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 
260 ssize_t
261 sctp_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 
308 static ssize_t
309 sctp_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 
348 ssize_t
349 sctp_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 
362 ssize_t
363 sctp_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