From 0d10fec19e048632042c1178c8cff117a59046b5 Mon Sep 17 00:00:00 2001 From: DavidBar-On Date: Wed, 23 Oct 2024 09:54:39 +0300 Subject: [PATCH 1/3] Support for UDP Data-port that is different than the Control-port --- src/iperf.h | 1 + src/iperf_api.c | 57 ++++++++++++++++++++++++++++++++++++++-------- src/iperf_api.h | 1 + src/iperf_error.c | 5 +++- src/iperf_locale.c | 5 ++-- src/iperf_udp.c | 6 ++--- 6 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index 7ced5c80d..ee5c44783 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -307,6 +307,7 @@ struct iperf_test TAILQ_HEAD(xbind_addrhead, xbind_entry) xbind_addrs; /* all -X opts */ int bind_port; /* --cport option */ int server_port; + int data_port; /* from --port option */ int omit; /* duration of omit period (-O flag) */ int duration; /* total duration of test (-t flag) */ char *diskfile_name; /* -F option */ diff --git a/src/iperf_api.c b/src/iperf_api.c index db614adf6..2061a488d 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -295,6 +295,12 @@ iperf_get_test_server_port(struct iperf_test *ipt) return ipt->server_port; } +int +iperf_get_test_data_port(struct iperf_test *ipt) +{ + return ipt->data_port; +} + char* iperf_get_test_server_hostname(struct iperf_test *ipt) { @@ -566,6 +572,12 @@ iperf_set_test_server_port(struct iperf_test *ipt, int srv_port) ipt->server_port = srv_port; } +void +iperf_set_test_data_port(struct iperf_test *ipt, int srv_data_port) +{ + ipt->data_port = srv_data_port; +} + void iperf_set_test_socket_bufsize(struct iperf_test *ipt, int socket_bufsize) { @@ -962,10 +974,10 @@ iperf_on_connect(struct iperf_test *test) iperf_printf(test, report_time, now_str); if (test->role == 'c') { - if (test->json_output) - cJSON_AddItemToObject(test->json_start, "connecting_to", iperf_json_printf("host: %s port: %d", test->server_hostname, (int64_t) test->server_port)); - else { - iperf_printf(test, report_connecting, test->server_hostname, test->server_port); + if (test->json_output) { + cJSON_AddItemToObject(test->json_start, "connecting_to", iperf_json_printf("host: %s port: %d data-port: %d", test->server_hostname, (int64_t) test->server_port, (int64_t) test->data_port)); + } else { + iperf_printf(test, report_connecting, test->server_hostname, test->server_port, test->data_port); if (test->reverse) iperf_printf(test, report_reverse, test->server_hostname); } @@ -1192,12 +1204,27 @@ 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:mhX:", longopts, NULL)) != -1) { switch (flag) { case 'p': - portno = atoi(optarg); - if (portno < 1 || portno > 65535) { - i_errno = IEBADPORT; - return -1; + + slash = strchr(optarg, '/'); + if (slash) { // Get Data port + *slash = '\0'; + ++slash; + portno = atoi(slash); + if (portno < 1 || portno > 65535) { + i_errno = IEBADPORT; + return -1; + } + test->data_port = portno; + client_flag = 1; } - test->server_port = portno; + if (strlen(optarg) > 0) { // Get control (and data) port + portno = atoi(optarg); + if (portno < 1 || portno > 65535) { + i_errno = IEBADPORT; + return -1; + } + test->server_port = portno; + } break; case 'f': if (!optarg) { @@ -1826,6 +1853,11 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) if (!rate_flag) test->settings->rate = test->protocol->id == Pudp ? UDP_RATE : 0; + if (test->data_port != test->server_port && test->protocol->id != Pudp) { + i_errno = IEDATAPORT; + return -1; + } + /* if no bytes or blocks specified, nor a duration_flag, and we have -F, ** get the file-size as the bytes count to be transferred */ @@ -2321,6 +2353,8 @@ send_parameters(struct iperf_test *test) cJSON_AddNumberToObject(j, "time", test->duration); cJSON_AddNumberToObject(j, "num", test->settings->bytes); cJSON_AddNumberToObject(j, "blockcount", test->settings->blocks); + if (test->protocol->id == Pudp) + cJSON_AddNumberToObject(j, "data_port", test->data_port); if (test->settings->mss) cJSON_AddNumberToObject(j, "MSS", test->settings->mss); if (test->no_delay) @@ -2440,7 +2474,9 @@ get_parameters(struct iperf_test *test) test->settings->blocks = 0; if ((j_p = iperf_cJSON_GetObjectItemType(j, "blockcount", cJSON_Number)) != NULL) test->settings->blocks = j_p->valueint; - if ((j_p = iperf_cJSON_GetObjectItemType(j, "MSS", cJSON_Number)) != NULL) + if ((j_p = iperf_cJSON_GetObjectItemType(j, "data_port", cJSON_Number)) != NULL) + test->data_port = j_p->valueint; + if ((j_p = iperf_cJSON_GetObjectItemType(j, "MSS", cJSON_Number)) != NULL) test->settings->mss = j_p->valueint; if ((j_p = iperf_cJSON_GetObjectItemType(j, "nodelay", cJSON_True)) != NULL) test->no_delay = 1; @@ -3079,6 +3115,7 @@ iperf_defaults(struct iperf_test *testp) testp->congestion_used = NULL; testp->remote_congestion_used = NULL; testp->server_port = PORT; + testp->data_port = testp->server_port; testp->ctrl_sck = -1; testp->listener = -1; testp->prot_listener = -1; diff --git a/src/iperf_api.h b/src/iperf_api.h index 02bc0f12b..163a4f14a 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -433,6 +433,7 @@ enum { IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP IESERVERAUTHUSERS = 35, // Cannot access authorized users file IECNTLKA = 36, // Control connection Keepalive period should be larger than the full retry period (interval * count) + IEDATAPORT = 37, // Data port is supported only for UDP /* Test errors */ IENEWTEST = 100, // Unable to create a new test (check perror) IEINITTEST = 101, // Test initialization failed (check perror) diff --git a/src/iperf_error.c b/src/iperf_error.c index f76ad5f21..18c0f4983 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -222,7 +222,10 @@ iperf_strerror(int int_errno) snprintf(errstr, len, "bad format specifier (valid formats are in the set [kmgtKMGT])"); break; case IEBADPORT: - snprintf(errstr, len, "port number must be between 1 and 65535 inclusive"); + snprintf(errstr, len, "illegal port value, number must be between 1 and 65535 inclusive"); + break; + case IEDATAPORT: + snprintf(errstr, len, "data port is supported only for UDP"); break; case IEMSS: snprintf(errstr, len, "TCP MSS too large (maximum = %d bytes)", MAX_MSS); diff --git a/src/iperf_locale.c b/src/iperf_locale.c index c0c5c7007..b10060f9e 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -99,7 +99,8 @@ 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 Client only - port for UDP data send/receive)\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" @@ -316,7 +317,7 @@ const char report_time[] = "Time: %s\n"; const char report_connecting[] = -"Connecting to host %s, port %d\n"; +"Connecting to host %s, port %d, data-port %d\n"; const char report_authentication_succeeded[] = "Authentication succeeded for user '%s' ts %" PRIu64 "\n"; diff --git a/src/iperf_udp.c b/src/iperf_udp.c index b848a8f44..76f942d56 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -450,7 +450,7 @@ iperf_udp_accept(struct iperf_test *test) * 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, test->data_port); if (test->prot_listener < 0) { i_errno = IESTREAMLISTEN; return -1; @@ -482,7 +482,7 @@ iperf_udp_listen(struct iperf_test *test) { int s; - if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port)) < 0) { + if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->data_port)) < 0) { i_errno = IESTREAMLISTEN; return -1; } @@ -511,7 +511,7 @@ iperf_udp_connect(struct iperf_test *test) int i, max_len_wait_for_reply; /* Create and bind our local socket. */ - if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, -1)) < 0) { + if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->data_port, -1)) < 0) { i_errno = IESTREAMCONNECT; return -1; } From 2e392e3c2d27ec76ec4f8d94dc241cfab652e91b Mon Sep 17 00:00:00 2001 From: DavidBar-On Date: Thu, 27 Mar 2025 08:56:55 +0200 Subject: [PATCH 2/3] Rebase and fix parameter checking error --- src/iperf_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iperf_api.c b/src/iperf_api.c index 2061a488d..6d6ffbfb4 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1853,7 +1853,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) if (!rate_flag) test->settings->rate = test->protocol->id == Pudp ? UDP_RATE : 0; - if (test->data_port != test->server_port && test->protocol->id != Pudp) { + if (test->role == 'c' && test->data_port != test->server_port && test->protocol->id != Pudp) { i_errno = IEDATAPORT; return -1; } From 92a84ac5f3a7e1b1fbf11c379a5c339cb235831c Mon Sep 17 00:00:00 2001 From: DavidBar-On Date: Sat, 29 Mar 2025 09:23:32 +0300 Subject: [PATCH 3/3] Bug fix --- src/iperf_api.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/iperf_api.c b/src/iperf_api.c index 6d6ffbfb4..9f808ed6f 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1178,7 +1178,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {NULL, 0, NULL, 0} }; int flag; - int portno; + int portno, data_portno; int blksize; int server_flag, client_flag, rate_flag, duration_flag, rcv_timeout_flag, snd_timeout_flag; char *endptr; @@ -1204,17 +1204,16 @@ 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:mhX:", longopts, NULL)) != -1) { switch (flag) { case 'p': - + data_portno = -1; slash = strchr(optarg, '/'); if (slash) { // Get Data port *slash = '\0'; ++slash; - portno = atoi(slash); - if (portno < 1 || portno > 65535) { + data_portno = atoi(slash); + if (data_portno < 1 || data_portno > 65535) { i_errno = IEBADPORT; return -1; } - test->data_port = portno; client_flag = 1; } if (strlen(optarg) > 0) { // Get control (and data) port @@ -1225,6 +1224,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) } test->server_port = portno; } + test->data_port = (data_portno == -1) ? test->server_port : data_portno; break; case 'f': if (!optarg) {