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
sctp_bindx(int sock,void * addrs,int addrcnt,int flags)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
sctp_getpaddrs(int sock,sctp_assoc_t id,void ** addrs)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
sctp_freepaddrs(void * addrs)158 sctp_freepaddrs(void *addrs)
159 {
160 free(addrs);
161 }
162
163 int
sctp_getladdrs(int sock,sctp_assoc_t id,void ** addrs)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
sctp_freeladdrs(void * addrs)219 sctp_freeladdrs(void *addrs)
220 {
221 free(addrs);
222 }
223
224 int
sctp_opt_info(int sock,sctp_assoc_t id,int opt,void * arg,socklen_t * len)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
sctp_peeloff(int sock,sctp_assoc_t id)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
sctp_recvmsg(int s,void * msg,size_t len,struct sockaddr * from,socklen_t * fromlen,struct sctp_sndrcvinfo * sinfo,int * msg_flags)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
sctp_send_common(int s,const void * msg,size_t len,const struct sockaddr * to,socklen_t tolen,uint32_t ppid,uint32_t sinfo_flags,uint16_t stream_no,uint32_t timetolive,uint32_t context,sctp_assoc_t aid,int flags)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
sctp_send(int s,const void * msg,size_t len,const struct sctp_sndrcvinfo * sinfo,int flags)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
sctp_sendmsg(int s,const void * msg,size_t len,const struct sockaddr * to,socklen_t tolen,uint32_t ppid,uint32_t flags,uint16_t stream_no,uint32_t timetolive,uint32_t context)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