Annotation of embedaddon/iperf/src/iperf_sctp.c, revision 1.1.1.2

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>