Annotation of embedaddon/iperf/src/iperf_sctp.c, revision 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>