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