--- embedaddon/iperf/src/iperf_client_api.c 2016/10/18 13:28:18 1.1 +++ embedaddon/iperf/src/iperf_client_api.c 2021/03/17 00:36:46 1.1.1.2 @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014, 2015, The Regents of the University of + * iperf, Copyright (c) 2014-2020, 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. @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -40,14 +41,23 @@ #include "iperf_api.h" #include "iperf_util.h" #include "iperf_locale.h" +#include "iperf_time.h" #include "net.h" #include "timer.h" +#if defined(HAVE_TCP_CONGESTION) +#if !defined(TCP_CA_NAME_MAX) +#define TCP_CA_NAME_MAX 16 +#endif /* TCP_CA_NAME_MAX */ +#endif /* HAVE_TCP_CONGESTION */ int -iperf_create_streams(struct iperf_test *test) +iperf_create_streams(struct iperf_test *test, int sender) { int i, s; +#if defined(HAVE_TCP_CONGESTION) + int saved_errno; +#endif /* HAVE_TCP_CONGESTION */ struct iperf_stream *sp; int orig_bind_port = test->bind_port; @@ -59,13 +69,42 @@ iperf_create_streams(struct iperf_test *test) if ((s = test->protocol->connect(test)) < 0) return -1; - if (test->sender) +#if defined(HAVE_TCP_CONGESTION) + if (test->protocol->id == Ptcp) { + if (test->congestion) { + if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { + saved_errno = errno; + close(s); + errno = saved_errno; + i_errno = IESETCONGESTION; + return -1; + } + } + { + socklen_t len = TCP_CA_NAME_MAX; + char ca[TCP_CA_NAME_MAX + 1]; + if (getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len) < 0) { + saved_errno = errno; + close(s); + errno = saved_errno; + i_errno = IESETCONGESTION; + return -1; + } + test->congestion_used = strdup(ca); + if (test->debug) { + printf("Congestion algorithm is %s\n", test->congestion_used); + } + } + } +#endif /* HAVE_TCP_CONGESTION */ + + if (sender) FD_SET(s, &test->write_set); else FD_SET(s, &test->read_set); if (s > test->max_fd) test->max_fd = s; - sp = iperf_new_stream(test, s); + sp = iperf_new_stream(test, s, sender); if (!sp) return -1; @@ -78,7 +117,7 @@ iperf_create_streams(struct iperf_test *test) } static void -test_timer_proc(TimerClientData client_data, struct timeval *nowP) +test_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { struct iperf_test *test = client_data.p; @@ -87,7 +126,7 @@ test_timer_proc(TimerClientData client_data, struct ti } static void -client_stats_timer_proc(TimerClientData client_data, struct timeval *nowP) +client_stats_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { struct iperf_test *test = client_data.p; @@ -98,7 +137,7 @@ client_stats_timer_proc(TimerClientData client_data, s } static void -client_reporter_timer_proc(TimerClientData client_data, struct timeval *nowP) +client_reporter_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { struct iperf_test *test = client_data.p; @@ -111,10 +150,10 @@ client_reporter_timer_proc(TimerClientData client_data static int create_client_timers(struct iperf_test * test) { - struct timeval now; + struct iperf_time now; TimerClientData cd; - if (gettimeofday(&now, NULL) < 0) { + if (iperf_time_now(&now) < 0) { i_errno = IEINITTEST; return -1; } @@ -146,7 +185,7 @@ create_client_timers(struct iperf_test * test) } static void -client_omit_timer_proc(TimerClientData client_data, struct timeval *nowP) +client_omit_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { struct iperf_test *test = client_data.p; @@ -154,7 +193,7 @@ client_omit_timer_proc(TimerClientData client_data, st test->omitting = 0; iperf_reset_stats(test); if (test->verbose && !test->json_output && test->reporter_interval == 0) - iprintf(test, "%s", report_omit_done); + iperf_printf(test, "%s", report_omit_done); /* Reset the timers. */ if (test->stats_timer != NULL) @@ -166,14 +205,14 @@ client_omit_timer_proc(TimerClientData client_data, st static int create_client_omit_timer(struct iperf_test * test) { - struct timeval now; + struct iperf_time now; TimerClientData cd; if (test->omit == 0) { test->omit_timer = NULL; test->omitting = 0; } else { - if (gettimeofday(&now, NULL) < 0) { + if (iperf_time_now(&now) < 0) { i_errno = IEINITTEST; return -1; } @@ -213,7 +252,14 @@ iperf_handle_message_client(struct iperf_test *test) test->on_connect(test); break; case CREATE_STREAMS: - if (iperf_create_streams(test) < 0) + if (test->mode == BIDIRECTIONAL) + { + if (iperf_create_streams(test, 1) < 0) + return -1; + if (iperf_create_streams(test, 0) < 0) + return -1; + } + else if (iperf_create_streams(test, test->mode) < 0) return -1; break; case TEST_START: @@ -223,7 +269,7 @@ iperf_handle_message_client(struct iperf_test *test) return -1; if (create_client_omit_timer(test) < 0) return -1; - if (!test->reverse) + if (test->mode) if (iperf_create_send_timers(test) < 0) return -1; break; @@ -290,7 +336,7 @@ iperf_connect(struct iperf_test *test) /* Create and connect the control channel */ if (test->ctrl_sck < 0) // Create the control channel using an ephemeral port - test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, 0, test->server_hostname, test->server_port); + test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, 0, test->server_hostname, test->server_port, test->settings->connect_timeout); if (test->ctrl_sck < 0) { i_errno = IECONNECT; return -1; @@ -304,6 +350,72 @@ iperf_connect(struct iperf_test *test) FD_SET(test->ctrl_sck, &test->read_set); if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck; + int opt; + socklen_t len; + + len = sizeof(opt); + if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) { + test->ctrl_sck_mss = 0; + } + else { + if (opt > 0 && opt <= MAX_UDP_BLOCKSIZE) { + test->ctrl_sck_mss = opt; + } + else { + char str[128]; + snprintf(str, sizeof(str), + "Ignoring nonsense TCP MSS %d", opt); + warning(str); + + test->ctrl_sck_mss = 0; + } + } + + if (test->verbose) { + printf("Control connection MSS %d\n", test->ctrl_sck_mss); + } + + /* + * If we're doing a UDP test and the block size wasn't explicitly + * set, then use the known MSS of the control connection to pick + * an appropriate default. If we weren't able to get the + * MSS for some reason, then default to something that should + * work on non-jumbo-frame Ethernet networks. The goal is to + * pick a reasonable default that is large but should get from + * sender to receiver without any IP fragmentation. + * + * We assume that the control connection is routed the same as the + * data packets (thus has the same PMTU). Also in the case of + * --reverse tests, we assume that the MTU is the same in both + * directions. Note that even if the algorithm guesses wrong, + * the user always has the option to override. + */ + if (test->protocol->id == Pudp) { + if (test->settings->blksize == 0) { + if (test->ctrl_sck_mss) { + test->settings->blksize = test->ctrl_sck_mss; + } + else { + test->settings->blksize = DEFAULT_UDP_BLKSIZE; + } + if (test->verbose) { + printf("Setting UDP block size to %d\n", test->settings->blksize); + } + } + + /* + * Regardless of whether explicitly or implicitly set, if the + * block size is larger than the MSS, print a warning. + */ + if (test->ctrl_sck_mss > 0 && + test->settings->blksize > test->ctrl_sck_mss) { + char str[128]; + snprintf(str, sizeof(str), + "UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops", test->settings->blksize, test->ctrl_sck_mss); + warning(str); + } + } + return 0; } @@ -324,6 +436,10 @@ iperf_client_end(struct iperf_test *test) if (iperf_set_send_state(test, IPERF_DONE) != 0) return -1; + /* Close control socket */ + if (test->ctrl_sck) + close(test->ctrl_sck); + return 0; } @@ -334,10 +450,14 @@ iperf_run_client(struct iperf_test * test) int startup; int result = 0; fd_set read_set, write_set; - struct timeval now; + struct iperf_time now; struct timeval* timeout = NULL; struct iperf_stream *sp; + if (test->logfile) + if (iperf_open_logfile(test) < 0) + return -1; + if (test->affinity != -1) if (iperf_setaffinity(test, test->affinity) != 0) return -1; @@ -350,15 +470,15 @@ iperf_run_client(struct iperf_test * test) cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version)); cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info())); } else if (test->verbose) { - iprintf(test, "%s\n", version); - iprintf(test, "%s", ""); - iprintf(test, "%s\n", get_system_info()); + iperf_printf(test, "%s\n", version); + iperf_printf(test, "%s", ""); + iperf_printf(test, "%s\n", get_system_info()); iflush(test); } /* Start the client and connect to the server */ if (iperf_connect(test) < 0) - return -1; + goto cleanup_and_fail; /* Begin calculating CPU utilization */ cpu_util(NULL); @@ -367,17 +487,17 @@ iperf_run_client(struct iperf_test * test) while (test->state != IPERF_DONE) { memcpy(&read_set, &test->read_set, sizeof(fd_set)); memcpy(&write_set, &test->write_set, sizeof(fd_set)); - (void) gettimeofday(&now, NULL); + iperf_time_now(&now); timeout = tmr_timeout(&now); result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout); if (result < 0 && errno != EINTR) { i_errno = IESELECT; - return -1; + goto cleanup_and_fail; } if (result > 0) { if (FD_ISSET(test->ctrl_sck, &read_set)) { if (iperf_handle_message_client(test) < 0) { - return -1; + goto cleanup_and_fail; } FD_CLR(test->ctrl_sck, &read_set); } @@ -397,18 +517,26 @@ iperf_run_client(struct iperf_test * test) } } - if (test->reverse) { - // Reverse mode. Client receives. - if (iperf_recv(test, &read_set) < 0) - return -1; + + if (test->mode == BIDIRECTIONAL) + { + if (iperf_send(test, &write_set) < 0) + goto cleanup_and_fail; + if (iperf_recv(test, &read_set) < 0) + goto cleanup_and_fail; + } else if (test->mode == SENDER) { + // Regular mode. Client sends. + if (iperf_send(test, &write_set) < 0) + goto cleanup_and_fail; } else { - // Regular mode. Client sends. - if (iperf_send(test, &write_set) < 0) - return -1; + // Reverse mode. Client receives. + if (iperf_recv(test, &read_set) < 0) + goto cleanup_and_fail; } + /* Run the timers. */ - (void) gettimeofday(&now, NULL); + iperf_time_now(&now); tmr_run(&now); /* Is the test done yet? */ @@ -429,7 +557,7 @@ iperf_run_client(struct iperf_test * test) cpu_util(test->cpu_util); test->stats_callback(test); if (iperf_set_send_state(test, TEST_END) != 0) - return -1; + goto cleanup_and_fail; } } // If we're in reverse mode, continue draining the data @@ -437,9 +565,9 @@ iperf_run_client(struct iperf_test * test) // deadlock where the server side fills up its pipe(s) // and gets blocked, so it can't receive state changes // from the client side. - else if (test->reverse && test->state == TEST_END) { + else if (test->mode == RECEIVER && test->state == TEST_END) { if (iperf_recv(test, &read_set) < 0) - return -1; + goto cleanup_and_fail; } } @@ -447,11 +575,18 @@ iperf_run_client(struct iperf_test * test) if (iperf_json_finish(test) < 0) return -1; } else { - iprintf(test, "\n"); - iprintf(test, "%s", report_done); + iperf_printf(test, "\n"); + iperf_printf(test, "%s", report_done); } iflush(test); return 0; + + cleanup_and_fail: + iperf_client_end(test); + if (test->json_output) + iperf_json_finish(test); + iflush(test); + return -1; }