diff --git a/src/iperf.h b/src/iperf.h index 527e549ed..f169cc4bd 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -293,6 +293,7 @@ struct iperf_test int server_port; int omit; /* duration of omit period (-O flag) */ int duration; /* total duration of test (-t flag) */ + int server_duration; /* maximum possible duration of test as enforced by the server (--server-time flag) */ char *diskfile_name; /* -F option */ int affinity, server_affinity; /* -A option */ #if defined(HAVE_CPUSET_SETAFFINITY) diff --git a/src/iperf_api.c b/src/iperf_api.c index 4c73e8328..c868f9177 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -892,7 +892,7 @@ void iperf_on_test_start(struct iperf_test *test) { if (test->json_output) { - cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d interval: %f", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, test->stats_interval)); + cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d server_duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d interval: %f", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->server_duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, test->stats_interval)); } else { if (test->verbose) { if (test->settings->bytes) @@ -900,7 +900,7 @@ iperf_on_test_start(struct iperf_test *test) else if (test->settings->blocks) iperf_printf(test, test_start_blocks, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->settings->blocks, test->settings->tos); else - iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->duration, test->settings->tos); + iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, (test->server_duration > 0 && test->duration > test->server_duration) ? test->server_duration : test->duration, test->settings->tos); } } if (test->json_stream) { @@ -1080,7 +1080,8 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"udp", no_argument, NULL, 'u'}, {"bitrate", required_argument, NULL, 'b'}, {"bandwidth", required_argument, NULL, 'b'}, - {"server-bitrate-limit", required_argument, NULL, OPT_SERVER_BITRATE_LIMIT}, + {"server-bitrate-limit", required_argument, NULL, OPT_SERVER_BITRATE_LIMIT}, + {"server-time", required_argument, NULL, OPT_SERVER_TIME}, {"time", required_argument, NULL, 't'}, {"bytes", required_argument, NULL, 'n'}, {"blockcount", required_argument, NULL, 'k'}, @@ -1164,7 +1165,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) int rcv_timeout_in = 0; blksize = 0; - server_flag = client_flag = rate_flag = duration_flag = rcv_timeout_flag = snd_timeout_flag =0; + server_flag = client_flag = rate_flag = duration_flag = rcv_timeout_flag = snd_timeout_flag = 0; #if defined(HAVE_SSL) char *client_username = NULL, *client_rsa_public_key = NULL, *server_rsa_private_key = NULL; FILE *ptr_file; @@ -1312,6 +1313,14 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->settings->bitrate_limit = unit_atof_rate(optarg); server_flag = 1; break; + case OPT_SERVER_TIME: + test->server_duration = atoi(optarg); + if (test->server_duration > MAX_TIME || test->server_duration < 0) { + i_errno = IEDURATION; + return -1; + } + server_flag = 1; + break; case 't': test->duration = atoi(optarg); if (test->duration > MAX_TIME || test->duration < 0) { @@ -2929,6 +2938,7 @@ iperf_defaults(struct iperf_test *testp) testp->omit = OMIT; testp->duration = DURATION; + testp->server_duration = 0; testp->diskfile_name = (char*) 0; testp->affinity = -1; testp->server_affinity = -1; diff --git a/src/iperf_api.h b/src/iperf_api.h index 131314243..adae45cd0 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -101,6 +101,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t; #define OPT_JSON_STREAM 28 #define OPT_SND_TIMEOUT 29 #define OPT_USE_PKCS1_PADDING 30 +#define OPT_SERVER_TIME 31 /* states */ #define TEST_START 1 @@ -475,6 +476,8 @@ enum { IEPTHREADJOIN=152, // Unable to join thread (check perror) IEPTHREADATTRINIT=153, // Unable to initialize thread attribute (check perror) IEPTHREADATTRDESTROY=154, // Unable to destroy thread attribute (check perror) + IESENDSERVERDURATION=155, // Unable to send the server's maximum measurement duration + IERECVSERVERDURATION=156, // Unable to receive the server's maximum measurement duration /* Stream errors */ IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror) IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 7ad4c939b..2896e4589 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -203,14 +203,26 @@ create_client_timers(struct iperf_test * test) } cd.p = test; test->timer = test->stats_timer = test->reporter_timer = NULL; - if (test->duration != 0) { - test->done = 0; - test->timer = tmr_create(&now, test_timer_proc, cd, ( test->duration + test->omit ) * SEC_TO_US, 0); + + int duration = test->duration; + if (duration != 0) { + /** + * The duration of the measurement should only be overridden if it exceeds the duration set by the server or + * if no duration is set on the server. + * + */ + if (test->server_duration > 0 && test->server_duration < duration) { + duration = test->server_duration; + } + + test->done = 0; + test->timer = tmr_create(&now, test_timer_proc, cd, (duration + test->omit) * SEC_TO_US, 0); if (test->timer == NULL) { i_errno = IEINITTEST; return -1; - } + } } + if (test->stats_interval != 0) { test->stats_timer = tmr_create(&now, client_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1); if (test->stats_timer == NULL) { @@ -426,6 +438,18 @@ iperf_connect(struct iperf_test *test) return -1; } + /** + * If the server has a value set for the --server-time flag, the client will read it here and adjust its measurement time accordingly. + * If the value is 0, then the measurement will run for the duration that the client has set. + */ + char server_duration_str[15] = ""; + if (Nread(test->ctrl_sck, server_duration_str, sizeof(server_duration_str), Ptcp) != sizeof(server_duration_str)) + { + i_errno = IERECVSERVERDURATION; + return -1; + } + test->server_duration = atoi(server_duration_str); + FD_SET(test->ctrl_sck, &test->read_set); if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck; diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 9d94e0234..85b0bc628 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -149,6 +149,8 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " --time-skew-threshold time skew threshold (in seconds) between the server\n" " and client during the authentication process\n" " --use-pkcs1-padding use pkcs1 padding at your own risk\n" + " --server-time time in seconds to transmit for. selected when \n" + " value is less than client's duration.\n" #endif //HAVE_SSL "Client specific:\n" " -c, --client [%%] run in client mode, connecting to \n" diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 7d512081c..aa13d99f9 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -175,8 +175,23 @@ iperf_accept(struct iperf_test *test) i_errno = IERECVCOOKIE; goto error_handling; } - FD_SET(test->ctrl_sck, &test->read_set); - if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck; + + /** + * If the server has a value set for the --server-time flag, send it to the client + * so that it can adjust its measurement time accordingly. If the value was not set + * via the flag, then the client will see a value of 0, which means that the server + * does not have a maximum measurement time. + */ + char server_duration_str[15] = ""; + sprintf(server_duration_str, "%d", test->server_duration); + + if (Nwrite(test->ctrl_sck, server_duration_str, sizeof(server_duration_str), Ptcp) < 0) { + i_errno = IESENDSERVERDURATION; + goto error_handling; + } + + FD_SET(test->ctrl_sck, &test->read_set); + if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck; if (iperf_set_send_state(test, PARAM_EXCHANGE) != 0) goto error_handling; @@ -340,9 +355,20 @@ create_server_timers(struct iperf_test * test) } cd.p = test; test->timer = test->stats_timer = test->reporter_timer = NULL; - if (test->duration != 0 ) { + + int duration = test->duration; + if (duration != 0 ) { + /** + * The duration of the measurement should only be overridden if it exceeds the duration set by the server or + * if no duration is set on the server. + * + */ + if (test->server_duration > 0 && test->server_duration < duration) { + duration = test->server_duration; + } + test->done = 0; - test->timer = tmr_create(&now, server_timer_proc, cd, (test->duration + test->omit + grace_period) * SEC_TO_US, 0); + test->timer = tmr_create(&now, server_timer_proc, cd, (duration + test->omit + grace_period) * SEC_TO_US, 0); if (test->timer == NULL) { i_errno = IEINITTEST; return -1;