diff --git a/src/iperf.h b/src/iperf.h index 7d14a3453..42e62aa03 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -302,6 +302,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 262adbc2e..4ce84ec79 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) { @@ -956,10 +968,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); } @@ -1177,12 +1189,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:hX:", 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) { @@ -1772,6 +1799,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 */ @@ -2288,6 +2320,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) @@ -2403,6 +2437,8 @@ get_parameters(struct iperf_test *test) test->settings->blocks = 0; if ((j_p = cJSON_GetObjectItem(j, "blockcount")) != NULL) test->settings->blocks = j_p->valueint; + if ((j_p = cJSON_GetObjectItem(j, "data_port")) != NULL) + test->data_port = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "MSS")) != NULL) test->settings->mss = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "nodelay")) != NULL) @@ -3025,6 +3061,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 2b71613e9..ed3ecdc2f 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -420,6 +420,7 @@ enum { IESNDTIMEOUT = 33, // Illegal message send timeout IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP IESERVERAUTHUSERS = 35, // Cannot access authorized users file + IEDATAPORT = 36, // 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 3388d376e..83f406150 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -204,7 +204,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 5c6e66dfd..ee2fe807d 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" @@ -311,7 +312,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 760116b4b..36e661429 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -447,7 +447,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; @@ -479,7 +479,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; } @@ -508,7 +508,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; }