--- embedaddon/iperf/src/iperf_tcp.c 2016/10/18 13:28:18 1.1.1.1 +++ embedaddon/iperf/src/iperf_tcp.c 2023/09/27 11:14:54 1.1.1.3 @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014, 2016, The Regents of the University of + * iperf, Copyright (c) 2014-2022, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -24,25 +24,25 @@ * This code is distributed under a BSD style license, see the LICENSE * file for complete information. */ -#include "iperf_config.h" - #include #include #include #include #include +#include #include #include #include #include -#include #include #include +#include #include "iperf.h" #include "iperf_api.h" #include "iperf_tcp.h" #include "net.h" +#include "cjson.h" #if defined(HAVE_FLOWLABEL) #include "flowlabel.h" @@ -62,14 +62,21 @@ iperf_tcp_recv(struct iperf_stream *sp) if (r < 0) return r; - sp->result->bytes_received += r; - sp->result->bytes_received_this_interval += r; + /* Only count bytes received while we're in the correct state. */ + if (sp->test->state == TEST_RUNNING) { + sp->result->bytes_received += r; + sp->result->bytes_received_this_interval += r; + } + else { + if (sp->test->debug) + printf("Late receive, state = %d\n", sp->test->state); + } return r; } -/* iperf_tcp_send +/* iperf_tcp_send * * sends the data for TCP */ @@ -78,17 +85,25 @@ iperf_tcp_send(struct iperf_stream *sp) { int r; + if (!sp->pending_size) + sp->pending_size = sp->settings->blksize; + if (sp->test->zerocopy) - r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->settings->blksize); + r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size); else - r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Ptcp); + r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp); if (r < 0) return r; + sp->pending_size -= r; sp->result->bytes_sent += r; sp->result->bytes_sent_this_interval += r; + if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG) + printf("sent %d bytes of %d, pending %d, total %" PRIu64 "\n", + r, sp->settings->blksize, sp->pending_size, sp->result->bytes_sent); + return r; } @@ -119,8 +134,7 @@ iperf_tcp_accept(struct iperf_test * test) if (strcmp(test->cookie, cookie) != 0) { if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) { - i_errno = IESENDMESSAGE; - return -1; + iperf_err(test, "failed to send access denied from busy server to new connecting client, errno = %d\n", errno); } close(s); } @@ -136,10 +150,10 @@ iperf_tcp_accept(struct iperf_test * test) int iperf_tcp_listen(struct iperf_test *test) { - struct addrinfo hints, *res; - char portstr[6]; int s, opt; + socklen_t optlen; int saved_errno; + int rcvbuf_actual, sndbuf_actual; s = test->listener; @@ -153,6 +167,9 @@ iperf_tcp_listen(struct iperf_test *test) * It's not clear whether this is a requirement or a convenience. */ if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { + struct addrinfo hints, *res; + char portstr[6]; + FD_CLR(s, &test->read_set); close(s); @@ -172,7 +189,7 @@ iperf_tcp_listen(struct iperf_test *test) } hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; - if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) { + if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) { i_errno = IESTREAMLISTEN; return -1; } @@ -223,44 +240,29 @@ iperf_tcp_listen(struct iperf_test *test) return -1; } } - if (test->debug) { - socklen_t optlen = sizeof(opt); - if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, &optlen) < 0) { - saved_errno = errno; - close(s); - freeaddrinfo(res); - errno = saved_errno; - i_errno = IESETBUF; - return -1; - } - printf("SO_SNDBUF is %u\n", opt); - } -#if defined(HAVE_TCP_CONGESTION) - if (test->congestion) { - if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { - close(s); - freeaddrinfo(res); - i_errno = IESETCONGESTION; - return -1; - } - } -#endif /* HAVE_TCP_CONGESTION */ #if defined(HAVE_SO_MAX_PACING_RATE) - /* If socket pacing is available and not disabled, try it. */ - if (! test->no_fq_socket_pacing) { + /* If fq socket pacing is specified, enable it. */ + if (test->settings->fqrate) { /* Convert bits per second to bytes per second */ - unsigned int rate = test->settings->rate / 8; - if (rate > 0) { + unsigned int fqrate = test->settings->fqrate / 8; + if (fqrate > 0) { if (test->debug) { - printf("Setting fair-queue socket pacing to %u\n", rate); + printf("Setting fair-queue socket pacing to %u\n", fqrate); } - if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &rate, sizeof(rate)) < 0) { - warning("Unable to set socket pacing, using application pacing instead"); - test->no_fq_socket_pacing = 1; + if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) { + warning("Unable to set socket pacing"); } } } #endif /* HAVE_SO_MAX_PACING_RATE */ + { + unsigned int rate = test->settings->rate / 8; + if (rate > 0) { + if (test->debug) { + printf("Setting application pacing to %u\n", rate); + } + } + } opt = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { saved_errno = errno; @@ -272,7 +274,7 @@ iperf_tcp_listen(struct iperf_test *test) } /* - * If we got an IPv6 socket, figure out if it shoudl accept IPv4 + * If we got an IPv6 socket, figure out if it should accept IPv4 * connections as well. See documentation in netannounce() for * more details. */ @@ -280,9 +282,9 @@ iperf_tcp_listen(struct iperf_test *test) if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET)) { if (test->settings->domain == AF_UNSPEC) opt = 0; - else + else opt = 1; - if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt)) < 0) { saved_errno = errno; close(s); @@ -305,14 +307,54 @@ iperf_tcp_listen(struct iperf_test *test) freeaddrinfo(res); - if (listen(s, 5) < 0) { + if (listen(s, INT_MAX) < 0) { i_errno = IESTREAMLISTEN; return -1; } test->listener = s; } - + + /* Read back and verify the sender socket buffer size */ + optlen = sizeof(sndbuf_actual); + if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) { + saved_errno = errno; + close(s); + errno = saved_errno; + i_errno = IESETBUF; + return -1; + } + if (test->debug) { + printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize); + } + if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) { + i_errno = IESETBUF2; + return -1; + } + + /* Read back and verify the receiver socket buffer size */ + optlen = sizeof(rcvbuf_actual); + if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) { + saved_errno = errno; + close(s); + errno = saved_errno; + i_errno = IESETBUF; + return -1; + } + if (test->debug) { + printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize); + } + if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) { + i_errno = IESETBUF2; + return -1; + } + + if (test->json_output) { + cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize); + cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual); + cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual); + } + return s; } @@ -320,62 +362,25 @@ iperf_tcp_listen(struct iperf_test *test) /* iperf_tcp_connect * * connect to a TCP stream listener + * This function is roughly similar to netdial(), and may indeed have + * been derived from it at some point, but it sets many TCP-specific + * options between socket creation and connection. */ int iperf_tcp_connect(struct iperf_test *test) { - struct addrinfo hints, *local_res, *server_res; - char portstr[6]; + struct addrinfo *server_res; int s, opt; + socklen_t optlen; int saved_errno; + int rcvbuf_actual, sndbuf_actual; - if (test->bind_address) { - memset(&hints, 0, sizeof(hints)); - hints.ai_family = test->settings->domain; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo(test->bind_address, NULL, &hints, &local_res) != 0) { - i_errno = IESTREAMCONNECT; - return -1; - } + s = create_socket(test->settings->domain, SOCK_STREAM, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res); + if (s < 0) { + i_errno = IESTREAMCONNECT; + return -1; } - memset(&hints, 0, sizeof(hints)); - hints.ai_family = test->settings->domain; - hints.ai_socktype = SOCK_STREAM; - snprintf(portstr, sizeof(portstr), "%d", test->server_port); - if (getaddrinfo(test->server_hostname, portstr, &hints, &server_res) != 0) { - if (test->bind_address) - freeaddrinfo(local_res); - i_errno = IESTREAMCONNECT; - return -1; - } - - if ((s = socket(server_res->ai_family, SOCK_STREAM, 0)) < 0) { - if (test->bind_address) - freeaddrinfo(local_res); - freeaddrinfo(server_res); - i_errno = IESTREAMCONNECT; - return -1; - } - - if (test->bind_address) { - struct sockaddr_in *lcladdr; - lcladdr = (struct sockaddr_in *)local_res->ai_addr; - lcladdr->sin_port = htons(test->bind_port); - local_res->ai_addr = (struct sockaddr *)lcladdr; - - if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { - saved_errno = errno; - close(s); - freeaddrinfo(local_res); - freeaddrinfo(server_res); - errno = saved_errno; - i_errno = IESTREAMCONNECT; - return -1; - } - freeaddrinfo(local_res); - } - /* Set socket options */ if (test->no_delay) { opt = 1; @@ -416,18 +421,72 @@ iperf_tcp_connect(struct iperf_test *test) return -1; } } - if (test->debug) { - socklen_t optlen = sizeof(opt); - if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, &optlen) < 0) { +#if defined(HAVE_TCP_USER_TIMEOUT) + if ((opt = test->settings->snd_timeout)) { + if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) { saved_errno = errno; close(s); freeaddrinfo(server_res); errno = saved_errno; - i_errno = IESETBUF; - return -1; - } - printf("SO_SNDBUF is %u\n", opt); + i_errno = IESETUSERTIMEOUT; + return -1; + } } +#endif /* HAVE_TCP_USER_TIMEOUT */ + + /* Read back and verify the sender socket buffer size */ + optlen = sizeof(sndbuf_actual); + if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IESETBUF; + return -1; + } + if (test->debug) { + printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize); + } + if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) { + i_errno = IESETBUF2; + return -1; + } + + /* Read back and verify the receiver socket buffer size */ + optlen = sizeof(rcvbuf_actual); + if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IESETBUF; + return -1; + } + if (test->debug) { + printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize); + } + if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) { + i_errno = IESETBUF2; + return -1; + } + + if (test->json_output) { + cJSON *sock_bufsize_item = cJSON_GetObjectItem(test->json_start, "sock_bufsize"); + if (sock_bufsize_item == NULL) { + cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize); + } + + cJSON *sndbuf_actual_item = cJSON_GetObjectItem(test->json_start, "sndbuf_actual"); + if (sndbuf_actual_item == NULL) { + cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual); + } + + cJSON *rcvbuf_actual_item = cJSON_GetObjectItem(test->json_start, "rcvbuf_actual"); + if (rcvbuf_actual_item == NULL) { + cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual); + } + } + #if defined(HAVE_FLOWLABEL) if (test->settings->flowlabel) { if (server_res->ai_addr->sa_family != AF_INET6) { @@ -447,7 +506,7 @@ iperf_tcp_connect(struct iperf_test *test) freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL); freq->flr_action = IPV6_FL_A_GET; freq->flr_flags = IPV6_FL_F_CREATE; - freq->flr_share = IPV6_FL_F_CREATE | IPV6_FL_S_EXCL; + freq->flr_share = IPV6_FL_S_ANY; memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16); if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) { @@ -468,38 +527,37 @@ iperf_tcp_connect(struct iperf_test *test) errno = saved_errno; i_errno = IESETFLOW; return -1; - } + } } } #endif /* HAVE_FLOWLABEL */ -#if defined(HAVE_TCP_CONGESTION) - if (test->congestion) { - if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { - close(s); - freeaddrinfo(server_res); - i_errno = IESETCONGESTION; - return -1; - } - } -#endif /* HAVE_TCP_CONGESTION */ - #if defined(HAVE_SO_MAX_PACING_RATE) - /* If socket pacing is available and not disabled, try it. */ - if (! test->no_fq_socket_pacing) { + /* If socket pacing is specified try to enable it. */ + if (test->settings->fqrate) { /* Convert bits per second to bytes per second */ - unsigned int rate = test->settings->rate / 8; - if (rate > 0) { + unsigned int fqrate = test->settings->fqrate / 8; + if (fqrate > 0) { if (test->debug) { - printf("Socket pacing set to %u\n", rate); + printf("Setting fair-queue socket pacing to %u\n", fqrate); } - if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &rate, sizeof(rate)) < 0) { - warning("Unable to set socket pacing, using application pacing instead"); - test->no_fq_socket_pacing = 1; + if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) { + warning("Unable to set socket pacing"); } } } #endif /* HAVE_SO_MAX_PACING_RATE */ + { + unsigned int rate = test->settings->rate / 8; + if (rate > 0) { + if (test->debug) { + printf("Setting application pacing to %u\n", rate); + } + } + } + + /* Set common socket options */ + iperf_common_sockopts(test, s); if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) { saved_errno = errno;