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

1.1       misho       1: /*
                      2:  * iperf, Copyright (c) 2014, 2015, 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 <netinet/tcp.h>
                     39: #include <sys/time.h>
                     40: #include <sys/select.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)
                     61:     int r;
                     62: 
                     63:     r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
                     64:     if (r < 0)
                     65:         return r;
                     66: 
                     67:     sp->result->bytes_received += r;
                     68:     sp->result->bytes_received_this_interval += r;
                     69: 
                     70:     return r;
                     71: #else
                     72:     i_errno = IENOSCTP;
                     73:     return -1;
                     74: #endif /* HAVE_SCTP */
                     75: }
                     76: 
                     77: 
                     78: /* iperf_sctp_send 
                     79:  *
                     80:  * sends the data for SCTP
                     81:  */
                     82: int
                     83: iperf_sctp_send(struct iperf_stream *sp)
                     84: {
                     85: #if defined(HAVE_SCTP)
                     86:     int r;
                     87: 
                     88:     r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
                     89:     if (r < 0)
                     90:         return r;    
                     91: 
                     92:     sp->result->bytes_sent += r;
                     93:     sp->result->bytes_sent_this_interval += r;
                     94: 
                     95:     return r;
                     96: #else
                     97:     i_errno = IENOSCTP;
                     98:     return -1;
                     99: #endif /* HAVE_SCTP */
                    100: }
                    101: 
                    102: 
                    103: 
                    104: /* iperf_sctp_accept
                    105:  *
                    106:  * accept a new SCTP stream connection
                    107:  */
                    108: int
                    109: iperf_sctp_accept(struct iperf_test * test)
                    110: {
                    111: #if defined(HAVE_SCTP)
                    112:     int     s;
                    113:     signed char rbuf = ACCESS_DENIED;
                    114:     char    cookie[COOKIE_SIZE];
                    115:     socklen_t len;
                    116:     struct sockaddr_storage addr;
                    117: 
                    118:     len = sizeof(addr);
                    119:     s = accept(test->listener, (struct sockaddr *) &addr, &len);
                    120:     if (s < 0) {
                    121:         i_errno = IESTREAMCONNECT;
                    122:         return -1;
                    123:     }
                    124: 
                    125:     if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) {
                    126:         i_errno = IERECVCOOKIE;
                    127:         return -1;
                    128:     }
                    129: 
                    130:     if (strcmp(test->cookie, cookie) != 0) {
                    131:         if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) {
                    132:             i_errno = IESENDMESSAGE;
                    133:             return -1;
                    134:         }
                    135:         close(s);
                    136:     }
                    137: 
                    138:     return s;
                    139: #else
                    140:     i_errno = IENOSCTP;
                    141:     return -1;
                    142: #endif /* HAVE_SCTP */
                    143: }
                    144: 
                    145: 
                    146: /* iperf_sctp_listen
                    147:  *
                    148:  * start up a listener for SCTP stream connections
                    149:  */
                    150: int
                    151: iperf_sctp_listen(struct iperf_test *test)
                    152: {
                    153: #if defined(HAVE_SCTP)
                    154:     struct addrinfo hints, *res;
                    155:     char portstr[6];
                    156:     int s, opt;
                    157: 
                    158:     close(test->listener);
                    159:    
                    160:     snprintf(portstr, 6, "%d", test->server_port);
                    161:     memset(&hints, 0, sizeof(hints));
                    162:     hints.ai_family = (test->settings->domain == AF_UNSPEC ? AF_INET6 : test->settings->domain);
                    163:     hints.ai_socktype = SOCK_STREAM;
                    164:     hints.ai_flags = AI_PASSIVE;
                    165:     if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) {
                    166:         i_errno = IESTREAMLISTEN;
                    167:         return -1;
                    168:     }
                    169: 
                    170:     if ((s = socket(res->ai_family, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
                    171:         freeaddrinfo(res);
                    172:         i_errno = IESTREAMLISTEN;
                    173:         return -1;
                    174:     }
                    175: 
                    176: #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
                    177:     if (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET6) {
                    178:         if (test->settings->domain == AF_UNSPEC)
                    179:             opt = 0;
                    180:         else if (test->settings->domain == AF_INET6)
                    181:             opt = 1;
                    182:         if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, 
                    183:                       (char *) &opt, sizeof(opt)) < 0) {
                    184:            close(s);
                    185:            freeaddrinfo(res);
                    186:            i_errno = IEPROTOCOL;
                    187:            return -1;
                    188:        }
                    189:     }
                    190: #endif /* IPV6_V6ONLY */
                    191: 
                    192:     opt = 1;
                    193:     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
                    194:         close(s);
                    195:         freeaddrinfo(res);
                    196:         i_errno = IEREUSEADDR;
                    197:         return -1;
                    198:     }
                    199: 
                    200:     /* servers must call sctp_bindx() _instead_ of bind() */
                    201:     if (!TAILQ_EMPTY(&test->xbind_addrs)) {
                    202:         freeaddrinfo(res);
                    203:         if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER))
                    204:             return -1;
                    205:     } else
                    206:     if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
                    207:         close(s);
                    208:         freeaddrinfo(res);
                    209:         i_errno = IESTREAMLISTEN;
                    210:         return -1;
                    211:     }
                    212: 
                    213:     freeaddrinfo(res);
                    214: 
                    215:     if (listen(s, 5) < 0) {
                    216:         i_errno = IESTREAMLISTEN;
                    217:         return -1;
                    218:     }
                    219: 
                    220:     test->listener = s;
                    221:   
                    222:     return s;
                    223: #else
                    224:     i_errno = IENOSCTP;
                    225:     return -1;
                    226: #endif /* HAVE_SCTP */
                    227: }
                    228: 
                    229: 
                    230: /* iperf_sctp_connect
                    231:  *
                    232:  * connect to a SCTP stream listener
                    233:  */
                    234: int
                    235: iperf_sctp_connect(struct iperf_test *test)
                    236: {
                    237: #if defined(HAVE_SCTP)
                    238:     int s, opt;
                    239:     char portstr[6];
                    240:     struct addrinfo hints, *local_res, *server_res;
                    241: 
                    242:     if (test->bind_address) {
                    243:         memset(&hints, 0, sizeof(hints));
                    244:         hints.ai_family = test->settings->domain;
                    245:         hints.ai_socktype = SOCK_STREAM;
                    246:         if (getaddrinfo(test->bind_address, NULL, &hints, &local_res) != 0) {
                    247:             i_errno = IESTREAMCONNECT;
                    248:             return -1;
                    249:         }
                    250:     }
                    251: 
                    252:     memset(&hints, 0, sizeof(hints));
                    253:     hints.ai_family = test->settings->domain;
                    254:     hints.ai_socktype = SOCK_STREAM;
                    255:     snprintf(portstr, sizeof(portstr), "%d", test->server_port);
                    256:     if (getaddrinfo(test->server_hostname, portstr, &hints, &server_res) != 0) {
                    257:        if (test->bind_address)
                    258:            freeaddrinfo(local_res);
                    259:         i_errno = IESTREAMCONNECT;
                    260:         return -1;
                    261:     }
                    262: 
                    263:     s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
                    264:     if (s < 0) {
                    265:        if (test->bind_address)
                    266:            freeaddrinfo(local_res);
                    267:        freeaddrinfo(server_res);
                    268:         i_errno = IESTREAMCONNECT;
                    269:         return -1;
                    270:     }
                    271: 
                    272:     if (test->no_delay != 0) {
                    273:          opt = 1;
                    274:          if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) {
                    275:              close(s);
                    276:              freeaddrinfo(server_res);
                    277:              i_errno = IESETNODELAY;
                    278:              return -1;
                    279:          }
                    280:     }
                    281: 
                    282:     if ((test->settings->mss >= 512 && test->settings->mss <= 131072)) {
                    283: 
                    284:        /*
                    285:         * Some platforms use a struct sctp_assoc_value as the
                    286:         * argument to SCTP_MAXSEG.  Other (older API implementations)
                    287:         * take an int.  FreeBSD 10 and CentOS 6 support SCTP_MAXSEG,
                    288:         * but OpenSolaris 11 doesn't.
                    289:         */
                    290: #ifdef HAVE_STRUCT_SCTP_ASSOC_VALUE
                    291:         struct sctp_assoc_value av;
                    292: 
                    293:        /*
                    294:         * Some platforms support SCTP_FUTURE_ASSOC, others need to
                    295:         * (equivalently) do 0 here.  FreeBSD 10 is an example of the
                    296:         * former, CentOS 6 Linux is an example of the latter.
                    297:         */
                    298: #ifdef SCTP_FUTURE_ASSOC
                    299:         av.assoc_id = SCTP_FUTURE_ASSOC;
                    300: #else
                    301:        av.assoc_id = 0;
                    302: #endif
                    303:         av.assoc_value = test->settings->mss;
                    304: 
                    305:         if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &av, sizeof(av)) < 0) {
                    306:             close(s);
                    307:             freeaddrinfo(server_res);
                    308:             i_errno = IESETMSS;
                    309:             return -1;
                    310:         }
                    311: #else
                    312:        opt = test->settings->mss;
                    313: 
                    314:        /*
                    315:         * Solaris might not support this option.  If it doesn't work,
                    316:         * ignore the error (at least for now).
                    317:         */
                    318:         if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &opt, sizeof(opt)) < 0 &&
                    319:            errno != ENOPROTOOPT) {
                    320:             close(s);
                    321:             freeaddrinfo(server_res);
                    322:             i_errno = IESETMSS;
                    323:             return -1;
                    324:         }
                    325: #endif HAVE_STRUCT_SCTP_ASSOC_VALUE
                    326:     }
                    327: 
                    328:     if (test->settings->num_ostreams > 0) {
                    329:         struct sctp_initmsg initmsg;
                    330: 
                    331:         memset(&initmsg, 0, sizeof(struct sctp_initmsg));
                    332:         initmsg.sinit_num_ostreams = test->settings->num_ostreams;
                    333: 
                    334:         if (setsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) {
                    335:                 close(s);
                    336:                 freeaddrinfo(server_res);
                    337:                 i_errno = IESETSCTPNSTREAM;
                    338:                 return -1;
                    339:         }
                    340:     }
                    341: 
                    342:     /* clients must call bind() followed by sctp_bindx() before connect() */
                    343:     if (!TAILQ_EMPTY(&test->xbind_addrs)) {
                    344:         if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT))
                    345:             return -1;
                    346:     }
                    347: 
                    348:     /* TODO support sctp_connectx() to avoid heartbeating. */
                    349:     if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
                    350:        close(s);
                    351:        freeaddrinfo(server_res);
                    352:         i_errno = IESTREAMCONNECT;
                    353:         return -1;
                    354:     }
                    355:     freeaddrinfo(server_res);
                    356: 
                    357:     /* Send cookie for verification */
                    358:     if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) {
                    359:        close(s);
                    360:         i_errno = IESENDCOOKIE;
                    361:         return -1;
                    362:     }
                    363: 
                    364:     /*
                    365:      * We want to allow fragmentation.  But there's at least one
                    366:      * implementation (Solaris) that doesn't support this option,
                    367:      * even though it defines SCTP_DISABLE_FRAGMENTS.  So we have to
                    368:      * try setting the option and ignore the error, if it doesn't
                    369:      * work.
                    370:      */
                    371:     opt = 0;
                    372:     if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &opt, sizeof(opt)) < 0 &&
                    373:        errno != ENOPROTOOPT) {
                    374:         close(s);
                    375:         freeaddrinfo(server_res);
                    376:         i_errno = IESETSCTPDISABLEFRAG;
                    377:         return -1;
                    378:     }
                    379: 
                    380:     return s;
                    381: #else
                    382:     i_errno = IENOSCTP;
                    383:     return -1;
                    384: #endif /* HAVE_SCTP */
                    385: }
                    386: 
                    387: 
                    388: 
                    389: int
                    390: iperf_sctp_init(struct iperf_test *test)
                    391: {
                    392: #if defined(HAVE_SCTP)
                    393:     return 0;
                    394: #else
                    395:     i_errno = IENOSCTP;
                    396:     return -1;
                    397: #endif /* HAVE_SCTP */
                    398: }
                    399: 
                    400: 
                    401: 
                    402: /* iperf_sctp_bindx
                    403:  *
                    404:  * handle binding to multiple endpoints (-X parameters)
                    405:  */
                    406: int
                    407: iperf_sctp_bindx(struct iperf_test *test, int s, int is_server)
                    408: {
                    409: #if defined(HAVE_SCTP)
                    410:     struct addrinfo hints;
                    411:     char portstr[6];
                    412:     char *servname;
                    413:     struct addrinfo *ai, *ai0;
                    414:     struct sockaddr *xaddrs;
                    415:     struct xbind_entry *xbe, *xbe0;
                    416:     char *bp;
                    417:     size_t xaddrlen;
                    418:     int nxaddrs;
                    419:     int retval;
                    420:     int domain;
                    421: 
                    422:     domain = test->settings->domain;
                    423:     xbe0 = NULL;
                    424:     retval = 0;
                    425: 
                    426:     if (TAILQ_EMPTY(&test->xbind_addrs))
                    427:         return retval; /* nothing to do */
                    428: 
                    429:     memset(&hints, 0, sizeof(hints));
                    430:     hints.ai_family = (domain == AF_UNSPEC ? AF_INET6 : domain);
                    431:     hints.ai_socktype = SOCK_STREAM;
                    432:     servname = NULL;
                    433:     if (is_server) {
                    434:         hints.ai_flags |= AI_PASSIVE;
                    435:         snprintf(portstr, 6, "%d", test->server_port);
                    436:         servname = portstr;
                    437:     }
                    438: 
                    439:     /* client: must pop first -X address and call bind().
                    440:      * sctp_bindx() must see the ephemeral port chosen by bind().
                    441:      * we deliberately ignore the -B argument in this case.
                    442:      */
                    443:     if (!is_server) {
                    444:         struct sockaddr *sa;
                    445:         struct sockaddr_in *sin;
                    446:         struct sockaddr_in6 *sin6;
                    447:         int eport;
                    448: 
                    449:         xbe0 = TAILQ_FIRST(&test->xbind_addrs);
                    450:         TAILQ_REMOVE(&test->xbind_addrs, xbe0, link);
                    451: 
                    452:         if (getaddrinfo(xbe0->name, servname, &hints, &xbe0->ai) != 0) {
                    453:             i_errno = IESETSCTPBINDX;
                    454:             retval = -1;
                    455:             goto out;
                    456:         }
                    457: 
                    458:         ai = xbe0->ai;
                    459:         if (domain != AF_UNSPEC && domain != ai->ai_family) {
                    460:             i_errno = IESETSCTPBINDX;
                    461:             retval = -1;
                    462:             goto out;
                    463:         }
                    464:         if (bind(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0) {
                    465:             i_errno = IESETSCTPBINDX;
                    466:             retval = -1;
                    467:             goto out;
                    468:         }
                    469: 
                    470:         /* if only one -X argument, nothing more to do */
                    471:         if (TAILQ_EMPTY(&test->xbind_addrs))
                    472:             goto out;
                    473: 
                    474:         sa = (struct sockaddr *)ai->ai_addr;
                    475:         if (sa->sa_family == AF_INET) {
                    476:             sin = (struct sockaddr_in *)ai->ai_addr;
                    477:             eport = sin->sin_port;
                    478:         } else if (sa->sa_family == AF_INET6) {
                    479:             sin6 = (struct sockaddr_in6 *)ai->ai_addr;
                    480:             eport = sin6->sin6_port;
                    481:         } else {
                    482:             i_errno = IESETSCTPBINDX;
                    483:             retval = -1;
                    484:             goto out;
                    485:         }
                    486:         snprintf(portstr, 6, "%d", ntohs(eport));
                    487:         servname = portstr;
                    488:     }
                    489: 
                    490:     /* pass 1: resolve and compute lengths. */
                    491:     nxaddrs = 0;
                    492:     xaddrlen = 0;
                    493:     TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
                    494:         if (xbe->ai != NULL)
                    495:             freeaddrinfo(xbe->ai);
                    496:         if (getaddrinfo(xbe->name, servname, &hints, &xbe->ai) != 0) {
                    497:             i_errno = IESETSCTPBINDX;
                    498:             retval = -1;
                    499:             goto out;
                    500:         }
                    501:         ai0 = xbe->ai;
                    502:         for (ai = ai0; ai; ai = ai->ai_next) {
                    503:             if (domain != AF_UNSPEC && domain != ai->ai_family)
                    504:                 continue;
                    505:             xaddrlen += ai->ai_addrlen;
                    506:             ++nxaddrs;
                    507:         }
                    508:     }
                    509: 
                    510:     /* pass 2: copy into flat buffer. */
                    511:     xaddrs = (struct sockaddr *)malloc(xaddrlen);
                    512:     if (!xaddrs) {
                    513:             i_errno = IESETSCTPBINDX;
                    514:             retval = -1;
                    515:             goto out;
                    516:     }
                    517:     bp = (char *)xaddrs;
                    518:     TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
                    519:         ai0 = xbe->ai;
                    520:         for (ai = ai0; ai; ai = ai->ai_next) {
                    521:             if (domain != AF_UNSPEC && domain != ai->ai_family)
                    522:                 continue;
                    523:            memcpy(bp, ai->ai_addr, ai->ai_addrlen);
                    524:             bp += ai->ai_addrlen;
                    525:         }
                    526:     }
                    527: 
                    528:     if (sctp_bindx(s, xaddrs, nxaddrs, SCTP_BINDX_ADD_ADDR) == -1) {
                    529:         close(s);
                    530:         free(xaddrs);
                    531:         i_errno = IESETSCTPBINDX;
                    532:         retval = -1;
                    533:         goto out;
                    534:     }
                    535: 
                    536:     free(xaddrs);
                    537:     retval = 0;
                    538: 
                    539: out:
                    540:     /* client: put head node back. */
                    541:     if (!is_server && xbe0)
                    542:         TAILQ_INSERT_HEAD(&test->xbind_addrs, xbe0, link);
                    543: 
                    544:     TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
                    545:         if (xbe->ai) {
                    546:             freeaddrinfo(xbe->ai);
                    547:             xbe->ai = NULL;
                    548:         }
                    549:     }
                    550: 
                    551:     return retval;
                    552: #else
                    553:     i_errno = IENOSCTP;
                    554:     return -1;
                    555: #endif /* HAVE_SCTP */
                    556: }

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