Annotation of embedaddon/iperf/src/iperf_tcp.c, revision 1.1
1.1 ! misho 1: /*
! 2: * iperf, Copyright (c) 2014, 2016, 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: #include "iperf.h"
! 43: #include "iperf_api.h"
! 44: #include "iperf_tcp.h"
! 45: #include "net.h"
! 46:
! 47: #if defined(HAVE_FLOWLABEL)
! 48: #include "flowlabel.h"
! 49: #endif /* HAVE_FLOWLABEL */
! 50:
! 51: /* iperf_tcp_recv
! 52: *
! 53: * receives the data for TCP
! 54: */
! 55: int
! 56: iperf_tcp_recv(struct iperf_stream *sp)
! 57: {
! 58: int r;
! 59:
! 60: r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Ptcp);
! 61:
! 62: if (r < 0)
! 63: return r;
! 64:
! 65: sp->result->bytes_received += r;
! 66: sp->result->bytes_received_this_interval += r;
! 67:
! 68: return r;
! 69: }
! 70:
! 71:
! 72: /* iperf_tcp_send
! 73: *
! 74: * sends the data for TCP
! 75: */
! 76: int
! 77: iperf_tcp_send(struct iperf_stream *sp)
! 78: {
! 79: int r;
! 80:
! 81: if (sp->test->zerocopy)
! 82: r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->settings->blksize);
! 83: else
! 84: r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Ptcp);
! 85:
! 86: if (r < 0)
! 87: return r;
! 88:
! 89: sp->result->bytes_sent += r;
! 90: sp->result->bytes_sent_this_interval += r;
! 91:
! 92: return r;
! 93: }
! 94:
! 95:
! 96: /* iperf_tcp_accept
! 97: *
! 98: * accept a new TCP stream connection
! 99: */
! 100: int
! 101: iperf_tcp_accept(struct iperf_test * test)
! 102: {
! 103: int s;
! 104: signed char rbuf = ACCESS_DENIED;
! 105: char cookie[COOKIE_SIZE];
! 106: socklen_t len;
! 107: struct sockaddr_storage addr;
! 108:
! 109: len = sizeof(addr);
! 110: if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
! 111: i_errno = IESTREAMCONNECT;
! 112: return -1;
! 113: }
! 114:
! 115: if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
! 116: i_errno = IERECVCOOKIE;
! 117: return -1;
! 118: }
! 119:
! 120: if (strcmp(test->cookie, cookie) != 0) {
! 121: if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) {
! 122: i_errno = IESENDMESSAGE;
! 123: return -1;
! 124: }
! 125: close(s);
! 126: }
! 127:
! 128: return s;
! 129: }
! 130:
! 131:
! 132: /* iperf_tcp_listen
! 133: *
! 134: * start up a listener for TCP stream connections
! 135: */
! 136: int
! 137: iperf_tcp_listen(struct iperf_test *test)
! 138: {
! 139: struct addrinfo hints, *res;
! 140: char portstr[6];
! 141: int s, opt;
! 142: int saved_errno;
! 143:
! 144: s = test->listener;
! 145:
! 146: /*
! 147: * If certain parameters are specified (such as socket buffer
! 148: * size), then throw away the listening socket (the one for which
! 149: * we just accepted the control connection) and recreate it with
! 150: * those parameters. That way, when new data connections are
! 151: * set, they'll have all the correct parameters in place.
! 152: *
! 153: * It's not clear whether this is a requirement or a convenience.
! 154: */
! 155: if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
! 156: FD_CLR(s, &test->read_set);
! 157: close(s);
! 158:
! 159: snprintf(portstr, 6, "%d", test->server_port);
! 160: memset(&hints, 0, sizeof(hints));
! 161:
! 162: /*
! 163: * If binding to the wildcard address with no explicit address
! 164: * family specified, then force us to get an AF_INET6 socket.
! 165: * More details in the comments in netanounce().
! 166: */
! 167: if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
! 168: hints.ai_family = AF_INET6;
! 169: }
! 170: else {
! 171: hints.ai_family = test->settings->domain;
! 172: }
! 173: hints.ai_socktype = SOCK_STREAM;
! 174: hints.ai_flags = AI_PASSIVE;
! 175: if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) {
! 176: i_errno = IESTREAMLISTEN;
! 177: return -1;
! 178: }
! 179:
! 180: if ((s = socket(res->ai_family, SOCK_STREAM, 0)) < 0) {
! 181: freeaddrinfo(res);
! 182: i_errno = IESTREAMLISTEN;
! 183: return -1;
! 184: }
! 185:
! 186: if (test->no_delay) {
! 187: opt = 1;
! 188: if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
! 189: saved_errno = errno;
! 190: close(s);
! 191: freeaddrinfo(res);
! 192: errno = saved_errno;
! 193: i_errno = IESETNODELAY;
! 194: return -1;
! 195: }
! 196: }
! 197: // XXX: Setting MSS is very buggy!
! 198: if ((opt = test->settings->mss)) {
! 199: if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
! 200: saved_errno = errno;
! 201: close(s);
! 202: freeaddrinfo(res);
! 203: errno = saved_errno;
! 204: i_errno = IESETMSS;
! 205: return -1;
! 206: }
! 207: }
! 208: if ((opt = test->settings->socket_bufsize)) {
! 209: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
! 210: saved_errno = errno;
! 211: close(s);
! 212: freeaddrinfo(res);
! 213: errno = saved_errno;
! 214: i_errno = IESETBUF;
! 215: return -1;
! 216: }
! 217: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
! 218: saved_errno = errno;
! 219: close(s);
! 220: freeaddrinfo(res);
! 221: errno = saved_errno;
! 222: i_errno = IESETBUF;
! 223: return -1;
! 224: }
! 225: }
! 226: if (test->debug) {
! 227: socklen_t optlen = sizeof(opt);
! 228: if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, &optlen) < 0) {
! 229: saved_errno = errno;
! 230: close(s);
! 231: freeaddrinfo(res);
! 232: errno = saved_errno;
! 233: i_errno = IESETBUF;
! 234: return -1;
! 235: }
! 236: printf("SO_SNDBUF is %u\n", opt);
! 237: }
! 238: #if defined(HAVE_TCP_CONGESTION)
! 239: if (test->congestion) {
! 240: if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) {
! 241: close(s);
! 242: freeaddrinfo(res);
! 243: i_errno = IESETCONGESTION;
! 244: return -1;
! 245: }
! 246: }
! 247: #endif /* HAVE_TCP_CONGESTION */
! 248: #if defined(HAVE_SO_MAX_PACING_RATE)
! 249: /* If socket pacing is available and not disabled, try it. */
! 250: if (! test->no_fq_socket_pacing) {
! 251: /* Convert bits per second to bytes per second */
! 252: unsigned int rate = test->settings->rate / 8;
! 253: if (rate > 0) {
! 254: if (test->debug) {
! 255: printf("Setting fair-queue socket pacing to %u\n", rate);
! 256: }
! 257: if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &rate, sizeof(rate)) < 0) {
! 258: warning("Unable to set socket pacing, using application pacing instead");
! 259: test->no_fq_socket_pacing = 1;
! 260: }
! 261: }
! 262: }
! 263: #endif /* HAVE_SO_MAX_PACING_RATE */
! 264: opt = 1;
! 265: if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
! 266: saved_errno = errno;
! 267: close(s);
! 268: freeaddrinfo(res);
! 269: errno = saved_errno;
! 270: i_errno = IEREUSEADDR;
! 271: return -1;
! 272: }
! 273:
! 274: /*
! 275: * If we got an IPv6 socket, figure out if it shoudl accept IPv4
! 276: * connections as well. See documentation in netannounce() for
! 277: * more details.
! 278: */
! 279: #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
! 280: if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET)) {
! 281: if (test->settings->domain == AF_UNSPEC)
! 282: opt = 0;
! 283: else
! 284: opt = 1;
! 285: if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
! 286: (char *) &opt, sizeof(opt)) < 0) {
! 287: saved_errno = errno;
! 288: close(s);
! 289: freeaddrinfo(res);
! 290: errno = saved_errno;
! 291: i_errno = IEV6ONLY;
! 292: return -1;
! 293: }
! 294: }
! 295: #endif /* IPV6_V6ONLY */
! 296:
! 297: if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
! 298: saved_errno = errno;
! 299: close(s);
! 300: freeaddrinfo(res);
! 301: errno = saved_errno;
! 302: i_errno = IESTREAMLISTEN;
! 303: return -1;
! 304: }
! 305:
! 306: freeaddrinfo(res);
! 307:
! 308: if (listen(s, 5) < 0) {
! 309: i_errno = IESTREAMLISTEN;
! 310: return -1;
! 311: }
! 312:
! 313: test->listener = s;
! 314: }
! 315:
! 316: return s;
! 317: }
! 318:
! 319:
! 320: /* iperf_tcp_connect
! 321: *
! 322: * connect to a TCP stream listener
! 323: */
! 324: int
! 325: iperf_tcp_connect(struct iperf_test *test)
! 326: {
! 327: struct addrinfo hints, *local_res, *server_res;
! 328: char portstr[6];
! 329: int s, opt;
! 330: int saved_errno;
! 331:
! 332: if (test->bind_address) {
! 333: memset(&hints, 0, sizeof(hints));
! 334: hints.ai_family = test->settings->domain;
! 335: hints.ai_socktype = SOCK_STREAM;
! 336: if (getaddrinfo(test->bind_address, NULL, &hints, &local_res) != 0) {
! 337: i_errno = IESTREAMCONNECT;
! 338: return -1;
! 339: }
! 340: }
! 341:
! 342: memset(&hints, 0, sizeof(hints));
! 343: hints.ai_family = test->settings->domain;
! 344: hints.ai_socktype = SOCK_STREAM;
! 345: snprintf(portstr, sizeof(portstr), "%d", test->server_port);
! 346: if (getaddrinfo(test->server_hostname, portstr, &hints, &server_res) != 0) {
! 347: if (test->bind_address)
! 348: freeaddrinfo(local_res);
! 349: i_errno = IESTREAMCONNECT;
! 350: return -1;
! 351: }
! 352:
! 353: if ((s = socket(server_res->ai_family, SOCK_STREAM, 0)) < 0) {
! 354: if (test->bind_address)
! 355: freeaddrinfo(local_res);
! 356: freeaddrinfo(server_res);
! 357: i_errno = IESTREAMCONNECT;
! 358: return -1;
! 359: }
! 360:
! 361: if (test->bind_address) {
! 362: struct sockaddr_in *lcladdr;
! 363: lcladdr = (struct sockaddr_in *)local_res->ai_addr;
! 364: lcladdr->sin_port = htons(test->bind_port);
! 365: local_res->ai_addr = (struct sockaddr *)lcladdr;
! 366:
! 367: if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
! 368: saved_errno = errno;
! 369: close(s);
! 370: freeaddrinfo(local_res);
! 371: freeaddrinfo(server_res);
! 372: errno = saved_errno;
! 373: i_errno = IESTREAMCONNECT;
! 374: return -1;
! 375: }
! 376: freeaddrinfo(local_res);
! 377: }
! 378:
! 379: /* Set socket options */
! 380: if (test->no_delay) {
! 381: opt = 1;
! 382: if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
! 383: saved_errno = errno;
! 384: close(s);
! 385: freeaddrinfo(server_res);
! 386: errno = saved_errno;
! 387: i_errno = IESETNODELAY;
! 388: return -1;
! 389: }
! 390: }
! 391: if ((opt = test->settings->mss)) {
! 392: if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
! 393: saved_errno = errno;
! 394: close(s);
! 395: freeaddrinfo(server_res);
! 396: errno = saved_errno;
! 397: i_errno = IESETMSS;
! 398: return -1;
! 399: }
! 400: }
! 401: if ((opt = test->settings->socket_bufsize)) {
! 402: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
! 403: saved_errno = errno;
! 404: close(s);
! 405: freeaddrinfo(server_res);
! 406: errno = saved_errno;
! 407: i_errno = IESETBUF;
! 408: return -1;
! 409: }
! 410: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
! 411: saved_errno = errno;
! 412: close(s);
! 413: freeaddrinfo(server_res);
! 414: errno = saved_errno;
! 415: i_errno = IESETBUF;
! 416: return -1;
! 417: }
! 418: }
! 419: if (test->debug) {
! 420: socklen_t optlen = sizeof(opt);
! 421: if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, &optlen) < 0) {
! 422: saved_errno = errno;
! 423: close(s);
! 424: freeaddrinfo(server_res);
! 425: errno = saved_errno;
! 426: i_errno = IESETBUF;
! 427: return -1;
! 428: }
! 429: printf("SO_SNDBUF is %u\n", opt);
! 430: }
! 431: #if defined(HAVE_FLOWLABEL)
! 432: if (test->settings->flowlabel) {
! 433: if (server_res->ai_addr->sa_family != AF_INET6) {
! 434: saved_errno = errno;
! 435: close(s);
! 436: freeaddrinfo(server_res);
! 437: errno = saved_errno;
! 438: i_errno = IESETFLOW;
! 439: return -1;
! 440: } else {
! 441: struct sockaddr_in6* sa6P = (struct sockaddr_in6*) server_res->ai_addr;
! 442: char freq_buf[sizeof(struct in6_flowlabel_req)];
! 443: struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
! 444: int freq_len = sizeof(*freq);
! 445:
! 446: memset(freq, 0, sizeof(*freq));
! 447: freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL);
! 448: freq->flr_action = IPV6_FL_A_GET;
! 449: freq->flr_flags = IPV6_FL_F_CREATE;
! 450: freq->flr_share = IPV6_FL_F_CREATE | IPV6_FL_S_EXCL;
! 451: memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16);
! 452:
! 453: if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) {
! 454: saved_errno = errno;
! 455: close(s);
! 456: freeaddrinfo(server_res);
! 457: errno = saved_errno;
! 458: i_errno = IESETFLOW;
! 459: return -1;
! 460: }
! 461: sa6P->sin6_flowinfo = freq->flr_label;
! 462:
! 463: opt = 1;
! 464: if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &opt, sizeof(opt)) < 0) {
! 465: saved_errno = errno;
! 466: close(s);
! 467: freeaddrinfo(server_res);
! 468: errno = saved_errno;
! 469: i_errno = IESETFLOW;
! 470: return -1;
! 471: }
! 472: }
! 473: }
! 474: #endif /* HAVE_FLOWLABEL */
! 475:
! 476: #if defined(HAVE_TCP_CONGESTION)
! 477: if (test->congestion) {
! 478: if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) {
! 479: close(s);
! 480: freeaddrinfo(server_res);
! 481: i_errno = IESETCONGESTION;
! 482: return -1;
! 483: }
! 484: }
! 485: #endif /* HAVE_TCP_CONGESTION */
! 486:
! 487: #if defined(HAVE_SO_MAX_PACING_RATE)
! 488: /* If socket pacing is available and not disabled, try it. */
! 489: if (! test->no_fq_socket_pacing) {
! 490: /* Convert bits per second to bytes per second */
! 491: unsigned int rate = test->settings->rate / 8;
! 492: if (rate > 0) {
! 493: if (test->debug) {
! 494: printf("Socket pacing set to %u\n", rate);
! 495: }
! 496: if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &rate, sizeof(rate)) < 0) {
! 497: warning("Unable to set socket pacing, using application pacing instead");
! 498: test->no_fq_socket_pacing = 1;
! 499: }
! 500: }
! 501: }
! 502: #endif /* HAVE_SO_MAX_PACING_RATE */
! 503:
! 504: if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
! 505: saved_errno = errno;
! 506: close(s);
! 507: freeaddrinfo(server_res);
! 508: errno = saved_errno;
! 509: i_errno = IESTREAMCONNECT;
! 510: return -1;
! 511: }
! 512:
! 513: freeaddrinfo(server_res);
! 514:
! 515: /* Send cookie for verification */
! 516: if (Nwrite(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
! 517: saved_errno = errno;
! 518: close(s);
! 519: errno = saved_errno;
! 520: i_errno = IESENDCOOKIE;
! 521: return -1;
! 522: }
! 523:
! 524: return s;
! 525: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>