Skip to content

Commit

Permalink
Backward compatibility added with some enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
davidBar-On committed Apr 16, 2023
1 parent 52b5cd1 commit 52a4703
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 16 deletions.
8 changes: 8 additions & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,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 @@ -433,9 +435,15 @@ extern int gerror; /* error value from getaddrinfo(3), for use in internal error
/* UDP "connect" message and reply (textual value for Wireshark, etc. readability - legacy was numeric) */
#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

/* 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 @@ -63,9 +63,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 @@ -1131,12 +1131,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 @@ -1289,7 +1307,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 @@ -2071,6 +2089,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 @@ -2086,7 +2115,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 @@ -2809,6 +2838,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 @@ -3083,6 +3114,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 @@ -402,6 +402,8 @@ enum {
IERVRSONLYRCVTIMEOUT = 32, // Client receive timeout is valid only in reverse mode
IESNDTIMEOUT = 33, // Illegal message send timeout
IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP
IENUMPORTS = 35, // number of ports is less than 1 or larger than server limit
IEPORTNUM = 36, // 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 @@ -440,7 +440,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 @@ -458,12 +458,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;
default:
snprintf(errstr, len, "int_errno=%d", int_errno);
Expand Down
6 changes: 6 additions & 0 deletions src/iperf_locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,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 /* any Windows type */
" -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 @@ -382,6 +382,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 @@ -407,6 +408,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
48 changes: 43 additions & 5 deletions src/iperf_udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,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 @@ -450,10 +451,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.
*/
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 @@ -462,8 +483,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 @@ -600,19 +623,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

0 comments on commit 52a4703

Please sign in to comment.