Skip to content

Commit 6555dd5

Browse files
committed
Rebase to 3.19.1+ with re-apply of the changes to support parallel UDP streams under Windows and Cygwin
1 parent 3291a16 commit 6555dd5

File tree

7 files changed

+112
-14
lines changed

7 files changed

+112
-14
lines changed

src/iperf.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ struct iperf_test
310310
TAILQ_HEAD(xbind_addrhead, xbind_entry) xbind_addrs; /* all -X opts */
311311
int bind_port; /* --cport option */
312312
int server_port;
313+
int num_server_ports; /* second value of --port option */
314+
int server_udp_streams_accepted; /* offset of last server port used - 0 means none used */
313315
int omit; /* duration of omit period (-O flag) */
314316
int duration; /* total duration of test (-t flag) */
315317
char *diskfile_name; /* -F option */
@@ -478,10 +480,16 @@ extern int gerror; /* error value from getaddrinfo(3), for use in internal error
478480
#else
479481
#define UDP_CONNECT_MSG 0x36373839 // "6789" - legacy value was 123456789
480482
#define UDP_CONNECT_REPLY 0x39383736 // "9876" - legacy value was 987654321
483+
#define UDP_CONNECT_REPLY_NEXT_PORT 0x39383735 // "9875": for Windows - indicates use next port
481484
#define LEGACY_UDP_CONNECT_REPLY 987654321 // Old servers may still reply with the legacy value
482485
#endif
483486

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

490+
/* Any type of WIndows OS or Cygwin */
491+
#if (defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__))
492+
#define WINDOWS_ANY 1
493+
#endif /* Any Windows type */
494+
487495
#endif /* !__IPERF_H */

src/iperf_api.c

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@
6060
#include <sys/cpuset.h>
6161
#endif /* HAVE_CPUSET_SETAFFINITY */
6262

63-
#if defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
63+
#if defined(WINDOWS_ANY)
6464
#define CPU_SETSIZE __CPU_SETSIZE
65-
#endif /* __CYGWIN__, _WIN32, _WIN64, __WINDOWS__ */
65+
#endif /* WINDOWS_ANY */
6666

