From 962c4c18a66b37228d00f11e62da5a154b6e2130 Mon Sep 17 00:00:00 2001 From: DavidBar-On Date: Sun, 29 Sep 2024 10:05:07 +0300 Subject: [PATCH] Rebase with master of 3.17.1+ --- src/iperf.h | 8 +++++++ src/iperf_api.c | 50 ++++++++++++++++++++++++++++++++++-------- src/iperf_api.h | 2 ++ src/iperf_error.c | 9 ++++++-- src/iperf_locale.c | 6 +++++ src/iperf_server_api.c | 8 +++++++ src/iperf_udp.c | 49 ++++++++++++++++++++++++++++++++++++----- 7 files changed, 115 insertions(+), 17 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index 7d14a3453..cf78ea0bf 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -302,6 +302,8 @@ struct iperf_test TAILQ_HEAD(xbind_addrhead, xbind_entry) xbind_addrs; /* all -X opts */ int bind_port; /* --cport option */ int server_port; + int num_server_ports; /* second value of --port option */ + int server_udp_streams_accepted; /* offset of last server port used - 0 means none used */ int omit; /* duration of omit period (-O flag) */ int duration; /* total duration of test (-t flag) */ char *diskfile_name; /* -F option */ @@ -466,10 +468,16 @@ extern int gerror; /* error value from getaddrinfo(3), for use in internal error #else #define UDP_CONNECT_MSG 0x36373839 // "6789" - legacy value was 123456789 #define UDP_CONNECT_REPLY 0x39383736 // "9876" - legacy value was 987654321 +#define UDP_CONNECT_REPLY_NEXT_PORT 0x39383735 // "9875": for Windows - indicates use next port #define LEGACY_UDP_CONNECT_REPLY 987654321 // Old servers may still reply with the legacy value #endif /* In Reverse mode, maximum number of packets to wait for "accept" response - to handle out of order packets */ #define MAX_REVERSE_OUT_OF_ORDER_PACKETS 2 +/* Any type of WIndows OS or Cygwin */ +#if (defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)) +#define WINDOWS_ANY 1 +#endif /* Any Windows type */ + #endif /* !__IPERF_H */ diff --git a/src/iperf_api.c b/src/iperf_api.c index 60efd1273..5a8a74439 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -61,9 +61,9 @@ #include #endif /* HAVE_CPUSET_SETAFFINITY */ -#if defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) +#if defined(WINDOWS_ANY) #define CPU_SETSIZE __CPU_SETSIZE -#endif /* __CYGWIN__, _WIN32, _WIN64, __WINDOWS__ */ +#endif /* WINDOWS_ANY */ #if defined(HAVE_SETPROCESSAFFINITYMASK) #include @@ -1177,12 +1177,30 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) { switch (flag) { case 'p': - portno = atoi(optarg); - if (portno < 1 || portno > 65535) { - i_errno = IEBADPORT; - return -1; + slash = optarg; +#if defined(WINDOWS_ANY) + slash = strchr(optarg, '/'); + if (slash) { + *slash = '\0'; + ++slash; + if (*slash != '\0') { + test->num_server_ports = atoi(slash); + if (test->num_server_ports < 1 || test->num_server_ports > MAX_STREAMS) { + i_errno = IENUMPORTS; + return -1; + } + server_flag = 1; + } } - test->server_port = portno; +#endif /* WINDOWS_ANY */ + if (!slash || strlen(optarg) > 0) { + portno = atoi(optarg); + if (portno < 1 || portno > 65535) { + i_errno = IEBADPORT; + return -1; + } + test->server_port = portno; + } break; case 'f': if (!optarg) { @@ -1339,7 +1357,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) break; case 'P': test->num_streams = atoi(optarg); - if (test->num_streams > MAX_STREAMS) { + if (test->num_streams > MAX_STREAMS || test->num_streams < 1) { i_errno = IENUMSTREAMS; return -1; } @@ -2197,6 +2215,17 @@ iperf_exchange_parameters(struct iperf_test *test) if (get_parameters(test) < 0) return -1; + // Check spcific conditions required for UDP under Windows as parallel streams + // using the same port numebr is not supported. +#if defined(WINDOWS_ANY) + if (test->protocol->id == Pudp) { + if (test->num_server_ports < test->num_streams * (test->bidirectional ? 2 : 1)) { + i_errno = IEPORTNUM; + return -1; + } + } +#endif /* WINDOWS_ANY */ + #if defined(HAVE_SSL) if (test_is_authorized(test) < 0){ if (iperf_set_send_state(test, SERVER_ERROR) != 0) @@ -2212,7 +2241,7 @@ iperf_exchange_parameters(struct iperf_test *test) #endif //HAVE_SSL if ((s = test->protocol->listen(test)) < 0) { - if (iperf_set_send_state(test, SERVER_ERROR) != 0) + if (iperf_set_send_state(test, SERVER_ERROR) != 0) return -1; err = htonl(i_errno); if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) { @@ -3025,6 +3054,8 @@ iperf_defaults(struct iperf_test *testp) testp->congestion_used = NULL; testp->remote_congestion_used = NULL; testp->server_port = PORT; + testp->num_server_ports = 1; + testp->server_udp_streams_accepted = 0; testp->ctrl_sck = -1; testp->listener = -1; testp->prot_listener = -1; @@ -3307,6 +3338,7 @@ iperf_reset_test(struct iperf_test *test) test->mode = RECEIVER; test->sender_has_retransmits = 0; set_protocol(test, Ptcp); + test->server_udp_streams_accepted = 0; test->omit = OMIT; test->duration = DURATION; test->server_affinity = -1; diff --git a/src/iperf_api.h b/src/iperf_api.h index 2b71613e9..c8cedde13 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -420,6 +420,8 @@ enum { IESNDTIMEOUT = 33, // Illegal message send timeout IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP IESERVERAUTHUSERS = 35, // Cannot access authorized users file + IENUMPORTS = 36, // number of ports is less than 1 or larger than server limit + IEPORTNUM = 37, // requested number of parallel streams is larger than the number of ports available for the server /* Test errors */ IENEWTEST = 100, // Unable to create a new test (check perror) IEINITTEST = 101, // Test initialization failed (check perror) diff --git a/src/iperf_error.c b/src/iperf_error.c index 3388d376e..c02daf1aa 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -464,7 +464,7 @@ iperf_strerror(int int_errno) case IETOTALRATE: snprintf(errstr, len, "total required bandwidth is larger than server limit"); break; - case IESKEWTHRESHOLD: + case IESKEWTHRESHOLD: snprintf(errstr, len, "skew threshold must be a positive number"); break; case IEIDLETIMEOUT: @@ -482,12 +482,17 @@ iperf_strerror(int int_errno) case IENOMSG: snprintf(errstr, len, "idle timeout for receiving data"); break; - case IESETDONTFRAGMENT: + case IESETDONTFRAGMENT: snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag"); break; case IESETUSERTIMEOUT: snprintf(errstr, len, "unable to set TCP USER_TIMEOUT"); perr = 1; + case IENUMPORTS: + snprintf(errstr, len, "number of ports is less than 1 or larger than server limit"); + break; + case IEPORTNUM: + snprintf(errstr, len, "requested number of parallel streams is larger than the number of ports available for the server"); break; case IEPTHREADCREATE: snprintf(errstr, len, "unable to create thread"); diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 5c6e66dfd..6997179e2 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -99,7 +99,13 @@ const char usage_shortstr[] = "Usage: iperf3 [-s|-c host] [options]\n" const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " iperf3 [-h|--help] [-v|--version]\n\n" "Server or Client:\n" +#if defined(WINDOWS_ANY) + " -p, --port #[/#] server port to listen on / connect to\n" + " (optional for server UDP: pool size of ports starting with\n" + " port # - required for parallel UDP sterams under Windows)\n" +#else " -p, --port # server port to listen on/connect to\n" +#endif /* WINDOWS_ANY */ " -f, --format [kmgtKMGT] format to report: Kbits, Mbits, Gbits, Tbits\n" " -i, --interval # seconds between periodic throughput reports\n" " -I, --pidfile file write PID file\n" diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 9727cdddb..8f8c371c0 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -442,6 +442,7 @@ static void cleanup_server(struct iperf_test *test) { struct iperf_stream *sp; + int i; /* Cancel outstanding threads */ int i_errno_save = i_errno; @@ -494,6 +495,13 @@ cleanup_server(struct iperf_test *test) test->prot_listener = -1; } + /* Close all listening ports in case pool of listening ports is used */ + for (i = 0; i <= test->server_udp_streams_accepted; i++) { + if (test->debug) + printf("Closing UDP port %d;\n", test->server_port + i); + close(test->server_port + i); + } + /* Cancel any remaining timers. */ if (test->stats_timer != NULL) { tmr_cancel(test->stats_timer); diff --git a/src/iperf_udp.c b/src/iperf_udp.c index 760116b4b..081100784 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -373,6 +373,7 @@ iperf_udp_accept(struct iperf_test *test) socklen_t len; int sz, s; int rc; + int port; /* * Get the current outstanding socket. This socket will be used to handle @@ -443,11 +444,30 @@ iperf_udp_accept(struct iperf_test *test) } } + port = test->server_port; + buf = UDP_CONNECT_REPLY; + + /* + * Since Windows does not suppport parallel UDP streams using the same local and remote ports, + * different server port is used for each steam - indicated by replying with UDP_CONNECT_REPLY_NEXT_PORT. + * Each stream, the listening port number is increased by 1. + */ +#if (defined(WINDOWS_ANY)) + if (test->num_server_ports > 1) { + test->server_udp_streams_accepted++; + + /* Change port number for next stream (but not for the last for backward compatibility) */ + if (test->server_udp_streams_accepted < test->num_streams * ((test->bidirectional ? 2 : 1))) { + port += test->server_udp_streams_accepted; + buf = UDP_CONNECT_REPLY_NEXT_PORT; + } + } +#endif /* WINDOWS_ANY */ + /* * Create a new "listening" socket to replace the one we were using before. */ - FD_CLR(test->prot_listener, &test->read_set); // No control messages from old listener - test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port); + test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, port); if (test->prot_listener < 0) { i_errno = IESTREAMLISTEN; return -1; @@ -456,8 +476,10 @@ iperf_udp_accept(struct iperf_test *test) FD_SET(test->prot_listener, &test->read_set); test->max_fd = (test->max_fd < test->prot_listener) ? test->prot_listener : test->max_fd; - /* Let the client know we're ready "accept" another UDP "stream" */ - buf = UDP_CONNECT_REPLY; + /* + * Let the client know we're ready "accept" another UDP "stream", + * and send the listening port when applicable. + */ if (write(s, &buf, sizeof(buf)) < 0) { i_errno = IESTREAMWRITE; return -1; @@ -594,19 +616,34 @@ iperf_udp_connect(struct iperf_test *test) do { if ((sz = recv(s, &buf, sizeof(buf), 0)) < 0) { i_errno = IESTREAMREAD; + if (test->num_server_ports > 1 && test->num_streams > 1) { + iperf_err(test, "accept response receive failed - may be caused by an old Windows client that does not support parallel UDP streams"); + } return -1; } if (test->debug) { printf("Connect received for Socket %d, sz=%d, buf=%x, i=%d, max_len_wait_for_reply=%d\n", s, sz, buf, i, max_len_wait_for_reply); } i += sz; - } while (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY && i < max_len_wait_for_reply); + } while (buf != UDP_CONNECT_REPLY && buf != UDP_CONNECT_REPLY_NEXT_PORT && buf != LEGACY_UDP_CONNECT_REPLY && i < max_len_wait_for_reply); - if (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY) { + /* + * Since Windows does not suppport parallel UDP streams using the same local and remote ports, + * different server port is used for each steam - indicated by UDP_CONNECT_REPLY_NEXT_PORT. + */ + if (buf != UDP_CONNECT_REPLY && buf != UDP_CONNECT_REPLY_NEXT_PORT && buf != LEGACY_UDP_CONNECT_REPLY) { i_errno = IESTREAMREAD; return -1; } + /* + * On WIndows, to overcome the limit of not supporting parallel UDP streams using the same port, + * `buf` will be UDP_CONNECT_REPLY_NEXT_PORT - indicating different server port for the next connection. + */ + if (buf == UDP_CONNECT_REPLY_NEXT_PORT) { + test->server_port += 1; + } + return s; }