]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libc/net/sctp_sys_calls.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / lib / libc / net / sctp_sys_calls.c
1 /*      $KAME: sctp_sys_calls.c,v 1.9 2004/08/17 06:08:53 itojun Exp $ */
2
3 /*
4  * Copyright (C) 2002-2007 Cisco Systems Inc,
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/errno.h>
41 #include <sys/syscall.h>
42 #include <sys/uio.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <netinet/sctp_uio.h>
46 #include <netinet/sctp.h>
47
48 #include <net/if_dl.h>
49
50 #ifndef IN6_IS_ADDR_V4MAPPED
51 #define IN6_IS_ADDR_V4MAPPED(a)               \
52         ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \
53          (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \
54          (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)))
55 #endif
56
57
58 #define SCTP_CONTROL_VEC_SIZE_SND   8192
59 #define SCTP_CONTROL_VEC_SIZE_RCV  16384
60 #define SCTP_STACK_BUF_SIZE         2048
61 #define SCTP_SMALL_IOVEC_SIZE          2
62
63 #ifdef SCTP_DEBUG_PRINT_ADDRESS
64
65 #define SCTP_STRING_BUF_SZ 256
66
67 static void
68 SCTPPrintAnAddress(struct sockaddr *a)
69 {
70         char stringToPrint[SCTP_STRING_BUF_SZ];
71         u_short prt;
72         char *srcaddr, *txt;
73
74         if (a == NULL) {
75                 printf("NULL\n");
76                 return;
77         }
78         if (a->sa_family == AF_INET) {
79                 srcaddr = (char *)&((struct sockaddr_in *)a)->sin_addr;
80                 txt = "IPv4 Address: ";
81                 prt = ntohs(((struct sockaddr_in *)a)->sin_port);
82         } else if (a->sa_family == AF_INET6) {
83                 srcaddr = (char *)&((struct sockaddr_in6 *)a)->sin6_addr;
84                 prt = ntohs(((struct sockaddr_in6 *)a)->sin6_port);
85                 txt = "IPv6 Address: ";
86         } else if (a->sa_family == AF_LINK) {
87                 int i;
88                 char tbuf[SCTP_STRING_BUF_SZ];
89                 u_char adbuf[SCTP_STRING_BUF_SZ];
90                 struct sockaddr_dl *dl;
91
92                 dl = (struct sockaddr_dl *)a;
93                 strncpy(tbuf, dl->sdl_data, dl->sdl_nlen);
94                 tbuf[dl->sdl_nlen] = 0;
95                 printf("Intf:%s (len:%d)Interface index:%d type:%x(%d) ll-len:%d ",
96                     tbuf,
97                     dl->sdl_nlen,
98                     dl->sdl_index,
99                     dl->sdl_type,
100                     dl->sdl_type,
101                     dl->sdl_alen
102                     );
103                 memcpy(adbuf, LLADDR(dl), dl->sdl_alen);
104                 for (i = 0; i < dl->sdl_alen; i++) {
105                         printf("%2.2x", adbuf[i]);
106                         if (i < (dl->sdl_alen - 1))
107                                 printf(":");
108                 }
109                 printf("\n");
110                 return;
111         } else {
112                 return;
113         }
114         if (inet_ntop(a->sa_family, srcaddr, stringToPrint, sizeof(stringToPrint))) {
115                 if (a->sa_family == AF_INET6) {
116                         printf("%s%s:%d scope:%d\n",
117                             txt, stringToPrint, prt,
118                             ((struct sockaddr_in6 *)a)->sin6_scope_id);
119                 } else {
120                         printf("%s%s:%d\n", txt, stringToPrint, prt);
121                 }
122
123         } else {
124                 printf("%s unprintable?\n", txt);
125         }
126 }
127
128 #endif                          /* SCTP_DEBUG_PRINT_ADDRESS */
129
130 static void
131 in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
132 {
133         bzero(sin, sizeof(*sin));
134         sin->sin_len = sizeof(struct sockaddr_in);
135         sin->sin_family = AF_INET;
136         sin->sin_port = sin6->sin6_port;
137         sin->sin_addr.s_addr = sin6->sin6_addr.__u6_addr.__u6_addr32[3];
138 }
139
140 int
141 sctp_getaddrlen(sa_family_t family)
142 {
143         int error, sd;
144         socklen_t siz;
145         struct sctp_assoc_value av;
146
147         av.assoc_value = family;
148         siz = sizeof(av);
149 #if defined(AF_INET)
150         sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
151 #elif defined(AF_INET6)
152         sd = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP);
153 #endif
154         if (sd == -1) {
155                 return (-1);
156         }
157         error = getsockopt(sd, IPPROTO_SCTP, SCTP_GET_ADDR_LEN, &av, &siz);
158         close(sd);
159         if (error == 0) {
160                 return ((int)av.assoc_value);
161         } else {
162                 return (-1);
163         }
164 }
165
166 int
167 sctp_connectx(int sd, const struct sockaddr *addrs, int addrcnt,
168     sctp_assoc_t * id)
169 {
170         char buf[SCTP_STACK_BUF_SIZE];
171         int i, ret, cnt, *aa;
172         char *cpto;
173         const struct sockaddr *at;
174         sctp_assoc_t *p_id;
175         size_t len = sizeof(int);
176
177         /* validate the address count and list */
178         if ((addrs == NULL) || (addrcnt <= 0)) {
179                 errno = EINVAL;
180                 return (-1);
181         }
182         at = addrs;
183         cnt = 0;
184         cpto = ((caddr_t)buf + sizeof(int));
185         /* validate all the addresses and get the size */
186         for (i = 0; i < addrcnt; i++) {
187                 if (at->sa_family == AF_INET) {
188                         if (at->sa_len != sizeof(struct sockaddr_in)) {
189                                 errno = EINVAL;
190                                 return (-1);
191                         }
192                         memcpy(cpto, at, at->sa_len);
193                         cpto = ((caddr_t)cpto + at->sa_len);
194                         len += at->sa_len;
195                 } else if (at->sa_family == AF_INET6) {
196                         if (at->sa_len != sizeof(struct sockaddr_in6)) {
197                                 errno = EINVAL;
198                                 return (-1);
199                         }
200                         if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) {
201                                 len += sizeof(struct sockaddr_in);
202                                 in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at);
203                                 cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in));
204                                 len += sizeof(struct sockaddr_in);
205                         } else {
206                                 memcpy(cpto, at, at->sa_len);
207                                 cpto = ((caddr_t)cpto + at->sa_len);
208                                 len += at->sa_len;
209                         }
210                 } else {
211                         errno = EINVAL;
212                         return (-1);
213                 }
214                 if (len > (sizeof(buf) - sizeof(int))) {
215                         /* Never enough memory */
216                         errno = E2BIG;
217                         return (-1);
218                 }
219                 at = (struct sockaddr *)((caddr_t)at + at->sa_len);
220                 cnt++;
221         }
222         /* do we have any? */
223         if (cnt == 0) {
224                 errno = EINVAL;
225                 return (-1);
226         }
227         aa = (int *)buf;
228         *aa = cnt;
229         ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf,
230             (socklen_t) len);
231         if ((ret == 0) && id) {
232                 p_id = (sctp_assoc_t *) buf;
233                 *id = *p_id;
234         }
235         return (ret);
236 }
237
238 int
239 sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
240 {
241         struct sctp_getaddresses *gaddrs;
242         struct sockaddr *sa;
243         struct sockaddr_in *sin;
244         struct sockaddr_in6 *sin6;
245         int i, sz, argsz;
246         uint16_t sport = 0;
247
248         /* validate the flags */
249         if ((flags != SCTP_BINDX_ADD_ADDR) &&
250             (flags != SCTP_BINDX_REM_ADDR)) {
251                 errno = EFAULT;
252                 return (-1);
253         }
254         /* validate the address count and list */
255         if ((addrcnt <= 0) || (addrs == NULL)) {
256                 errno = EINVAL;
257                 return (-1);
258         }
259         argsz = (sizeof(struct sockaddr_storage) +
260             sizeof(struct sctp_getaddresses));
261         gaddrs = (struct sctp_getaddresses *)calloc(1, argsz);
262         if (gaddrs == NULL) {
263                 errno = ENOMEM;
264                 return (-1);
265         }
266         /* First pre-screen the addresses */
267         sa = addrs;
268         for (i = 0; i < addrcnt; i++) {
269                 sz = sa->sa_len;
270                 if (sa->sa_family == AF_INET) {
271                         if (sa->sa_len != sizeof(struct sockaddr_in))
272                                 goto out_error;
273                         sin = (struct sockaddr_in *)sa;
274                         if (sin->sin_port) {
275                                 /* non-zero port, check or save */
276                                 if (sport) {
277                                         /* Check against our port */
278                                         if (sport != sin->sin_port) {
279                                                 goto out_error;
280                                         }
281                                 } else {
282                                         /* save off the port */
283                                         sport = sin->sin_port;
284                                 }
285                         }
286                 } else if (sa->sa_family == AF_INET6) {
287                         if (sa->sa_len != sizeof(struct sockaddr_in6))
288                                 goto out_error;
289                         sin6 = (struct sockaddr_in6 *)sa;
290                         if (sin6->sin6_port) {
291                                 /* non-zero port, check or save */
292                                 if (sport) {
293                                         /* Check against our port */
294                                         if (sport != sin6->sin6_port) {
295                                                 goto out_error;
296                                         }
297                                 } else {
298                                         /* save off the port */
299                                         sport = sin6->sin6_port;
300                                 }
301                         }
302                 } else {
303                         /* invalid address family specified */
304                         goto out_error;
305                 }
306
307
308         }
309         sa = addrs;
310         /*
311          * Now if there was a port mentioned, assure that the first address
312          * has that port to make sure it fails or succeeds correctly.
313          */
314         if (sport) {
315                 sin = (struct sockaddr_in *)sa;
316                 sin->sin_port = sport;
317         }
318         for (i = 0; i < addrcnt; i++) {
319                 sz = sa->sa_len;
320                 if (sa->sa_family == AF_INET) {
321                         if (sa->sa_len != sizeof(struct sockaddr_in))
322                                 goto out_error;
323                 } else if (sa->sa_family == AF_INET6) {
324                         if (sa->sa_len != sizeof(struct sockaddr_in6))
325                                 goto out_error;
326                 } else {
327                         /* invalid address family specified */
328         out_error:
329                         free(gaddrs);
330                         errno = EINVAL;
331                         return (-1);
332                 }
333                 memset(gaddrs, 0, argsz);
334                 gaddrs->sget_assoc_id = 0;
335                 memcpy(gaddrs->addr, sa, sz);
336                 if (setsockopt(sd, IPPROTO_SCTP, flags, gaddrs,
337                     (socklen_t) argsz) != 0) {
338                         free(gaddrs);
339                         return (-1);
340                 }
341                 sa = (struct sockaddr *)((caddr_t)sa + sz);
342         }
343         free(gaddrs);
344         return (0);
345 }
346
347
348 int
349 sctp_opt_info(int sd, sctp_assoc_t id, int opt, void *arg, socklen_t * size)
350 {
351         if (arg == NULL) {
352                 errno = EINVAL;
353                 return (-1);
354         }
355         switch (opt) {
356         case SCTP_RTOINFO:
357                 ((struct sctp_rtoinfo *)arg)->srto_assoc_id = id;
358                 break;
359         case SCTP_ASSOCINFO:
360                 ((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
361                 break;
362         case SCTP_DEFAULT_SEND_PARAM:
363                 ((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
364                 break;
365         case SCTP_SET_PEER_PRIMARY_ADDR:
366                 ((struct sctp_setpeerprim *)arg)->sspp_assoc_id = id;
367                 break;
368         case SCTP_PRIMARY_ADDR:
369                 ((struct sctp_setprim *)arg)->ssp_assoc_id = id;
370                 break;
371         case SCTP_PEER_ADDR_PARAMS:
372                 ((struct sctp_paddrparams *)arg)->spp_assoc_id = id;
373                 break;
374         case SCTP_MAXSEG:
375                 ((struct sctp_assoc_value *)arg)->assoc_id = id;
376                 break;
377         case SCTP_AUTH_KEY:
378                 ((struct sctp_authkey *)arg)->sca_assoc_id = id;
379                 break;
380         case SCTP_AUTH_ACTIVE_KEY:
381                 ((struct sctp_authkeyid *)arg)->scact_assoc_id = id;
382                 break;
383         case SCTP_DELAYED_SACK:
384                 ((struct sctp_sack_info *)arg)->sack_assoc_id = id;
385                 break;
386         case SCTP_CONTEXT:
387                 ((struct sctp_assoc_value *)arg)->assoc_id = id;
388                 break;
389         case SCTP_STATUS:
390                 ((struct sctp_status *)arg)->sstat_assoc_id = id;
391                 break;
392         case SCTP_GET_PEER_ADDR_INFO:
393                 ((struct sctp_paddrinfo *)arg)->spinfo_assoc_id = id;
394                 break;
395         case SCTP_PEER_AUTH_CHUNKS:
396                 ((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
397                 break;
398         case SCTP_LOCAL_AUTH_CHUNKS:
399                 ((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
400                 break;
401         default:
402                 break;
403         }
404         return (getsockopt(sd, IPPROTO_SCTP, opt, arg, size));
405 }
406
407 int
408 sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
409 {
410         struct sctp_getaddresses *addrs;
411         struct sockaddr *sa;
412         struct sockaddr *re;
413         sctp_assoc_t asoc;
414         caddr_t lim;
415         socklen_t siz;
416         int cnt;
417
418         if (raddrs == NULL) {
419                 errno = EFAULT;
420                 return (-1);
421         }
422         asoc = id;
423         siz = sizeof(sctp_assoc_t);
424         if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_REMOTE_ADDR_SIZE,
425             &asoc, &siz) != 0) {
426                 return (-1);
427         }
428         /* size required is returned in 'asoc' */
429         siz = (size_t)asoc;
430         siz += sizeof(struct sctp_getaddresses);
431         addrs = calloc(1, siz);
432         if (addrs == NULL) {
433                 return (-1);
434         }
435         addrs->sget_assoc_id = id;
436         /* Now lets get the array of addresses */
437         if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_PEER_ADDRESSES,
438             addrs, &siz) != 0) {
439                 free(addrs);
440                 return (-1);
441         }
442         re = (struct sockaddr *)&addrs->addr[0];
443         *raddrs = re;
444         cnt = 0;
445         sa = (struct sockaddr *)&addrs->addr[0];
446         lim = (caddr_t)addrs + siz;
447         while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
448                 sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
449                 cnt++;
450         }
451         return (cnt);
452 }
453
454 void
455 sctp_freepaddrs(struct sockaddr *addrs)
456 {
457         /* Take away the hidden association id */
458         void *fr_addr;
459
460         fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
461         /* Now free it */
462         free(fr_addr);
463 }
464
465 int
466 sctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **raddrs)
467 {
468         struct sctp_getaddresses *addrs;
469         struct sockaddr *re;
470         caddr_t lim;
471         struct sockaddr *sa;
472         int size_of_addresses;
473         socklen_t siz;
474         int cnt;
475
476         if (raddrs == NULL) {
477                 errno = EFAULT;
478                 return (-1);
479         }
480         size_of_addresses = 0;
481         siz = sizeof(int);
482         if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDR_SIZE,
483             &size_of_addresses, &siz) != 0) {
484                 errno = ENOMEM;
485                 return (-1);
486         }
487         if (size_of_addresses == 0) {
488                 errno = ENOTCONN;
489                 return (-1);
490         }
491         siz = size_of_addresses + sizeof(struct sockaddr_storage);
492         siz += sizeof(struct sctp_getaddresses);
493         addrs = calloc(1, siz);
494         if (addrs == NULL) {
495                 errno = ENOMEM;
496                 return (-1);
497         }
498         addrs->sget_assoc_id = id;
499         /* Now lets get the array of addresses */
500         if (getsockopt(sd, IPPROTO_SCTP, SCTP_GET_LOCAL_ADDRESSES, addrs,
501             &siz) != 0) {
502                 free(addrs);
503                 errno = ENOMEM;
504                 return (-1);
505         }
506         re = (struct sockaddr *)&addrs->addr[0];
507         *raddrs = re;
508         cnt = 0;
509         sa = (struct sockaddr *)&addrs->addr[0];
510         lim = (caddr_t)addrs + siz;
511         while (((caddr_t)sa < lim) && (sa->sa_len > 0)) {
512                 sa = (struct sockaddr *)((caddr_t)sa + sa->sa_len);
513                 cnt++;
514         }
515         return (cnt);
516 }
517
518 void
519 sctp_freeladdrs(struct sockaddr *addrs)
520 {
521         /* Take away the hidden association id */
522         void *fr_addr;
523
524         fr_addr = (void *)((caddr_t)addrs - sizeof(sctp_assoc_t));
525         /* Now free it */
526         free(fr_addr);
527 }
528
529
530 ssize_t
531 sctp_sendmsg(int s,
532     const void *data,
533     size_t len,
534     const struct sockaddr *to,
535     socklen_t tolen,
536     u_int32_t ppid,
537     u_int32_t flags,
538     u_int16_t stream_no,
539     u_int32_t timetolive,
540     u_int32_t context)
541 {
542 #ifdef SYS_sctp_generic_sendmsg
543         struct sctp_sndrcvinfo sinfo;
544
545         sinfo.sinfo_ppid = ppid;
546         sinfo.sinfo_flags = flags;
547         sinfo.sinfo_stream = stream_no;
548         sinfo.sinfo_timetolive = timetolive;
549         sinfo.sinfo_context = context;
550         sinfo.sinfo_assoc_id = 0;
551         return (syscall(SYS_sctp_generic_sendmsg, s,
552             data, len, to, tolen, &sinfo, 0));
553 #else
554
555         ssize_t sz;
556         struct msghdr msg;
557         struct sctp_sndrcvinfo *s_info;
558         struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
559         char controlVector[SCTP_CONTROL_VEC_SIZE_RCV];
560         struct cmsghdr *cmsg;
561         struct sockaddr *who = NULL;
562         union {
563                 struct sockaddr_in in;
564                 struct sockaddr_in6 in6;
565         }     addr;
566
567 /*
568   fprintf(io, "sctp_sendmsg(sd:%d, data:%x, len:%d, to:%x, tolen:%d, ppid:%x, flags:%x str:%d ttl:%d ctx:%x\n",
569   s,
570   (u_int)data,
571   (int)len,
572   (u_int)to,
573   (int)tolen,
574   ppid, flags,
575   (int)stream_no,
576   (int)timetolive,
577   (u_int)context);
578   fflush(io);
579 */
580         if ((tolen > 0) && ((to == NULL) || (tolen < sizeof(struct sockaddr)))) {
581                 errno = EINVAL;
582                 return -1;
583         }
584         if (to && (tolen > 0)) {
585                 if (to->sa_family == AF_INET) {
586                         if (tolen != sizeof(struct sockaddr_in)) {
587                                 errno = EINVAL;
588                                 return -1;
589                         }
590                         if ((to->sa_len > 0) && (to->sa_len != sizeof(struct sockaddr_in))) {
591                                 errno = EINVAL;
592                                 return -1;
593                         }
594                         memcpy(&addr, to, sizeof(struct sockaddr_in));
595                         addr.in.sin_len = sizeof(struct sockaddr_in);
596                 } else if (to->sa_family == AF_INET6) {
597                         if (tolen != sizeof(struct sockaddr_in6)) {
598                                 errno = EINVAL;
599                                 return -1;
600                         }
601                         if ((to->sa_len > 0) && (to->sa_len != sizeof(struct sockaddr_in6))) {
602                                 errno = EINVAL;
603                                 return -1;
604                         }
605                         memcpy(&addr, to, sizeof(struct sockaddr_in6));
606                         addr.in6.sin6_len = sizeof(struct sockaddr_in6);
607                 } else {
608                         errno = EAFNOSUPPORT;
609                         return -1;
610                 }
611                 who = (struct sockaddr *)&addr;
612         }
613         iov[0].iov_base = (char *)data;
614         iov[0].iov_len = len;
615         iov[1].iov_base = NULL;
616         iov[1].iov_len = 0;
617
618         if (who) {
619                 msg.msg_name = (caddr_t)who;
620                 msg.msg_namelen = who->sa_len;
621         } else {
622                 msg.msg_name = (caddr_t)NULL;
623                 msg.msg_namelen = 0;
624         }
625         msg.msg_iov = iov;
626         msg.msg_iovlen = 1;
627         msg.msg_control = (caddr_t)controlVector;
628
629         cmsg = (struct cmsghdr *)controlVector;
630
631         cmsg->cmsg_level = IPPROTO_SCTP;
632         cmsg->cmsg_type = SCTP_SNDRCV;
633         cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
634         s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
635
636         s_info->sinfo_stream = stream_no;
637         s_info->sinfo_ssn = 0;
638         s_info->sinfo_flags = flags;
639         s_info->sinfo_ppid = ppid;
640         s_info->sinfo_context = context;
641         s_info->sinfo_assoc_id = 0;
642         s_info->sinfo_timetolive = timetolive;
643         errno = 0;
644         msg.msg_controllen = cmsg->cmsg_len;
645         sz = sendmsg(s, &msg, 0);
646         return (sz);
647 #endif
648 }
649
650
651 sctp_assoc_t
652 sctp_getassocid(int sd, struct sockaddr *sa)
653 {
654         struct sctp_paddrinfo sp;
655         socklen_t siz;
656
657         /* First get the assoc id */
658         siz = sizeof(sp);
659         memset(&sp, 0, sizeof(sp));
660         memcpy((caddr_t)&sp.spinfo_address, sa, sa->sa_len);
661         errno = 0;
662         if (getsockopt(sd, IPPROTO_SCTP,
663             SCTP_GET_PEER_ADDR_INFO, &sp, &siz) != 0) {
664                 return ((sctp_assoc_t) 0);
665         }
666         /* We depend on the fact that 0 can never be returned */
667         return (sp.spinfo_assoc_id);
668 }
669
670 ssize_t
671 sctp_send(int sd, const void *data, size_t len,
672     const struct sctp_sndrcvinfo *sinfo,
673     int flags)
674 {
675
676 #ifdef SYS_sctp_generic_sendmsg
677         struct sockaddr *to = NULL;
678
679         return (syscall(SYS_sctp_generic_sendmsg, sd,
680             data, len, to, 0, sinfo, flags));
681 #else
682         ssize_t sz;
683         struct msghdr msg;
684         struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
685         struct sctp_sndrcvinfo *s_info;
686         char controlVector[SCTP_CONTROL_VEC_SIZE_SND];
687         struct cmsghdr *cmsg;
688
689         if (sinfo == NULL) {
690                 errno = EINVAL;
691                 return (-1);
692         }
693         iov[0].iov_base = (char *)data;
694         iov[0].iov_len = len;
695         iov[1].iov_base = NULL;
696         iov[1].iov_len = 0;
697
698         msg.msg_name = 0;
699         msg.msg_namelen = 0;
700         msg.msg_iov = iov;
701         msg.msg_iovlen = 1;
702         msg.msg_control = (caddr_t)controlVector;
703
704         cmsg = (struct cmsghdr *)controlVector;
705
706         cmsg->cmsg_level = IPPROTO_SCTP;
707         cmsg->cmsg_type = SCTP_SNDRCV;
708         cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
709         s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
710         /* copy in the data */
711         *s_info = *sinfo;
712         errno = 0;
713         msg.msg_controllen = cmsg->cmsg_len;
714         sz = sendmsg(sd, &msg, flags);
715         return (sz);
716 #endif
717 }
718
719
720
721 ssize_t
722 sctp_sendx(int sd, const void *msg, size_t msg_len,
723     struct sockaddr *addrs, int addrcnt,
724     struct sctp_sndrcvinfo *sinfo,
725     int flags)
726 {
727         ssize_t ret;
728         int i, cnt, *aa, saved_errno;
729         char *buf;
730         int add_len, len, no_end_cx = 0;
731         struct sockaddr *at;
732
733         if (addrs == NULL) {
734                 errno = EINVAL;
735                 return (-1);
736         }
737 #ifdef SYS_sctp_generic_sendmsg
738         if (addrcnt < SCTP_SMALL_IOVEC_SIZE) {
739                 socklen_t l;
740
741                 /*
742                  * Quick way, we don't need to do a connectx so lets use the
743                  * syscall directly.
744                  */
745                 l = addrs->sa_len;
746                 return (syscall(SYS_sctp_generic_sendmsg, sd,
747                     msg, msg_len, addrs, l, sinfo, flags));
748         }
749 #endif
750
751         len = sizeof(int);
752         at = addrs;
753         cnt = 0;
754         /* validate all the addresses and get the size */
755         for (i = 0; i < addrcnt; i++) {
756                 if (at->sa_family == AF_INET) {
757                         add_len = sizeof(struct sockaddr_in);
758                 } else if (at->sa_family == AF_INET6) {
759                         add_len = sizeof(struct sockaddr_in6);
760                 } else {
761                         errno = EINVAL;
762                         return (-1);
763                 }
764                 len += add_len;
765                 at = (struct sockaddr *)((caddr_t)at + add_len);
766                 cnt++;
767         }
768         /* do we have any? */
769         if (cnt == 0) {
770                 errno = EINVAL;
771                 return (-1);
772         }
773         buf = malloc(len);
774         if (buf == NULL) {
775                 return (-1);
776         }
777         aa = (int *)buf;
778         *aa = cnt;
779         aa++;
780         memcpy((caddr_t)aa, addrs, (len - sizeof(int)));
781         ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_DELAYED, (void *)buf,
782             (socklen_t) len);
783
784         free(buf);
785         if (ret != 0) {
786                 if (errno == EALREADY) {
787                         no_end_cx = 1;
788                         goto continue_send;
789                 }
790                 return (ret);
791         }
792 continue_send:
793         sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs);
794         if (sinfo->sinfo_assoc_id == 0) {
795                 printf("Huh, can't get associd? TSNH!\n");
796                 (void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
797                     (socklen_t) addrs->sa_len);
798                 errno = ENOENT;
799                 return (-1);
800         }
801         ret = sctp_send(sd, msg, msg_len, sinfo, flags);
802         saved_errno = errno;
803         if (no_end_cx == 0)
804                 (void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs,
805                     (socklen_t) addrs->sa_len);
806
807         errno = saved_errno;
808         return (ret);
809 }
810
811 ssize_t
812 sctp_sendmsgx(int sd,
813     const void *msg,
814     size_t len,
815     struct sockaddr *addrs,
816     int addrcnt,
817     u_int32_t ppid,
818     u_int32_t flags,
819     u_int16_t stream_no,
820     u_int32_t timetolive,
821     u_int32_t context)
822 {
823         struct sctp_sndrcvinfo sinfo;
824
825         memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
826         sinfo.sinfo_ppid = ppid;
827         sinfo.sinfo_flags = flags;
828         sinfo.sinfo_ssn = stream_no;
829         sinfo.sinfo_timetolive = timetolive;
830         sinfo.sinfo_context = context;
831         return sctp_sendx(sd, msg, len, addrs, addrcnt, &sinfo, 0);
832 }
833
834 ssize_t
835 sctp_recvmsg(int s,
836     void *dbuf,
837     size_t len,
838     struct sockaddr *from,
839     socklen_t * fromlen,
840     struct sctp_sndrcvinfo *sinfo,
841     int *msg_flags)
842 {
843 #ifdef SYS_sctp_generic_recvmsg
844         struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
845
846         iov[0].iov_base = dbuf;
847         iov[0].iov_len = len;
848         return (syscall(SYS_sctp_generic_recvmsg, s,
849             iov, 1, from, fromlen, sinfo, msg_flags));
850 #else
851         struct sctp_sndrcvinfo *s_info;
852         ssize_t sz;
853         int sinfo_found = 0;
854         struct msghdr msg;
855         struct iovec iov[SCTP_SMALL_IOVEC_SIZE];
856         char controlVector[SCTP_CONTROL_VEC_SIZE_RCV];
857         struct cmsghdr *cmsg;
858
859         if (msg_flags == NULL) {
860                 errno = EINVAL;
861                 return (-1);
862         }
863         msg.msg_flags = 0;
864         iov[0].iov_base = dbuf;
865         iov[0].iov_len = len;
866         iov[1].iov_base = NULL;
867         iov[1].iov_len = 0;
868         msg.msg_name = (caddr_t)from;
869         if (fromlen == NULL)
870                 msg.msg_namelen = 0;
871         else
872                 msg.msg_namelen = *fromlen;
873         msg.msg_iov = iov;
874         msg.msg_iovlen = 1;
875         msg.msg_control = (caddr_t)controlVector;
876         msg.msg_controllen = sizeof(controlVector);
877         errno = 0;
878         sz = recvmsg(s, &msg, *msg_flags);
879         if (sz <= 0)
880                 return (sz);
881
882         s_info = NULL;
883         len = sz;
884         *msg_flags = msg.msg_flags;
885         if (sinfo)
886                 sinfo->sinfo_assoc_id = 0;
887
888         if ((msg.msg_controllen) && sinfo) {
889                 /*
890                  * parse through and see if we find the sctp_sndrcvinfo (if
891                  * the user wants it).
892                  */
893                 cmsg = (struct cmsghdr *)controlVector;
894                 while (cmsg) {
895                         if ((cmsg->cmsg_len == 0) || (cmsg->cmsg_len > msg.msg_controllen)) {
896                                 break;
897                         }
898                         if (cmsg->cmsg_level == IPPROTO_SCTP) {
899                                 if (cmsg->cmsg_type == SCTP_SNDRCV) {
900                                         /* Got it */
901                                         s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
902                                         /* Copy it to the user */
903                                         if (sinfo)
904                                                 *sinfo = *s_info;
905                                         sinfo_found = 1;
906                                         break;
907                                 } else if (cmsg->cmsg_type == SCTP_EXTRCV) {
908                                         /*
909                                          * Got it, presumably the user has
910                                          * asked for this extra info, so the
911                                          * structure holds more room :-D
912                                          */
913                                         s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
914                                         /* Copy it to the user */
915                                         if (sinfo) {
916                                                 memcpy(sinfo, s_info, sizeof(struct sctp_extrcvinfo));
917                                         }
918                                         sinfo_found = 1;
919                                         break;
920
921                                 }
922                         }
923                         cmsg = CMSG_NXTHDR(&msg, cmsg);
924                 }
925         }
926         return (sz);
927 #endif
928 }
929
930
931 #if defined(HAVE_SCTP_PEELOFF_SOCKOPT)
932 #include <netinet/sctp_peeloff.h>
933
934 int
935 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
936 {
937         struct sctp_peeloff_opt peeloff;
938         int error;
939         socklen_t optlen;
940
941         /* set in the socket option params */
942         memset(&peeloff, 0, sizeof(peeloff));
943         peeloff.s = sd;
944         peeloff.assoc_id = assoc_id;
945         optlen = sizeof(peeloff);
946         error = getsockopt(sd, IPPROTO_SCTP, SCTP_PEELOFF, (void *)&peeloff,
947             &optlen);
948         if (error) {
949                 errno = error;
950                 return (-1);
951         } else {
952                 return (peeloff.new_sd);
953         }
954 }
955
956 #endif
957
958 #if !defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
959
960 int
961 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
962 {
963         /* NOT supported, return invalid sd */
964         errno = ENOTSUP;
965         return (-1);
966 }
967
968 #endif
969 #if defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT)
970 int
971 sctp_peeloff(int sd, sctp_assoc_t assoc_id)
972 {
973         return (syscall(SYS_sctp_peeloff, sd, assoc_id));
974 }
975
976 #endif
977
978
979 #undef SCTP_CONTROL_VEC_SIZE_SND
980 #undef SCTP_CONTROL_VEC_SIZE_RCV
981 #undef SCTP_STACK_BUF_SIZE
982 #undef SCTP_SMALL_IOVEC_SIZE