6767
#if defined(HAVE_SETPROCESSAFFINITYMASK)
6868
#include <Windows.h>
@@ -1200,12 +1200,30 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
12001200
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:mhX:", longopts, NULL)) != -1) {
12011201
switch (flag) {
12021202
case 'p':
1203-
portno = atoi(optarg);
1204-
if (portno < 1 || portno > 65535) {
1205-
i_errno = IEBADPORT;
1206-
return -1;
1203+
slash = optarg;
1204+
#if defined(WINDOWS_ANY)
1205+
slash = strchr(optarg, '/');
1206+
if (slash) {
1207+
*slash = '\0';
1208+
++slash;
1209+
if (*slash != '\0') {
1210+
test->num_server_ports = atoi(slash);
1211+
if (test->num_server_ports < 1 || test->num_server_ports > MAX_STREAMS) {
1212+
i_errno = IENUMPORTS;
1213+
return -1;
1214+
}
1215+
server_flag = 1;
1216+
}
12071217
}
1208-
test->server_port = portno;
1218+
#endif /* WINDOWS_ANY */
1219+
if (!slash || strlen(optarg) > 0) {
1220+
portno = atoi(optarg);
1221+
if (portno < 1 || portno > 65535) {
1222+
i_errno = IEBADPORT;
1223+
return -1;
1224+
}
1225+
test->server_port = portno;
1226+
}
12091227
break;
12101228
case 'f':
12111229
if (!optarg) {
@@ -1362,7 +1380,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
13621380
break;
13631381
case 'P':
13641382
test->num_streams = atoi(optarg);
1365-
if (test->num_streams > MAX_STREAMS) {
1383+
if (test->num_streams > MAX_STREAMS || test->num_streams < 1) {
13661384
i_errno = IENUMSTREAMS;
13671385
return -1;
13681386
}
@@ -2250,6 +2268,17 @@ iperf_exchange_parameters(struct iperf_test *test)
22502268
if (get_parameters(test) < 0)
22512269
return -1;
22522270

2271+
// Check spcific conditions required for UDP under Windows as parallel streams
2272+
// using the same port numebr is not supported.
2273+
#if defined(WINDOWS_ANY)
2274+
if (test->protocol->id == Pudp) {
2275+
if (test->num_server_ports < test->num_streams * (test->bidirectional ? 2 : 1)) {
2276+
i_errno = IEPORTNUM;
2277+
return -1;
2278+
}
2279+
}
2280+
#endif /* WINDOWS_ANY */
2281+
22532282
#if defined(HAVE_SSL)
22542283
if (test_is_authorized(test) < 0){
22552284
if (iperf_set_send_state(test, SERVER_ERROR) != 0)
@@ -3116,6 +3145,8 @@ iperf_defaults(struct iperf_test *testp)
31163145
testp->congestion_used = NULL;
31173146
testp->remote_congestion_used = NULL;
31183147
testp->server_port = PORT;
3148+
testp->num_server_ports = 1;
3149+
testp->server_udp_streams_accepted = 0;
31193150
testp->ctrl_sck = -1;
31203151
testp->listener = -1;
31213152
testp->prot_listener = -1;
@@ -3409,6 +3440,7 @@ iperf_reset_test(struct iperf_test *test)
34093440
test->mode = RECEIVER;
34103441
test->sender_has_retransmits = 0;
34113442
set_protocol(test, Ptcp);
3443+
test->server_udp_streams_accepted = 0;
34123444
test->omit = OMIT;
34133445
test->duration = DURATION;
34143446
test->server_affinity = -1;

src/iperf_api.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ enum {
435435
IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP
436436
IESERVERAUTHUSERS = 35, // Cannot access authorized users file
437437
IECNTLKA = 36, // Control connection Keepalive period should be larger than the full retry period (interval * count)
438+
IENUMPORTS = 37, // number of ports is less than 1 or larger than server limit
439+
IEPORTNUM = 38, // requested number of parallel streams is larger than the number of ports available for the server
438440
/* Test errors */
439441
IENEWTEST = 100, // Unable to create a new test (check perror)
440442
IEINITTEST = 101, // Test initialization failed (check perror)

src/iperf_error.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,11 @@ iperf_strerror(int int_errno)
506506
case IESETUSERTIMEOUT:
507507
snprintf(errstr, len, "unable to set TCP USER_TIMEOUT");
508508
perr = 1;
509+
case IENUMPORTS:
510+
snprintf(errstr, len, "number of ports is less than 1 or larger than server limit");
511+
break;
512+
case IEPORTNUM:
513+
snprintf(errstr, len, "requested number of parallel streams is larger than the number of ports available for the server");
509514
break;
510515
case IEPTHREADCREATE:
511516
snprintf(errstr, len, "unable to create thread");

src/iperf_locale.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,13 @@ const char usage_shortstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
9999
const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
100100
" iperf3 [-h|--help] [-v|--version]\n\n"
101101
"Server or Client:\n"
102+
#if (defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__))
103+
" -p, --port #[/#] server port to listen on / connect to\n"
104+
" (optional for server UDP: pool size of ports starting with\n"
105+
" port # - required for parallel UDP sterams under Windows)\n"
106+
#else
102107
" -p, --port # server port to listen on/connect to\n"
108+
#endif /* WINDOWS_ANY */
103109
" -f, --format [kmgtKMGT] format to report: Kbits, Mbits, Gbits, Tbits\n"
104110
" -i, --interval # seconds between periodic throughput reports\n"
105111
" -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
@@ -448,6 +448,7 @@ static void
448448
cleanup_server(struct iperf_test *test)
449449
{
450450
struct iperf_stream *sp;
451+
int i;
451452

452453
/* Cancel outstanding threads */
453454
int i_errno_save = i_errno;
@@ -503,6 +504,13 @@ cleanup_server(struct iperf_test *test)
503504
test->prot_listener = -1;
504505
}
505506

507+
/* Close all listening ports in case pool of listening ports is used */
508+
for (i = 0; i <= test->server_udp_streams_accepted; i++) {
509+
if (test->debug)
510+
printf("Closing UDP port %d;\n", test->server_port + i);
511+
close(test->server_port + i);
512+
}
513+
506514
/* Cancel any remaining timers. */
507515
if (test->stats_timer != NULL) {
508516
tmr_cancel(test->stats_timer);

src/iperf_udp.c

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ iperf_udp_accept(struct iperf_test *test)
385385
socklen_t len;
386386
int sz, s;
387387
int rc;
388+
int port;
388389

389390
/*
390391
* Get the current outstanding socket. This socket will be used to handle
@@ -455,11 +456,30 @@ iperf_udp_accept(struct iperf_test *test)
455456
}
456457
}
457458

459+
port = test->server_port;
460+
buf = UDP_CONNECT_REPLY;
461+
462+
/*
463+
* Since Windows does not support parallel UDP streams using the same local and remote ports,
464+
* different server port is used for each steam - indicated by replying with UDP_CONNECT_REPLY_NEXT_PORT.
465+
* Each stream, the listening port number is increased by 1.
466+
*/
467+
#if (defined(WINDOWS_ANY))
468+
if (test->num_server_ports > 1) {
469+
test->server_udp_streams_accepted++;
470+
471+
/* Change port number for next stream (but not for the last for backward compatibility) */
472+
if (test->server_udp_streams_accepted < test->num_streams * ((test->bidirectional ? 2 : 1))) {
473+
port += test->server_udp_streams_accepted;
474+
buf = UDP_CONNECT_REPLY_NEXT_PORT;
475+
}
476+
}
477+
#endif /* WINDOWS_ANY */
478+
458479
/*
459480
* Create a new "listening" socket to replace the one we were using before.
460481
*/
461-
FD_CLR(test->prot_listener, &test->read_set); // No control messages from old listener
462-
test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port);
482+
test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, port);
463483
if (test->prot_listener < 0) {
464484
i_errno = IESTREAMLISTEN;
465485
return -1;
@@ -468,8 +488,10 @@ iperf_udp_accept(struct iperf_test *test)
468488
FD_SET(test->prot_listener, &test->read_set);
469489
test->max_fd = (test->max_fd < test->prot_listener) ? test->prot_listener : test->max_fd;
470490

471-
/* Let the client know we're ready "accept" another UDP "stream" */
472-
buf = UDP_CONNECT_REPLY;
491+
/*
492+
* Let the client know we're ready "accept" another UDP "stream",
493+
* and send the listening port when applicable.
494+
*/
473495
if (write(s, &buf, sizeof(buf)) < 0) {
474496
i_errno = IESTREAMWRITE;
475497
return -1;
@@ -606,19 +628,34 @@ iperf_udp_connect(struct iperf_test *test)
606628
do {
607629
if ((sz = recv(s, &buf, sizeof(buf), 0)) < 0) {
608630
i_errno = IESTREAMREAD;
631+
if (test->num_server_ports > 1 && test->num_streams > 1) {
632+
iperf_err(test, "accept response receive failed - may be caused by an old Windows client that does not support parallel UDP streams");
633+
}
609634
return -1;
610635
}
611636
if (test->debug) {
612637
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);
613638
}
614639
i += sz;
615-
} while (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY && i < max_len_wait_for_reply);
640+
} while (buf != UDP_CONNECT_REPLY && buf != UDP_CONNECT_REPLY_NEXT_PORT && buf != LEGACY_UDP_CONNECT_REPLY && i < max_len_wait_for_reply);
616641

617-
if (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY) {
642+
/*
643+
* Since Windows does not support parallel UDP streams using the same local and remote ports,
644+
* different server port is used for each steam - indicated by UDP_CONNECT_REPLY_NEXT_PORT.
645+
*/
646+
if (buf != UDP_CONNECT_REPLY && buf != UDP_CONNECT_REPLY_NEXT_PORT && buf != LEGACY_UDP_CONNECT_REPLY) {
618647
i_errno = IESTREAMREAD;
619648
return -1;
620649
}
621650

651+
/*
652+
* On WIndows, to overcome the limit of not supporting parallel UDP streams using the same port,
653+
* `buf` will be UDP_CONNECT_REPLY_NEXT_PORT - indicating different server port for the next connection.
654+
*/
655+
if (buf == UDP_CONNECT_REPLY_NEXT_PORT) {
656+
test->server_port += 1;
657+
}
658+
622659
return s;
623660
}
624661

0 commit comments

Comments
 (0)