• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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