Skip to content

Commit 52a4703

Browse files
committed
Backward compatibility added with some enhancements
1 parent 52b5cd1 commit 52a4703

File tree

7 files changed

+115
-16
lines changed

7 files changed

+115
-16
lines changed

src/iperf.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ struct iperf_test
279279
TAILQ_HEAD(xbind_addrhead, xbind_entry) xbind_addrs; /* all -X opts */
280280
int bind_port; /* --cport option */
281281
int server_port;
282+
int num_server_ports; /* second value of --port option */
283+
int server_udp_streams_accepted; /* offset of last server port used - 0 means none used */
282284
int omit; /* duration of omit period (-O flag) */
283285
int duration; /* total duration of test (-t flag) */
284286
char *diskfile_name; /* -F option */
@@ -433,9 +435,15 @@ extern int gerror; /* error value from getaddrinfo(3), for use in internal error
433435
/* UDP "connect" message and reply (textual value for Wireshark, etc. readability - legacy was numeric) */
434436
#define UDP_CONNECT_MSG 0x36373839 // "6789" - legacy value was 123456789
435437
#define UDP_CONNECT_REPLY 0x39383736 // "9876" - legacy value was 987654321
438+
#define UDP_CONNECT_REPLY_NEXT_PORT 0x39383735 // "9875": for Windows - indicates use next port
436439
#define LEGACY_UDP_CONNECT_REPLY 987654321 // Old servers may still reply with the legacy value
437440

438441
/* In Reverse mode, maximum number of packets to wait for "accept" response - to handle out of order packets */
439442
#define MAX_REVERSE_OUT_OF_ORDER_PACKETS 2
440443

444+
/* Any type of WIndows OS or Cygwin */
445+
#if (defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__))
446+
#define WINDOWS_ANY 1
447+
#endif /* Any Windows type */
448+
441449
#endif /* !__IPERF_H */

src/iperf_api.c

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@
6363
#include <sys/cpuset.h>
6464
#endif /* HAVE_CPUSET_SETAFFINITY */
6565

66-
#if defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
66+
#if defined(WINDOWS_ANY)
6767
#define CPU_SETSIZE __CPU_SETSIZE
68-
#endif /* __CYGWIN__, _WIN32, _WIN64, __WINDOWS__ */
68+
#endif /* WINDOWS_ANY */
6969

