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
sctp_bindx(int sock,void * addrs,int addrcnt,int flags)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
sctp_getpaddrs(int sock,sctp_assoc_t id,void ** addrs)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
sctp_freepaddrs(void * addrs)160 sctp_freepaddrs(void *addrs)
161 {
162 free(addrs);
163 }
164
165 int
sctp_getladdrs(int sock,sctp_assoc_t id,void ** addrs)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
sctp_freeladdrs(void * addrs)221 sctp_freeladdrs(void *addrs)
222 {
223 free(addrs);
224 }
225
226 int
sctp_opt_info(int sock,sctp_assoc_t id,int opt,void * arg,socklen_t * len)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
sctp_peeloff(int sock,sctp_assoc_t id)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
sctp_recvmsg(int s,void * msg,size_t len,struct sockaddr * from,socklen_t * fromlen,struct sctp_sndrcvinfo * sinfo,int * msg_flags)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
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)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
sctp_send(int s,const void * msg,size_t len,const struct sctp_sndrcvinfo * sinfo,int flags)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
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)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