1: /*
2: * iperf, Copyright (c) 2014-2019, The Regents of the University of
3: * California, through Lawrence Berkeley National Laboratory (subject
4: * to receipt of any required approvals from the U.S. Dept. of
5: * Energy). All rights reserved.
6: *
7: * If you have questions about your rights to use or distribute this
8: * software, please contact Berkeley Lab's Technology Transfer
9: * Department at TTD@lbl.gov.
10: *
11: * NOTICE. This software is owned by the U.S. Department of Energy.
12: * As such, the U.S. Government has been granted for itself and others
13: * acting on its behalf a paid-up, nonexclusive, irrevocable,
14: * worldwide license in the Software to reproduce, prepare derivative
15: * works, and perform publicly and display publicly. Beginning five
16: * (5) years after the date permission to assert copyright is obtained
17: * from the U.S. Department of Energy, and subject to any subsequent
18: * five (5) year renewals, the U.S. Government is granted for itself
19: * and others acting on its behalf a paid-up, nonexclusive,
20: * irrevocable, worldwide license in the Software to reproduce,
21: * prepare derivative works, distribute copies to the public, perform
22: * publicly and display publicly, and to permit others to do so.
23: *
24: * This code is distributed under a BSD style license, see the LICENSE
25: * file for complete information.
26: */
27: #include "iperf_config.h"
28:
29: #include <stdio.h>
30: #include <stdlib.h>
31: #include <string.h>
32: #include <errno.h>
33: #include <unistd.h>
34: #include <sys/socket.h>
35: #include <sys/types.h>
36: #include <netinet/in.h>
37: #include <netdb.h>
38: #include <sys/time.h>
39: #include <sys/select.h>
40: #include <limits.h>
41:
42: #ifdef HAVE_NETINET_SCTP_H
43: #include <netinet/sctp.h>
44: #endif /* HAVE_NETINET_SCTP_H */
45:
46: #include "iperf.h"
47: #include "iperf_api.h"
48: #include "iperf_sctp.h"
49: #include "net.h"
50:
51:
52:
53: /* iperf_sctp_recv
54: *
55: * receives the data for SCTP
56: */
57: int
58: iperf_sctp_recv(struct iperf_stream *sp)
59: {
60: #if defined(HAVE_SCTP_H)
61: int r;
62:
63: r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
64: if (r < 0)
65: return r;
66:
67: /* Only count bytes received while we're in the correct state. */
68: if (sp->test->state == TEST_RUNNING) {
69: sp->result->bytes_received += r;
70: sp->result->bytes_received_this_interval += r;
71: }
72: else {
73: if (sp->test->debug)
74: printf("Late receive, state = %d\n", sp->test->state);
75: }
76:
77: return r;
78: #else
79: i_errno = IENOSCTP;
80: return -1;
81: #endif /* HAVE_SCTP_H */
82: }
83:
84:
85: /* iperf_sctp_send
86: *
87: * sends the data for SCTP
88: */
89: int
90: iperf_sctp_send(struct iperf_stream *sp)
91: {
92: #if defined(HAVE_SCTP_H)
93: int r;
94:
95: r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
96: if (r < 0)
97: return r;
98:
99: sp->result->bytes_sent += r;
100: sp->result->bytes_sent_this_interval += r;
101:
102: return r;
103: #else
104: i_errno = IENOSCTP;
105: return -1;
106: #endif /* HAVE_SCTP_H */
107: }
108:
109:
110:
111: /* iperf_sctp_accept
112: *
113: * accept a new SCTP stream connection
114: */
115: int
116: iperf_sctp_accept(struct iperf_test * test)
117: {
118: #if defined(HAVE_SCTP_H)
119: int s;
120: signed char rbuf = ACCESS_DENIED;
121: char cookie[COOKIE_SIZE];
122: socklen_t len;
123: struct sockaddr_storage addr;
124:
125: len = sizeof(addr);
126: s = accept(test->listener, (struct sockaddr *) &addr, &len);
127: if (s < 0) {
128: i_errno = IESTREAMCONNECT;
129: return -1;
130: }
131:
132: if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) {
133: i_errno = IERECVCOOKIE;
134: close(s);
135: return -1;
136: }
137:
138: if (strncmp(test->cookie, cookie, COOKIE_SIZE) != 0) {
139: if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) {
140: i_errno = IESENDMESSAGE;
141: close(s);
142: return -1;
143: }
144: close(s);
145: }
146:
147: return s;
148: #else
149: i_errno = IENOSCTP;
150: return -1;
151: #endif /* HAVE_SCTP_H */
152: }
153:
154:
155: /* iperf_sctp_listen
156: *
157: * start up a listener for SCTP stream connections
158: */
159: int
160: iperf_sctp_listen(struct iperf_test *test)
161: {
162: #if defined(HAVE_SCTP_H)
163: struct addrinfo hints, *res;
164: char portstr[6];
165: int s, opt, saved_errno;
166:
167: close(test->listener);
168: test->listener = -1;
169:
170: snprintf(portstr, 6, "%d", test->server_port);
171: memset(&hints, 0, sizeof(hints));
172: /*
173: * If binding to the wildcard address with no explicit address
174: * family specified, then force us to get an AF_INET6 socket.
175: * More details in the comments in netanounce().
176: */
177: if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
178: hints.ai_family = AF_INET6;
179: } else {
180: hints.ai_family = test->settings->domain;
181: }
182: hints.ai_socktype = SOCK_STREAM;
183: hints.ai_flags = AI_PASSIVE;
184: if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) {
185: i_errno = IESTREAMLISTEN;
186: return -1;
187: }
188:
189: if ((s = socket(res->ai_family, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
190: freeaddrinfo(res);
191: i_errno = IESTREAMLISTEN;
192: return -1;
193: }
194:
195: if ((opt = test->settings->socket_bufsize)) {
196: int saved_errno;
197: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
198: saved_errno = errno;
199: close(s);
200: freeaddrinfo(res);
201: errno = saved_errno;
202: i_errno = IESETBUF;
203: return -1;
204: }
205: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
206: saved_errno = errno;
207: close(s);
208: freeaddrinfo(res);
209: errno = saved_errno;
210: i_errno = IESETBUF;
211: return -1;
212: }
213: }
214:
215: if (test->bind_dev) {
216: #if defined(SO_BINDTODEVICE)
217: if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
218: test->bind_dev, IFNAMSIZ) < 0)
219: #endif // SO_BINDTODEVICE
220: {
221: saved_errno = errno;
222: close(s);
223: freeaddrinfo(res);
224: i_errno = IEBINDDEV;
225: errno = saved_errno;
226: return -1;
227: }
228: }
229:
230: #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
231: if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC ||
232: test->settings->domain == AF_INET6)) {
233: if (test->settings->domain == AF_UNSPEC)
234: opt = 0;
235: else
236: opt = 1;
237: if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
238: (char *) &opt, sizeof(opt)) < 0) {
239: saved_errno = errno;
240: close(s);
241: freeaddrinfo(res);
242: errno = saved_errno;
243: i_errno = IEPROTOCOL;
244: return -1;
245: }
246: }
247: #endif /* IPV6_V6ONLY */
248:
249: opt = 1;
250: if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
251: saved_errno = errno;
252: close(s);
253: freeaddrinfo(res);
254: errno = saved_errno;
255: i_errno = IEREUSEADDR;
256: return -1;
257: }
258:
259: /* servers must call sctp_bindx() _instead_ of bind() */
260: if (!TAILQ_EMPTY(&test->xbind_addrs)) {
261: if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER)) {
262: close(s);
263: freeaddrinfo(res);
264: return -1;
265: }
266: } else
267: if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
268: saved_errno = errno;
269: close(s);
270: freeaddrinfo(res);
271: errno = saved_errno;
272: i_errno = IESTREAMLISTEN;
273: return -1;
274: }
275:
276: freeaddrinfo(res);
277:
278: if (listen(s, INT_MAX) < 0) {
279: i_errno = IESTREAMLISTEN;
280: return -1;
281: }
282:
283: test->listener = s;
284:
285: return s;
286: #else
287: i_errno = IENOSCTP;
288: return -1;
289: #endif /* HAVE_SCTP_H */
290: }
291:
292:
293: /* iperf_sctp_connect
294: *
295: * connect to a SCTP stream listener
296: */
297: int
298: iperf_sctp_connect(struct iperf_test *test)
299: {
300: #if defined(HAVE_SCTP_H)
301: int s, opt, saved_errno;
302: char portstr[6];
303: struct addrinfo hints, *local_res = NULL, *server_res = NULL;
304:
305: if (test->bind_address) {
306: memset(&hints, 0, sizeof(hints));
307: hints.ai_family = test->settings->domain;
308: hints.ai_socktype = SOCK_STREAM;
309: if ((gerror = getaddrinfo(test->bind_address, NULL, &hints, &local_res)) != 0) {
310: i_errno = IESTREAMCONNECT;
311: return -1;
312: }
313: }
314:
315: memset(&hints, 0, sizeof(hints));
316: hints.ai_family = test->settings->domain;
317: hints.ai_socktype = SOCK_STREAM;
318: snprintf(portstr, sizeof(portstr), "%d", test->server_port);
319: if ((gerror = getaddrinfo(test->server_hostname, portstr, &hints, &server_res)) != 0) {
320: if (test->bind_address)
321: freeaddrinfo(local_res);
322: i_errno = IESTREAMCONNECT;
323: return -1;
324: }
325:
326: s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
327: if (s < 0) {
328: freeaddrinfo(local_res);
329: freeaddrinfo(server_res);
330: i_errno = IESTREAMCONNECT;
331: return -1;
332: }
333:
334: if ((opt = test->settings->socket_bufsize)) {
335: int saved_errno;
336: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
337: saved_errno = errno;
338: close(s);
339: freeaddrinfo(server_res);
340: errno = saved_errno;
341: i_errno = IESETBUF;
342: return -1;
343: }
344: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
345: saved_errno = errno;
346: close(s);
347: freeaddrinfo(server_res);
348: errno = saved_errno;
349: i_errno = IESETBUF;
350: return -1;
351: }
352: }
353:
354: if (test->bind_dev) {
355: #if defined(SO_BINDTODEVICE)
356: if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
357: test->bind_dev, IFNAMSIZ) < 0)
358: #endif // SO_BINDTODEVICE
359: {
360: saved_errno = errno;
361: close(s);
362: freeaddrinfo(local_res);
363: freeaddrinfo(server_res);
364: i_errno = IEBINDDEV;
365: errno = saved_errno;
366: return -1;
367: }
368: }
369:
370: /*
371: * Various ways to bind the local end of the connection.
372: * 1. --bind (with or without --cport).
373: */
374: if (test->bind_address) {
375: struct sockaddr_in *lcladdr;
376: lcladdr = (struct sockaddr_in *)local_res->ai_addr;
377: lcladdr->sin_port = htons(test->bind_port);
378:
379: if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
380: saved_errno = errno;
381: close(s);
382: freeaddrinfo(local_res);
383: freeaddrinfo(server_res);
384: errno = saved_errno;
385: i_errno = IESTREAMCONNECT;
386: return -1;
387: }
388: freeaddrinfo(local_res);
389: }
390: /* --cport, no --bind */
391: else if (test->bind_port) {
392: size_t addrlen;
393: struct sockaddr_storage lcl;
394:
395: /* IPv4 */
396: if (server_res->ai_family == AF_INET) {
397: struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
398: lcladdr->sin_family = AF_INET;
399: lcladdr->sin_port = htons(test->bind_port);
400: lcladdr->sin_addr.s_addr = INADDR_ANY;
401: addrlen = sizeof(struct sockaddr_in);
402: }
403: /* IPv6 */
404: else if (server_res->ai_family == AF_INET6) {
405: struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
406: lcladdr->sin6_family = AF_INET6;
407: lcladdr->sin6_port = htons(test->bind_port);
408: lcladdr->sin6_addr = in6addr_any;
409: addrlen = sizeof(struct sockaddr_in6);
410: }
411: /* Unknown protocol */
412: else {
413: saved_errno = errno;
414: close(s);
415: freeaddrinfo(server_res);
416: errno = saved_errno;
417: i_errno = IEPROTOCOL;
418: return -1;
419: }
420:
421: if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
422: saved_errno = errno;
423: close(s);
424: freeaddrinfo(server_res);
425: errno = saved_errno;
426: i_errno = IESTREAMCONNECT;
427: return -1;
428: }
429: }
430:
431: if (test->no_delay != 0) {
432: opt = 1;
433: if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) {
434: saved_errno = errno;
435: close(s);
436: freeaddrinfo(server_res);
437: errno = saved_errno;
438: i_errno = IESETNODELAY;
439: return -1;
440: }
441: }
442:
443: if ((test->settings->mss >= 512 && test->settings->mss <= 131072)) {
444:
445: /*
446: * Some platforms use a struct sctp_assoc_value as the
447: * argument to SCTP_MAXSEG. Other (older API implementations)
448: * take an int. FreeBSD 10 and CentOS 6 support SCTP_MAXSEG,
449: * but OpenSolaris 11 doesn't.
450: */
451: #ifdef HAVE_STRUCT_SCTP_ASSOC_VALUE
452: struct sctp_assoc_value av;
453:
454: /*
455: * Some platforms support SCTP_FUTURE_ASSOC, others need to
456: * (equivalently) do 0 here. FreeBSD 10 is an example of the
457: * former, CentOS 6 Linux is an example of the latter.
458: */
459: #ifdef SCTP_FUTURE_ASSOC
460: av.assoc_id = SCTP_FUTURE_ASSOC;
461: #else
462: av.assoc_id = 0;
463: #endif
464: av.assoc_value = test->settings->mss;
465:
466: if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &av, sizeof(av)) < 0) {
467: saved_errno = errno;
468: close(s);
469: freeaddrinfo(server_res);
470: errno = saved_errno;
471: i_errno = IESETMSS;
472: return -1;
473: }
474: #else
475: opt = test->settings->mss;
476:
477: /*
478: * Solaris might not support this option. If it doesn't work,
479: * ignore the error (at least for now).
480: */
481: if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &opt, sizeof(opt)) < 0 &&
482: errno != ENOPROTOOPT) {
483: saved_errno = errno;
484: close(s);
485: freeaddrinfo(server_res);
486: errno = saved_errno;
487: i_errno = IESETMSS;
488: return -1;
489: }
490: #endif /* HAVE_STRUCT_SCTP_ASSOC_VALUE */
491: }
492:
493: if (test->settings->num_ostreams > 0) {
494: struct sctp_initmsg initmsg;
495:
496: memset(&initmsg, 0, sizeof(struct sctp_initmsg));
497: initmsg.sinit_num_ostreams = test->settings->num_ostreams;
498:
499: if (setsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) {
500: saved_errno = errno;
501: close(s);
502: freeaddrinfo(server_res);
503: errno = saved_errno;
504: i_errno = IESETSCTPNSTREAM;
505: return -1;
506: }
507: }
508:
509: /* clients must call bind() followed by sctp_bindx() before connect() */
510: if (!TAILQ_EMPTY(&test->xbind_addrs)) {
511: if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT)) {
512: freeaddrinfo(server_res);
513: close(s);
514: return -1;
515: }
516: }
517:
518: /* TODO support sctp_connectx() to avoid heartbeating. */
519: if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
520: saved_errno = errno;
521: close(s);
522: freeaddrinfo(server_res);
523: errno = saved_errno;
524: i_errno = IESTREAMCONNECT;
525: return -1;
526: }
527:
528: /* Send cookie for verification */
529: if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) {
530: saved_errno = errno;
531: close(s);
532: freeaddrinfo(server_res);
533: errno = saved_errno;
534: i_errno = IESENDCOOKIE;
535: return -1;
536: }
537:
538: /*
539: * We want to allow fragmentation. But there's at least one
540: * implementation (Solaris) that doesn't support this option,
541: * even though it defines SCTP_DISABLE_FRAGMENTS. So we have to
542: * try setting the option and ignore the error, if it doesn't
543: * work.
544: */
545: opt = 0;
546: if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &opt, sizeof(opt)) < 0 &&
547: errno != ENOPROTOOPT) {
548: saved_errno = errno;
549: close(s);
550: freeaddrinfo(server_res);
551: errno = saved_errno;
552: i_errno = IESETSCTPDISABLEFRAG;
553: return -1;
554: }
555:
556: freeaddrinfo(server_res);
557: return s;
558: #else
559: i_errno = IENOSCTP;
560: return -1;
561: #endif /* HAVE_SCTP_H */
562: }
563:
564:
565:
566: int
567: iperf_sctp_init(struct iperf_test *test)
568: {
569: #if defined(HAVE_SCTP_H)
570: return 0;
571: #else
572: i_errno = IENOSCTP;
573: return -1;
574: #endif /* HAVE_SCTP_H */
575: }
576:
577:
578:
579: /* iperf_sctp_bindx
580: *
581: * handle binding to multiple endpoints (-X parameters)
582: */
583: int
584: iperf_sctp_bindx(struct iperf_test *test, int s, int is_server)
585: {
586: #if defined(HAVE_SCTP_H)
587: struct addrinfo hints;
588: char portstr[6];
589: char *servname;
590: struct addrinfo *ai, *ai0;
591: struct sockaddr *xaddrs;
592: struct xbind_entry *xbe, *xbe0;
593: char *bp;
594: size_t xaddrlen;
595: int nxaddrs;
596: int retval;
597: int domain;
598: int saved_errno;
599:
600: domain = test->settings->domain;
601: xbe0 = NULL;
602: retval = 0;
603:
604: if (TAILQ_EMPTY(&test->xbind_addrs))
605: return retval; /* nothing to do */
606:
607: memset(&hints, 0, sizeof(hints));
608: hints.ai_family = (domain == AF_UNSPEC ? AF_INET6 : domain);
609: hints.ai_socktype = SOCK_STREAM;
610: servname = NULL;
611: if (is_server) {
612: hints.ai_flags |= AI_PASSIVE;
613: snprintf(portstr, 6, "%d", test->server_port);
614: servname = portstr;
615: }
616:
617: /* client: must pop first -X address and call bind().
618: * sctp_bindx() must see the ephemeral port chosen by bind().
619: * we deliberately ignore the -B argument in this case.
620: */
621: if (!is_server) {
622: struct sockaddr *sa;
623: struct sockaddr_in *sin;
624: struct sockaddr_in6 *sin6;
625: int eport;
626:
627: xbe0 = TAILQ_FIRST(&test->xbind_addrs);
628: TAILQ_REMOVE(&test->xbind_addrs, xbe0, link);
629:
630: if ((gerror = getaddrinfo(xbe0->name, servname, &hints, &xbe0->ai)) != 0) {
631: i_errno = IESETSCTPBINDX;
632: retval = -1;
633: goto out;
634: }
635:
636: ai = xbe0->ai;
637: if (domain != AF_UNSPEC && domain != ai->ai_family) {
638: i_errno = IESETSCTPBINDX;
639: retval = -1;
640: goto out;
641: }
642: if (bind(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0) {
643: i_errno = IESETSCTPBINDX;
644: retval = -1;
645: goto out;
646: }
647:
648: /* if only one -X argument, nothing more to do */
649: if (TAILQ_EMPTY(&test->xbind_addrs))
650: goto out;
651:
652: sa = (struct sockaddr *)ai->ai_addr;
653: if (sa->sa_family == AF_INET) {
654: sin = (struct sockaddr_in *)ai->ai_addr;
655: eport = sin->sin_port;
656: } else if (sa->sa_family == AF_INET6) {
657: sin6 = (struct sockaddr_in6 *)ai->ai_addr;
658: eport = sin6->sin6_port;
659: } else {
660: i_errno = IESETSCTPBINDX;
661: retval = -1;
662: goto out;
663: }
664: snprintf(portstr, 6, "%d", ntohs(eport));
665: servname = portstr;
666: }
667:
668: /* pass 1: resolve and compute lengths. */
669: nxaddrs = 0;
670: xaddrlen = 0;
671: TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
672: if (xbe->ai != NULL)
673: freeaddrinfo(xbe->ai);
674: if ((gerror = getaddrinfo(xbe->name, servname, &hints, &xbe->ai)) != 0) {
675: i_errno = IESETSCTPBINDX;
676: retval = -1;
677: goto out;
678: }
679: ai0 = xbe->ai;
680: for (ai = ai0; ai; ai = ai->ai_next) {
681: if (domain != AF_UNSPEC && domain != ai->ai_family)
682: continue;
683: xaddrlen += ai->ai_addrlen;
684: ++nxaddrs;
685: }
686: }
687:
688: /* pass 2: copy into flat buffer. */
689: xaddrs = (struct sockaddr *)malloc(xaddrlen);
690: if (!xaddrs) {
691: i_errno = IESETSCTPBINDX;
692: retval = -1;
693: goto out;
694: }
695: bp = (char *)xaddrs;
696: TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
697: ai0 = xbe->ai;
698: for (ai = ai0; ai; ai = ai->ai_next) {
699: if (domain != AF_UNSPEC && domain != ai->ai_family)
700: continue;
701: memcpy(bp, ai->ai_addr, ai->ai_addrlen);
702: bp += ai->ai_addrlen;
703: }
704: }
705:
706: if (sctp_bindx(s, xaddrs, nxaddrs, SCTP_BINDX_ADD_ADDR) == -1) {
707: saved_errno = errno;
708: close(s);
709: free(xaddrs);
710: errno = saved_errno;
711: i_errno = IESETSCTPBINDX;
712: retval = -1;
713: goto out;
714: }
715:
716: free(xaddrs);
717: retval = 0;
718:
719: out:
720: /* client: put head node back. */
721: if (!is_server && xbe0)
722: TAILQ_INSERT_HEAD(&test->xbind_addrs, xbe0, link);
723:
724: TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
725: if (xbe->ai) {
726: freeaddrinfo(xbe->ai);
727: xbe->ai = NULL;
728: }
729: }
730:
731: return retval;
732: #else
733: i_errno = IENOSCTP;
734: return -1;
735: #endif /* HAVE_SCTP_H */
736: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>