7070
#if defined(HAVE_SETPROCESSAFFINITYMASK)
7171
#include <Windows.h>
@@ -1131,12 +1131,30 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
11311131
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) {
11321132
switch (flag) {
11331133
case 'p':
1134-
portno = atoi(optarg);
1135-
if (portno < 1 || portno > 65535) {
1136-
i_errno = IEBADPORT;
1137-
return -1;
1134+
slash = optarg;
1135+
#if defined(WINDOWS_ANY)
1136+
slash = strchr(optarg, '/');
1137+
if (slash) {
1138+
*slash = '\0';
1139+
++slash;
1140+
if (*slash != '\0') {
1141+
test->num_server_ports = atoi(slash);
1142+
if (test->num_server_ports < 1 || test->num_server_ports > MAX_STREAMS) {
1143+
i_errno = IENUMPORTS;
1144+
return -1;
1145+
}
1146+
server_flag = 1;
1147+
}
11381148
}
1139-
test->server_port = portno;
1149+
#endif /* WINDOWS_ANY */
1150+
if (!slash || strlen(optarg) > 0) {
1151+
portno = atoi(optarg);
1152+
if (portno < 1 || portno > 65535) {
1153+
i_errno = IEBADPORT;
1154+
return -1;
1155+
}
1156+
test->server_port = portno;
1157+
}
11401158
break;
11411159
case 'f':
11421160
if (!optarg) {
@@ -1289,7 +1307,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
12891307
break;
12901308
case 'P':
12911309
test->num_streams = atoi(optarg);
1292-
if (test->num_streams > MAX_STREAMS) {
1310+
if (test->num_streams > MAX_STREAMS || test->num_streams < 1) {
12931311
i_errno = IENUMSTREAMS;
12941312
return -1;
12951313
}
@@ -2071,6 +2089,17 @@ iperf_exchange_parameters(struct iperf_test *test)
20712089
if (get_parameters(test) < 0)
20722090
return -1;
20732091

2092+
// Check spcific conditions required for UDP under Windows as parallel streams
2093+
// using the same port numebr is not supported.
2094+
#if defined(WINDOWS_ANY)
2095+
if (test->protocol->id == Pudp) {
2096+
if (test->num_server_ports < test->num_streams * (test->bidirectional ? 2 : 1)) {
2097+
i_errno = IEPORTNUM;
2098+
return -1;
2099+
}
2100+
}
2101+
#endif /* WINDOWS_ANY */
2102+
20742103
#if defined(HAVE_SSL)
20752104
if (test_is_authorized(test) < 0){
20762105
if (iperf_set_send_state(test, SERVER_ERROR) != 0)
@@ -2086,7 +2115,7 @@ iperf_exchange_parameters(struct iperf_test *test)
20862115
#endif //HAVE_SSL
20872116

20882117
if ((s = test->protocol->listen(test)) < 0) {
2089-
if (iperf_set_send_state(test, SERVER_ERROR) != 0)
2118+
if (iperf_set_send_state(test, SERVER_ERROR) != 0)
20902119
return -1;
20912120
err = htonl(i_errno);
20922121
if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
@@ -2809,6 +2838,8 @@ iperf_defaults(struct iperf_test *testp)
28092838
testp->congestion_used = NULL;
28102839
testp->remote_congestion_used = NULL;
28112840
testp->server_port = PORT;
2841+
testp->num_server_ports = 1;
2842+
testp->server_udp_streams_accepted = 0;
28122843
testp->ctrl_sck = -1;
28132844
testp->listener = -1;
28142845
testp->prot_listener = -1;
@@ -3083,6 +3114,7 @@ iperf_reset_test(struct iperf_test *test)
30833114
test->mode = RECEIVER;
30843115
test->sender_has_retransmits = 0;
30853116
set_protocol(test, Ptcp);
3117+
test->server_udp_streams_accepted = 0;
30863118
test->omit = OMIT;
30873119
test->duration = DURATION;
30883120
test->server_affinity = -1;

src/iperf_api.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ enum {
402402
IERVRSONLYRCVTIMEOUT = 32, // Client receive timeout is valid only in reverse mode
403403
IESNDTIMEOUT = 33, // Illegal message send timeout
404404
IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP
405+
IENUMPORTS = 35, // number of ports is less than 1 or larger than server limit
406+
IEPORTNUM = 36, // requested number of parallel streams is larger than the number of ports available for the server
405407
/* Test errors */
406408
IENEWTEST = 100, // Unable to create a new test (check perror)
407409
IEINITTEST = 101, // Test initialization failed (check perror)

src/iperf_error.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ iperf_strerror(int int_errno)
440440
case IETOTALRATE:
441441
snprintf(errstr, len, "total required bandwidth is larger than server limit");
442442
break;
443-
case IESKEWTHRESHOLD:
443+
case IESKEWTHRESHOLD:
444444
snprintf(errstr, len, "skew threshold must be a positive number");
445445
break;
446446
case IEIDLETIMEOUT:
@@ -458,12 +458,17 @@ iperf_strerror(int int_errno)
458458
case IENOMSG:
459459
snprintf(errstr, len, "idle timeout for receiving data");
460460
break;
461-
case IESETDONTFRAGMENT:
461+
case IESETDONTFRAGMENT:
462462
snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag");
463463
break;
464464
case IESETUSERTIMEOUT:
465465
snprintf(errstr, len, "unable to set TCP USER_TIMEOUT");
466466
perr = 1;
467+
case IENUMPORTS:
468+
snprintf(errstr, len, "number of ports is less than 1 or larger than server limit");
469+
break;
470+
case IEPORTNUM:
471+
snprintf(errstr, len, "requested number of parallel streams is larger than the number of ports available for the server");
467472
break;
468473
default:
469474
snprintf(errstr, len, "int_errno=%d", int_errno);

src/iperf_locale.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,13 @@ const char usage_shortstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
9797
const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
9898
" iperf3 [-h|--help] [-v|--version]\n\n"
9999
"Server or Client:\n"
100+
#if (defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__))
101+
" -p, --port #[/#] server port to listen on / connect to\n"
102+
" (optional for server UDP: pool size of ports starting with\n"
103+
" port # - required for parallel UDP sterams under Windows)\n"
104+
#else
100105
" -p, --port # server port to listen on/connect to\n"
106+
#endif /* any Windows type */
101107
" -f, --format [kmgtKMGT] format to report: Kbits, Mbits, Gbits, Tbits\n"
102108
" -i, --interval # seconds between periodic throughput reports\n"
103109
" -I, --pidfile file write PID file\n"

src/iperf_server_api.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ static void
382382
cleanup_server(struct iperf_test *test)
383383
{
384384
struct iperf_stream *sp;
385+
int i;
385386

386387
/* Close open streams */
387388
SLIST_FOREACH(sp, &test->streams, streams) {
@@ -407,6 +408,13 @@ cleanup_server(struct iperf_test *test)
407408
test->prot_listener = -1;
408409
}
409410

411+
/* Close all listening ports in case pool of listening ports is used */
412+
for (i = 0; i <= test->server_udp_streams_accepted; i++) {
413+
if (test->debug)
414+
printf("Closing UDP port %d;\n", test->server_port + i);
415+
close(test->server_port + i);
416+
}
417+
410418
/* Cancel any remaining timers. */
411419
if (test->stats_timer != NULL) {
412420
tmr_cancel(test->stats_timer);

src/iperf_udp.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ iperf_udp_accept(struct iperf_test *test)
380380
socklen_t len;
381381
int sz, s;
382382
int rc;
383+
int port;
383384

384385
/*
385386
* Get the current outstanding socket. This socket will be used to handle
@@ -450,10 +451,30 @@ iperf_udp_accept(struct iperf_test *test)
450451
}
451452
}
452453

454+
port = test->server_port;
455+
buf = UDP_CONNECT_REPLY;
456+
457+
/*
458+
* Since Windows does not suppport parallel UDP streams using the same local and remote ports,
459+
* different server port is used for each steam - indicated by replying with UDP_CONNECT_REPLY_NEXT_PORT.
460+
* Each stream, the listening port number is increased by 1.
461+
*/
462+
#if (defined(WINDOWS_ANY))
463+
if (test->num_server_ports > 1) {
464+
test->server_udp_streams_accepted++;
465+
466+
/* Change port number for next stream (but not for the last for backward compatibility) */
467+
if (test->server_udp_streams_accepted < test->num_streams * ((test->bidirectional ? 2 : 1))) {
468+
port += test->server_udp_streams_accepted;
469+
buf = UDP_CONNECT_REPLY_NEXT_PORT;
470+
}
471+
}
472+
#endif /* WINDOWS_ANY */
473+
453474
/*
454475
* Create a new "listening" socket to replace the one we were using before.
455476
*/
456-
test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port);
477+
test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, port);
457478
if (test->prot_listener < 0) {
458479
i_errno = IESTREAMLISTEN;
459480
return -1;
@@ -462,8 +483,10 @@ iperf_udp_accept(struct iperf_test *test)
462483
FD_SET(test->prot_listener, &test->read_set);
463484
test->max_fd = (test->max_fd < test->prot_listener) ? test->prot_listener : test->max_fd;
464485

465-
/* Let the client know we're ready "accept" another UDP "stream" */
466-
buf = UDP_CONNECT_REPLY;
486+
/*
487+
* Let the client know we're ready "accept" another UDP "stream",
488+
* and send the listening port when applicable.
489+
*/
467490
if (write(s, &buf, sizeof(buf)) < 0) {
468491
i_errno = IESTREAMWRITE;
469492
return -1;
@@ -600,19 +623,34 @@ iperf_udp_connect(struct iperf_test *test)
600623
do {
601624
if ((sz = recv(s, &buf, sizeof(buf), 0)) < 0) {
602625
i_errno = IESTREAMREAD;
626+
if (test->num_server_ports > 1 && test->num_streams > 1) {
627+
iperf_err(test, "accept response receive failed - may be caused by an old Windows client that does not support parallel UDP streams");
628+
}
603629
return -1;
604630
}
605631
if (test->debug) {
606632
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);
607633
}
608634
i += sz;
609-
} while (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY && i < max_len_wait_for_reply);
635+
} while (buf != UDP_CONNECT_REPLY && buf != UDP_CONNECT_REPLY_NEXT_PORT && buf != LEGACY_UDP_CONNECT_REPLY && i < max_len_wait_for_reply);
610636

611-
if (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY) {
637+
/*
638+
* Since Windows does not suppport parallel UDP streams using the same local and remote ports,
639+
* different server port is used for each steam - indicated by UDP_CONNECT_REPLY_NEXT_PORT.
640+
*/
641+
if (buf != UDP_CONNECT_REPLY && buf != UDP_CONNECT_REPLY_NEXT_PORT && buf != LEGACY_UDP_CONNECT_REPLY) {
612642
i_errno = IESTREAMREAD;
613643
return -1;
614644
}
615645

646+
/*
647+
* On WIndows, to overcome the limit of not supporting parallel UDP streams using the same port,
648+
* `buf` will be UDP_CONNECT_REPLY_NEXT_PORT - indicating different server port for the next connection.
649+
*/
650+
if (buf == UDP_CONNECT_REPLY_NEXT_PORT) {
651+
test->server_port += 1;
652+
}
653+
616654
return s;
617655
}
618656

0 commit comments

Comments
 (0)