diff --git a/src/iperf.h b/src/iperf.h index 3fc91d0c0..2abd4bb93 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -272,6 +272,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_of_server_ports; /* second value of --port option */ + int server_udp_streams_accepted; /* offset of last server port used - 0 means none used (max `num_of_server_ports`) */ int omit; /* duration of omit period (-O flag) */ int duration; /* total duration of test (-t flag) */ char *diskfile_name; /* -F option */ @@ -397,6 +399,9 @@ struct iperf_test #define OMIT 0 /* seconds */ #define DURATION 10 /* seconds */ +#define UDP_STREAM_INIT_REQ_DATAGRAM 123456789 +#define UDP_STREAM_INIT_RESP_DATAGRAM 987654321 + #define SEC_TO_NS 1000000000LL /* too big for enum/const on some platforms */ #define MAX_RESULT_STRING 4096 diff --git a/src/iperf_api.c b/src/iperf_api.c index f8f2321ec..c290e8d6c 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1033,6 +1033,21 @@ 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': + slash = strchr(optarg, '/'); + if (slash) { + *slash = '\0'; + ++slash; + if (*slash != '\0') { + test->num_of_server_ports = atoi(slash); + if (test->num_of_server_ports < 1 || test->num_of_server_ports > MAX_STREAMS) { + i_errno = IENUMPORTS; + return -1; + } + if (test->num_of_server_ports > 1) { + server_flag = 1; + } + } + } portno = atoi(optarg); if (portno < 1 || portno > 65535) { i_errno = IEBADPORT; @@ -1898,6 +1913,26 @@ iperf_exchange_parameters(struct iperf_test *test) if (get_parameters(test) < 0) return -1; + // Check spcific conditions required for UDP under Cygwin as it does not support parallel stream using same port + if (test->protocol->id == Pudp) { + // Ensure UDP ports pool size if large enough for the required number of streams + if (test->num_of_server_ports > 1) { + if (test->num_of_server_ports < test->num_streams) { + i_errno = IEPORTNUM; + return -1; + } + } +#if defined(__CYGWIN__) + else { + // Parallel UDP streams under Cygwin must use different port number per stream + if (test->num_streams > 1) { + i_errno = IECYGWINPORTSUDP; + return -1; + } + } +#endif /* __CYGWIN__ */ + } + #if defined(HAVE_SSL) if (test_is_authorized(test) < 0){ if (iperf_set_send_state(test, SERVER_ERROR) != 0) @@ -1913,7 +1948,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) { @@ -2631,6 +2666,8 @@ iperf_defaults(struct iperf_test *testp) testp->congestion_used = NULL; testp->remote_congestion_used = NULL; testp->server_port = PORT; + testp->num_of_server_ports = 0; + testp->server_udp_streams_accepted = 0; testp->ctrl_sck = -1; testp->prot_listener = -1; testp->other_side_has_retransmits = 0; @@ -2904,6 +2941,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 cb4a86d76..0cfc7a0bc 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -379,6 +379,9 @@ enum { IEIDLETIMEOUT = 30, // Invalid value specified as idle state timeout IERCVTIMEOUT = 31, // Illegal message receive timeout IERVRSONLYRCVTIMEOUT = 32, // Client receive timeout is valid only in reverse mode + IENUMPORTS = 33, // number of ports is less than 1 or larger than server limit + IEPORTNUM = 34, // requested number of parallel streams is larger than the number of ports set for the server + IECYGWINPORTSUDP = 35, // different port (parameter `-p #/`) should be available for parallel UDP streams under Cygwin /* 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 1bcf8a83e..380951a2d 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -432,7 +432,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: @@ -441,9 +441,18 @@ 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 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 set for the server"); + break; + case IECYGWINPORTSUDP: + snprintf(errstr, len, "different port (parameter `-p #/`) should be available for parallel UDP streams under Cygwin"); + break; default: snprintf(errstr, len, "int_errno=%d", int_errno); perr = 1; diff --git a/src/iperf_locale.c b/src/iperf_locale.c index e1e9dc5b6..fe9a1793c 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -97,7 +97,9 @@ 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" - " -p, --port # server port to listen on/connect to\n" + " -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 cygwin)\n" " -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 500ff73da..92bf40789 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -371,6 +371,7 @@ static void cleanup_server(struct iperf_test *test) { struct iperf_stream *sp; + int i; /* Close open streams */ SLIST_FOREACH(sp, &test->streams, streams) { @@ -390,6 +391,12 @@ cleanup_server(struct iperf_test *test) close(test->prot_listener); } + /* Close all listening ports in case pool of listening ports is used */ + for (i = 0; i <= test->server_udp_streams_accepted; i++) { + 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 126cd639f..91e9b6db6 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -369,6 +369,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 @@ -437,10 +438,28 @@ iperf_udp_accept(struct iperf_test *test) } } + + /* + * Set listening por for accepting next parallel stream request. + * Use differet port per stream when pool of ports was defined. + * (required to support parallel stream under Windows cygwin.) + */ + buf = UDP_STREAM_INIT_RESP_DATAGRAM; + port = test->server_port; + if (test->num_of_server_ports > 1) { + if (test->server_udp_streams_accepted >= test->num_of_server_ports) { + i_errno = IEPORTNUM; + return -1; + } + test->server_udp_streams_accepted++; + port += test->server_udp_streams_accepted; + buf = port; + } + /* * Create a new "listening" socket to replace the one we were using before. */ - 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; @@ -449,8 +468,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 = 987654321; /* any content will work here */ + /* + * 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; @@ -560,7 +581,7 @@ iperf_udp_connect(struct iperf_test *test) * Write a datagram to the UDP stream to let the server know we're here. * The server learns our address by obtaining its peer's address. */ - buf = 123456789; /* this can be pretty much anything */ + buf = UDP_STREAM_INIT_REQ_DATAGRAM; /* this can be pretty much anything */ if (write(s, &buf, sizeof(buf)) < 0) { // XXX: Should this be changed to IESTREAMCONNECT? i_errno = IESTREAMWRITE; @@ -575,6 +596,13 @@ iperf_udp_connect(struct iperf_test *test) return -1; } + /* + * If the recived value is a port number, use it as the next connection port + */ + if (buf != UDP_STREAM_INIT_RESP_DATAGRAM) { + test->server_port = buf; + } + return s; }