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
iperf_sctp_recv(struct iperf_stream * sp)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
iperf_sctp_send(struct iperf_stream * sp)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
iperf_sctp_accept(struct iperf_test * test)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
iperf_sctp_listen(struct iperf_test * test)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
169 snprintf(portstr, 6, "%d", test->server_port);
170 memset(&hints, 0, sizeof(hints));
171 /*
172 * If binding to the wildcard address with no explicit address
173 * family specified, then force us to get an AF_INET6 socket.
174 * More details in the comments in netanounce().
175 */
176 if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
177 hints.ai_family = AF_INET6;
178 } else {
179 hints.ai_family = test->settings->domain;
180 }
181 hints.ai_socktype = SOCK_STREAM;
182 hints.ai_flags = AI_PASSIVE;
183 if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) {
184 i_errno = IESTREAMLISTEN;
185 return -1;
186 }
187
188 if ((s = socket(res->ai_family, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
189 freeaddrinfo(res);
190 i_errno = IESTREAMLISTEN;
191 return -1;
192 }
193
194 if ((opt = test->settings->socket_bufsize)) {
195 int saved_errno;
196 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
197 saved_errno = errno;
198 close(s);
199 freeaddrinfo(res);
200 errno = saved_errno;
201 i_errno = IESETBUF;
202 return -1;
203 }
204 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
205 saved_errno = errno;
206 close(s);
207 freeaddrinfo(res);
208 errno = saved_errno;
209 i_errno = IESETBUF;
210 return -1;
211 }
212 }
213
214 if (test->bind_dev) {
215 #if defined(SO_BINDTODEVICE)
216 if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
217 test->bind_dev, IFNAMSIZ) < 0)
218 #endif // SO_BINDTODEVICE
219 {
220 saved_errno = errno;
221 close(s);
222 freeaddrinfo(res);
223 i_errno = IEBINDDEV;
224 errno = saved_errno;
225 return -1;
226 }
227 }
228
229 #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
230 if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC ||
231 test->settings->domain == AF_INET6)) {
232 if (test->settings->domain == AF_UNSPEC)
233 opt = 0;
234 else
235 opt = 1;
236 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
237 (char *) &opt, sizeof(opt)) < 0) {
238 saved_errno = errno;
239 close(s);
240 freeaddrinfo(res);
241 errno = saved_errno;
242 i_errno = IEPROTOCOL;
243 return -1;
244 }
245 }
246 #endif /* IPV6_V6ONLY */
247
248 opt = 1;
249 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
250 saved_errno = errno;
251 close(s);
252 freeaddrinfo(res);
253 errno = saved_errno;
254 i_errno = IEREUSEADDR;
255 return -1;
256 }
257
258 /* servers must call sctp_bindx() _instead_ of bind() */
259 if (!TAILQ_EMPTY(&test->xbind_addrs)) {
260 if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER)) {
261 close(s);
262 freeaddrinfo(res);
263 return -1;
264 }
265 } else
266 if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
267 saved_errno = errno;
268 close(s);
269 freeaddrinfo(res);
270 errno = saved_errno;
271 i_errno = IESTREAMLISTEN;
272 return -1;
273 }
274
275 freeaddrinfo(res);
276
277 if (listen(s, INT_MAX) < 0) {
278 i_errno = IESTREAMLISTEN;
279 return -1;
280 }
281
282 test->listener = s;
283
284 return s;
285 #else
286 i_errno = IENOSCTP;
287 return -1;
288 #endif /* HAVE_SCTP_H */
289 }
290
291
292 /* iperf_sctp_connect
293 *
294 * connect to a SCTP stream listener
295 */
296 int
iperf_sctp_connect(struct iperf_test * test)297 iperf_sctp_connect(struct iperf_test *test)
298 {
299 #if defined(HAVE_SCTP_H)
300 int s, opt, saved_errno;
301 char portstr[6];
302 struct addrinfo hints, *local_res = NULL, *server_res = NULL;
303
304 if (test->bind_address) {
305 memset(&hints, 0, sizeof(hints));
306 hints.ai_family = test->settings->domain;
307 hints.ai_socktype = SOCK_STREAM;
308 if ((gerror = getaddrinfo(test->bind_address, NULL, &hints, &local_res)) != 0) {
309 i_errno = IESTREAMCONNECT;
310 return -1;
311 }
312 }
313
314 memset(&hints, 0, sizeof(hints));
315 hints.ai_family = test->settings->domain;
316 hints.ai_socktype = SOCK_STREAM;
317 snprintf(portstr, sizeof(portstr), "%d", test->server_port);
318 if ((gerror = getaddrinfo(test->server_hostname, portstr, &hints, &server_res)) != 0) {
319 if (test->bind_address)
320 freeaddrinfo(local_res);
321 i_errno = IESTREAMCONNECT;
322 return -1;
323 }
324
325 s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
326 if (s < 0) {
327 freeaddrinfo(local_res);
328 freeaddrinfo(server_res);
329 i_errno = IESTREAMCONNECT;
330 return -1;
331 }
332
333 if ((opt = test->settings->socket_bufsize)) {
334 int saved_errno;
335 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
336 saved_errno = errno;
337 close(s);
338 freeaddrinfo(server_res);
339 errno = saved_errno;
340 i_errno = IESETBUF;
341 return -1;
342 }
343 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
344 saved_errno = errno;
345 close(s);
346 freeaddrinfo(server_res);
347 errno = saved_errno;
348 i_errno = IESETBUF;
349 return -1;
350 }
351 }
352
353 if (test->bind_dev) {
354 #if defined(SO_BINDTODEVICE)
355 if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
356 test->bind_dev, IFNAMSIZ) < 0)
357 #endif // SO_BINDTODEVICE
358 {
359 saved_errno = errno;
360 close(s);
361 freeaddrinfo(local_res);
362 freeaddrinfo(server_res);
363 i_errno = IEBINDDEV;
364 errno = saved_errno;
365 return -1;
366 }
367 }
368
369 /*
370 * Various ways to bind the local end of the connection.
371 * 1. --bind (with or without --cport).
372 */
373 if (test->bind_address) {
374 struct sockaddr_in *lcladdr;
375 lcladdr = (struct sockaddr_in *)local_res->ai_addr;
376 lcladdr->sin_port = htons(test->bind_port);
377
378 if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
379 saved_errno = errno;
380 close(s);
381 freeaddrinfo(local_res);
382 freeaddrinfo(server_res);
383 errno = saved_errno;
384 i_errno = IESTREAMCONNECT;
385 return -1;
386 }
387 freeaddrinfo(local_res);
388 }
389 /* --cport, no --bind */
390 else if (test->bind_port) {
391 size_t addrlen;
392 struct sockaddr_storage lcl;
393
394 /* IPv4 */
395 if (server_res->ai_family == AF_INET) {
396 struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
397 lcladdr->sin_family = AF_INET;
398 lcladdr->sin_port = htons(test->bind_port);
399 lcladdr->sin_addr.s_addr = INADDR_ANY;
400 addrlen = sizeof(struct sockaddr_in);
401 }
402 /* IPv6 */
403 else if (server_res->ai_family == AF_INET6) {
404 struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
405 lcladdr->sin6_family = AF_INET6;
406 lcladdr->sin6_port = htons(test->bind_port);
407 lcladdr->sin6_addr = in6addr_any;
408 addrlen = sizeof(struct sockaddr_in6);
409 }
410 /* Unknown protocol */
411 else {
412 saved_errno = errno;
413 close(s);
414 freeaddrinfo(server_res);
415 errno = saved_errno;
416 i_errno = IEPROTOCOL;
417 return -1;
418 }
419
420 if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
421 saved_errno = errno;
422 close(s);
423 freeaddrinfo(server_res);
424 errno = saved_errno;
425 i_errno = IESTREAMCONNECT;
426 return -1;
427 }
428 }
429
430 if (test->no_delay != 0) {
431 opt = 1;
432 if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) {
433 saved_errno = errno;
434 close(s);
435 freeaddrinfo(server_res);
436 errno = saved_errno;
437 i_errno = IESETNODELAY;
438 return -1;
439 }
440 }
441
442 if ((test->settings->mss >= 512 && test->settings->mss <= 131072)) {
443
444 /*
445 * Some platforms use a struct sctp_assoc_value as the
446 * argument to SCTP_MAXSEG. Other (older API implementations)
447 * take an int. FreeBSD 10 and CentOS 6 support SCTP_MAXSEG,
448 * but OpenSolaris 11 doesn't.
449 */
450 #ifdef HAVE_STRUCT_SCTP_ASSOC_VALUE
451 struct sctp_assoc_value av;
452
453 /*
454 * Some platforms support SCTP_FUTURE_ASSOC, others need to
455 * (equivalently) do 0 here. FreeBSD 10 is an example of the
456 * former, CentOS 6 Linux is an example of the latter.
457 */
458 #ifdef SCTP_FUTURE_ASSOC
459 av.assoc_id = SCTP_FUTURE_ASSOC;
460 #else
461 av.assoc_id = 0;
462 #endif
463 av.assoc_value = test->settings->mss;
464
465 if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &av, sizeof(av)) < 0) {
466 saved_errno = errno;
467 close(s);
468 freeaddrinfo(server_res);
469 errno = saved_errno;
470 i_errno = IESETMSS;
471 return -1;
472 }
473 #else
474 opt = test->settings->mss;
475
476 /*
477 * Solaris might not support this option. If it doesn't work,
478 * ignore the error (at least for now).
479 */
480 if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &opt, sizeof(opt)) < 0 &&
481 errno != ENOPROTOOPT) {
482 saved_errno = errno;
483 close(s);
484 freeaddrinfo(server_res);
485 errno = saved_errno;
486 i_errno = IESETMSS;
487 return -1;
488 }
489 #endif /* HAVE_STRUCT_SCTP_ASSOC_VALUE */
490 }
491
492 if (test->settings->num_ostreams > 0) {
493 struct sctp_initmsg initmsg;
494
495 memset(&initmsg, 0, sizeof(struct sctp_initmsg));
496 initmsg.sinit_num_ostreams = test->settings->num_ostreams;
497
498 if (setsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) {
499 saved_errno = errno;
500 close(s);
501 freeaddrinfo(server_res);
502 errno = saved_errno;
503 i_errno = IESETSCTPNSTREAM;
504 return -1;
505 }
506 }
507
508 /* clients must call bind() followed by sctp_bindx() before connect() */
509 if (!TAILQ_EMPTY(&test->xbind_addrs)) {
510 if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT)) {
511 freeaddrinfo(server_res);
512 close(s);
513 return -1;
514 }
515 }
516
517 /* TODO support sctp_connectx() to avoid heartbeating. */
518 if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
519 saved_errno = errno;
520 close(s);
521 freeaddrinfo(server_res);
522 errno = saved_errno;
523 i_errno = IESTREAMCONNECT;
524 return -1;
525 }
526
527 /* Send cookie for verification */
528 if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) {
529 saved_errno = errno;
530 close(s);
531 freeaddrinfo(server_res);
532 errno = saved_errno;
533 i_errno = IESENDCOOKIE;
534 return -1;
535 }
536
537 /*
538 * We want to allow fragmentation. But there's at least one
539 * implementation (Solaris) that doesn't support this option,
540 * even though it defines SCTP_DISABLE_FRAGMENTS. So we have to
541 * try setting the option and ignore the error, if it doesn't
542 * work.
543 */
544 opt = 0;
545 if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &opt, sizeof(opt)) < 0 &&
546 errno != ENOPROTOOPT) {
547 saved_errno = errno;
548 close(s);
549 freeaddrinfo(server_res);
550 errno = saved_errno;
551 i_errno = IESETSCTPDISABLEFRAG;
552 return -1;
553 }
554
555 freeaddrinfo(server_res);
556 return s;
557 #else
558 i_errno = IENOSCTP;
559 return -1;
560 #endif /* HAVE_SCTP_H */
561 }
562
563
564
565 int
iperf_sctp_init(struct iperf_test * test)566 iperf_sctp_init(struct iperf_test *test)
567 {
568 #if defined(HAVE_SCTP_H)
569 return 0;
570 #else
571 i_errno = IENOSCTP;
572 return -1;
573 #endif /* HAVE_SCTP_H */
574 }
575
576
577
578 /* iperf_sctp_bindx
579 *
580 * handle binding to multiple endpoints (-X parameters)
581 */
582 int
iperf_sctp_bindx(struct iperf_test * test,int s,int is_server)583 iperf_sctp_bindx(struct iperf_test *test, int s, int is_server)
584 {
585 #if defined(HAVE_SCTP_H)
586 struct addrinfo hints;
587 char portstr[6];
588 char *servname;
589 struct addrinfo *ai, *ai0;
590 struct sockaddr *xaddrs;
591 struct xbind_entry *xbe, *xbe0;
592 char *bp;
593 size_t xaddrlen;
594 int nxaddrs;
595 int retval;
596 int domain;
597 int saved_errno;
598
599 domain = test->settings->domain;
600 xbe0 = NULL;
601 retval = 0;
602
603 if (TAILQ_EMPTY(&test->xbind_addrs))
604 return retval; /* nothing to do */
605
606 memset(&hints, 0, sizeof(hints));
607 hints.ai_family = (domain == AF_UNSPEC ? AF_INET6 : domain);
608 hints.ai_socktype = SOCK_STREAM;
609 servname = NULL;
610 if (is_server) {
611 hints.ai_flags |= AI_PASSIVE;
612 snprintf(portstr, 6, "%d", test->server_port);
613 servname = portstr;
614 }
615
616 /* client: must pop first -X address and call bind().
617 * sctp_bindx() must see the ephemeral port chosen by bind().
618 * we deliberately ignore the -B argument in this case.
619 */
620 if (!is_server) {
621 struct sockaddr *sa;
622 struct sockaddr_in *sin;
623 struct sockaddr_in6 *sin6;
624 int eport;
625
626 xbe0 = TAILQ_FIRST(&test->xbind_addrs);
627 TAILQ_REMOVE(&test->xbind_addrs, xbe0, link);
628
629 if ((gerror = getaddrinfo(xbe0->name, servname, &hints, &xbe0->ai)) != 0) {
630 i_errno = IESETSCTPBINDX;
631 retval = -1;
632 goto out;
633 }
634
635 ai = xbe0->ai;
636 if (domain != AF_UNSPEC && domain != ai->ai_family) {
637 i_errno = IESETSCTPBINDX;
638 retval = -1;
639 goto out;
640 }
641 if (bind(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0) {
642 i_errno = IESETSCTPBINDX;
643 retval = -1;
644 goto out;
645 }
646
647 /* if only one -X argument, nothing more to do */
648 if (TAILQ_EMPTY(&test->xbind_addrs))
649 goto out;
650
651 sa = (struct sockaddr *)ai->ai_addr;
652 if (sa->sa_family == AF_INET) {
653 sin = (struct sockaddr_in *)ai->ai_addr;
654 eport = sin->sin_port;
655 } else if (sa->sa_family == AF_INET6) {
656 sin6 = (struct sockaddr_in6 *)ai->ai_addr;
657 eport = sin6->sin6_port;
658 } else {
659 i_errno = IESETSCTPBINDX;
660 retval = -1;
661 goto out;
662 }
663 snprintf(portstr, 6, "%d", ntohs(eport));
664 servname = portstr;
665 }
666
667 /* pass 1: resolve and compute lengths. */
668 nxaddrs = 0;
669 xaddrlen = 0;
670 TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
671 if (xbe->ai != NULL)
672 freeaddrinfo(xbe->ai);
673 if ((gerror = getaddrinfo(xbe->name, servname, &hints, &xbe->ai)) != 0) {
674 i_errno = IESETSCTPBINDX;
675 retval = -1;
676 goto out;
677 }
678 ai0 = xbe->ai;
679 for (ai = ai0; ai; ai = ai->ai_next) {
680 if (domain != AF_UNSPEC && domain != ai->ai_family)
681 continue;
682 xaddrlen += ai->ai_addrlen;
683 ++nxaddrs;
684 }
685 }
686
687 /* pass 2: copy into flat buffer. */
688 xaddrs = (struct sockaddr *)malloc(xaddrlen);
689 if (!xaddrs) {
690 i_errno = IESETSCTPBINDX;
691 retval = -1;
692 goto out;
693 }
694 bp = (char *)xaddrs;
695 TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
696 ai0 = xbe->ai;
697 for (ai = ai0; ai; ai = ai->ai_next) {
698 if (domain != AF_UNSPEC && domain != ai->ai_family)
699 continue;
700 memcpy(bp, ai->ai_addr, ai->ai_addrlen);
701 bp += ai->ai_addrlen;
702 }
703 }
704
705 if (sctp_bindx(s, xaddrs, nxaddrs, SCTP_BINDX_ADD_ADDR) == -1) {
706 saved_errno = errno;
707 close(s);
708 free(xaddrs);
709 errno = saved_errno;
710 i_errno = IESETSCTPBINDX;
711 retval = -1;
712 goto out;
713 }
714
715 free(xaddrs);
716 retval = 0;
717
718 out:
719 /* client: put head node back. */
720 if (!is_server && xbe0)
721 TAILQ_INSERT_HEAD(&test->xbind_addrs, xbe0, link);
722
723 TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
724 if (xbe->ai) {
725 freeaddrinfo(xbe->ai);
726 xbe->ai = NULL;
727 }
728 }
729
730 return retval;
731 #else
732 i_errno = IENOSCTP;
733 return -1;
734 #endif /* HAVE_SCTP_H */
735 }
736