diff --git a/src/iperf_api.c b/src/iperf_api.c index dcf386c30..7cd0d6322 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1706,9 +1706,6 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) } else if (test->role == 'c' && (test->server_skew_threshold != 0)){ i_errno = IESERVERONLY; return -1; - } else if (test->role == 'c' && rcv_timeout_flag && test->mode == SENDER){ - i_errno = IERVRSONLYRCVTIMEOUT; - return -1; } else if (test->role == 's' && (server_rsa_private_key || test->server_authorized_users) && !(server_rsa_private_key && test->server_authorized_users)) { i_errno = IESETSERVERAUTH; @@ -2142,6 +2139,40 @@ iperf_create_send_timers(struct iperf_test * test) return 0; } +/* cancel send (pacing) timers */ +void +iperf_cancel_send_timers(struct iperf_test * test) +{ + struct iperf_stream *sp; + + SLIST_FOREACH(sp, &test->streams, streams) { + if (sp->send_timer != NULL) { + tmr_cancel(sp->send_timer); + sp->send_timer = NULL; + } + } +} + +/* cancel all periodic timers */ +void +iperf_cancel_periodic_timers(struct iperf_test * test) +{ + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Canceling all periodic timers\n"); + } + + iperf_cancel_send_timers(test); + + if (test->stats_timer != NULL) { + tmr_cancel(test->stats_timer); + test->stats_timer = NULL; + } + if (test->reporter_timer != NULL) { + tmr_cancel(test->reporter_timer); + test->reporter_timer = NULL; + } +} + #if defined(HAVE_SSL) int test_is_authorized(struct iperf_test *test){ if ( !(test->server_rsa_private_key && test->server_authorized_users)) { diff --git a/src/iperf_api.h b/src/iperf_api.h index 2b71613e9..ef810900d 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -333,6 +333,7 @@ void warning(const char *); int iperf_exchange_results(struct iperf_test *); int iperf_init_test(struct iperf_test *); int iperf_create_send_timers(struct iperf_test *); +void iperf_cancel_periodic_timers(struct iperf_test *); int iperf_parse_arguments(struct iperf_test *, int, char **); int iperf_open_logfile(struct iperf_test *); void iperf_close_logfile(struct iperf_test *); @@ -416,7 +417,6 @@ enum { IESKEWTHRESHOLD = 29, // Invalid value specified as skew threshold IEIDLETIMEOUT = 30, // Invalid value specified as idle state timeout IERCVTIMEOUT = 31, // Illegal message receive timeout - IERVRSONLYRCVTIMEOUT = 32, // Client receive timeout is valid only in reverse mode IESNDTIMEOUT = 33, // Illegal message send timeout IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP IESERVERAUTHUSERS = 35, // Cannot access authorized users file diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index c26bcc27a..4167c317d 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -358,6 +358,7 @@ iperf_handle_message_client(struct iperf_test *test) case TEST_RUNNING: break; case EXCHANGE_RESULTS: + iperf_cancel_periodic_timers(test); if (iperf_exchange_results(test) < 0) return -1; break; @@ -607,7 +608,7 @@ iperf_run_client(struct iperf_test * test) /* Begin calculating CPU utilization */ cpu_util(NULL); - if (test->mode != SENDER) + if (test->mode != SENDER || test->state != TEST_RUNNING) rcv_timeout_us = (test->settings->rcv_timeout.secs * SEC_TO_US) + test->settings->rcv_timeout.usecs; else rcv_timeout_us = 0; @@ -622,16 +623,16 @@ iperf_run_client(struct iperf_test * test) iperf_time_now(&now); timeout = tmr_timeout(&now); - // In reverse active mode client ensures data is received - if (test->state == TEST_RUNNING && rcv_timeout_us > 0) { + // In non-sending active mode or not during active test, client ensures data םor control messages are received + if (rcv_timeout_us > 0) { timeout_us = -1; if (timeout != NULL) { used_timeout.tv_sec = timeout->tv_sec; used_timeout.tv_usec = timeout->tv_usec; timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; } - /* Cap the maximum select timeout at 1 second */ - if (timeout_us > SEC_TO_US) { + /* Cap the maximum select timeout at 1 second during active test*/ + if (test->state == TEST_RUNNING && timeout_us > SEC_TO_US) { timeout_us = SEC_TO_US; } if (timeout_us < 0 || timeout_us > rcv_timeout_us) { @@ -641,6 +642,8 @@ iperf_run_client(struct iperf_test * test) timeout = &used_timeout; } + if (timeout) timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; + #if (defined(__vxworks)) || (defined(__VXWORKS__)) if (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0) { taskDelay (1); @@ -657,23 +660,32 @@ iperf_run_client(struct iperf_test * test) if (result < 0 && errno != EINTR) { i_errno = IESELECT; goto cleanup_and_fail; - } else if (result == 0 && test->state == TEST_RUNNING && rcv_timeout_us > 0) { + } else if (result == 0 && rcv_timeout_us > 0) { /* - * If nothing was received in non-reverse running state - * then probably something got stuck - either client, - * server or network, and test should be terminated./ + * If nothing was received then probably something got stuck - + * either client, server or network, and test should be terminated. */ iperf_time_now(&now); - if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) { - t_usecs = iperf_time_in_usecs(&diff_time); - if (t_usecs > rcv_timeout_us) { - /* Idle timeout if no new blocks received */ - if (test->blocks_received == last_receive_blocks) { - i_errno = IENOMSG; - goto cleanup_and_fail; + if (test->state == TEST_RUNNING && test->mode != SENDER) { // Check if data is received + if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) { + t_usecs = iperf_time_in_usecs(&diff_time); + if (t_usecs > rcv_timeout_us) { + /* Idle timeout if no new blocks received */ + if (test->blocks_received == last_receive_blocks) { + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Receiving data blocks timed out\n"); + } + i_errno = IENOMSG; + goto cleanup_and_fail; + } } } - + } else if (test->state != TEST_RUNNING && rcv_timeout_us == timeout_us) { // Check if control messages are received + if (test->debug >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Receiving control messages timed out\n"); + } + i_errno = IENOMSG; + goto cleanup_and_fail; } } diff --git a/src/iperf_error.c b/src/iperf_error.c index 3388d376e..c067c6ac4 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -377,10 +377,6 @@ iperf_strerror(int int_errno) case IEUDPFILETRANSFER: snprintf(errstr, len, "cannot transfer file using UDP"); break; - case IERVRSONLYRCVTIMEOUT: - snprintf(errstr, len, "client receive timeout is valid only in receiving mode"); - perr = 1; - break; case IEDAEMON: snprintf(errstr, len, "unable to become a daemon"); perr = 1; @@ -480,7 +476,7 @@ iperf_strerror(int int_errno) snprintf(errstr, len, "host device name (ip%%) is supported (and required) only for IPv6 link-local address"); break; case IENOMSG: - snprintf(errstr, len, "idle timeout for receiving data"); + snprintf(errstr, len, "idle timeout for receiving data or control messages"); break; case IESETDONTFRAGMENT: snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag"); diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 5c6e66dfd..4ac5145f4 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -123,7 +123,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " --timestamps<=format> emit a timestamp at the start of each output line\n" " (optional \"=\" and format string as per strftime(3))\n" - " --rcv-timeout # idle timeout for receiving data (default %d ms)\n" + " --rcv-timeout # idle timeout for receiving data or control messages (default %d ms)\n" #if defined(HAVE_TCP_USER_TIMEOUT) " --snd-timeout # timeout for unacknowledged TCP data\n" " (in ms, default is system settings)\n" diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 9727cdddb..2bbdb5ba7 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -272,6 +272,9 @@ iperf_handle_message_server(struct iperf_test *test) test->reporter_callback(test); if (iperf_set_send_state(test, EXCHANGE_RESULTS) != 0) return -1; + + iperf_cancel_periodic_timers(test); + if (iperf_exchange_results(test) < 0) return -1; if (iperf_set_send_state(test, DISPLAY_RESULTS) != 0) @@ -605,15 +608,16 @@ iperf_run_server(struct iperf_test *test) used_timeout.tv_usec = 0; timeout = &used_timeout; } - } else if (test->mode != SENDER) { // In non-reverse active mode server ensures data is received + } else if (test->mode != SENDER || test->state != TEST_RUNNING) { + // In non-reverse active mode or not during active test, server ensures data םor control messages are received timeout_us = -1; if (timeout != NULL) { used_timeout.tv_sec = timeout->tv_sec; used_timeout.tv_usec = timeout->tv_usec; timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; } - /* Cap the maximum select timeout at 1 second */ - if (timeout_us > SEC_TO_US) { + /* Cap the maximum select timeout at 1 second during active test */ + if (test->state == TEST_RUNNING && timeout_us > SEC_TO_US) { timeout_us = SEC_TO_US; } if (timeout_us < 0 || timeout_us > rcv_timeout_us) { @@ -660,10 +664,12 @@ iperf_run_server(struct iperf_test *test) } /* - * Running a test. If we're receiving, be sure we're making - * progress (sender hasn't died/crashed). + * Receiver when running a test or after test ended. + * Be sure we're making progress (sender hasn't died/crashed). */ - else if (test->mode != SENDER && t_usecs > rcv_timeout_us) { + else if ( t_usecs > rcv_timeout_us && + (test->mode != SENDER || test->state != TEST_RUNNING) ) + { /* Idle timeout if no new blocks received */ if (test->blocks_received == last_receive_blocks) { test->server_forced_no_msg_restarts_count += 1;