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>