Skip to content

Commit

Permalink
Fix for issues #472/1161 - support parallel UDP stearms under Cygwin
Browse files Browse the repository at this point in the history
  • Loading branch information
davidBar-On committed Jun 12, 2021
1 parent 3b31c4b commit 022772d
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 8 deletions.
5 changes: 5 additions & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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

Expand Down
40 changes: 39 additions & 1 deletion src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/iperf_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 #/<num of ports>`) 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)
Expand Down
13 changes: 11 additions & 2 deletions src/iperf_error.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 #/<num of ports>`) should be available for parallel UDP streams under Cygwin");
break;
default:
snprintf(errstr, len, "int_errno=%d", int_errno);
perr = 1;
Expand Down
4 changes: 3 additions & 1 deletion src/iperf_locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
7 changes: 7 additions & 0 deletions src/iperf_server_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
36 changes: 32 additions & 4 deletions src/iperf_udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down

0 comments on commit 022772d

Please sign in to comment.