Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support parallel UDP streams under Windows and Cygwin #1163

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
50 changes: 41 additions & 9 deletions src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@
#include <sys/cpuset.h>
#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 <Windows.h>
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/iperf_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 7 additions & 2 deletions src/iperf_error.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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");
Expand Down
6 changes: 6 additions & 0 deletions src/iperf_locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__))
" -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"
Expand Down
8 changes: 8 additions & 0 deletions src/iperf_server_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
49 changes: 43 additions & 6 deletions src/iperf_udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
Loading