--- embedaddon/iperf/src/iperf_server_api.c 2016/10/18 13:28:18 1.1.1.1 +++ embedaddon/iperf/src/iperf_server_api.c 2021/03/17 00:36:46 1.1.1.2 @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014, 2015, The Regents of the University of + * iperf, Copyright (c) 2014-2020 The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -40,11 +40,9 @@ #include #include #include -#include #ifdef HAVE_STDINT_H #include #endif -#include #include #include #include @@ -56,12 +54,17 @@ #include "iperf_tcp.h" #include "iperf_util.h" #include "timer.h" +#include "iperf_time.h" #include "net.h" #include "units.h" -#include "tcp_window_size.h" #include "iperf_util.h" #include "iperf_locale.h" +#if defined(HAVE_TCP_CONGESTION) +#if !defined(TCP_CA_NAME_MAX) +#define TCP_CA_NAME_MAX 16 +#endif /* TCP_CA_NAME_MAX */ +#endif /* HAVE_TCP_CONGESTION */ int iperf_server_listen(struct iperf_test *test) @@ -84,39 +87,13 @@ iperf_server_listen(struct iperf_test *test) } if (!test->json_output) { - iprintf(test, "-----------------------------------------------------------\n"); - iprintf(test, "Server listening on %d\n", test->server_port); + iperf_printf(test, "-----------------------------------------------------------\n"); + iperf_printf(test, "Server listening on %d\n", test->server_port); + iperf_printf(test, "-----------------------------------------------------------\n"); + if (test->forceflush) + iflush(test); } - // This needs to be changed to reflect if client has different window size - // make sure we got what we asked for - /* XXX: This needs to be moved to the stream listener - if ((x = get_tcp_windowsize(test->listener, SO_RCVBUF)) < 0) { - // Needs to set some sort of error number/message - perror("SO_RCVBUF"); - return -1; - } - */ - - // XXX: This code needs to be moved to after parameter exhange - /* - char ubuf[UNIT_LEN]; - int x; - - if (test->protocol->id == Ptcp) { - if (test->settings->socket_bufsize > 0) { - unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A'); - if (!test->json_output) - iprintf(test, report_window, ubuf); - } else { - if (!test->json_output) - iprintf(test, "%s", report_autotune); - } - } - */ - if (!test->json_output) - iprintf(test, "-----------------------------------------------------------\n"); - FD_ZERO(&test->read_set); FD_ZERO(&test->write_set); FD_SET(test->listener, &test->read_set); @@ -246,73 +223,28 @@ iperf_handle_message_server(struct iperf_test *test) return 0; } -/* XXX: This function is not used anymore */ -void -iperf_test_reset(struct iperf_test *test) +static void +server_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; struct iperf_stream *sp; - close(test->ctrl_sck); - + test->timer = NULL; + if (test->done) + return; + test->done = 1; /* Free streams */ while (!SLIST_EMPTY(&test->streams)) { sp = SLIST_FIRST(&test->streams); SLIST_REMOVE_HEAD(&test->streams, streams); + close(sp->socket); iperf_free_stream(sp); } - if (test->timer != NULL) { - tmr_cancel(test->timer); - test->timer = NULL; - } - 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; - } - test->done = 0; - - SLIST_INIT(&test->streams); - - test->role = 's'; - set_protocol(test, Ptcp); - test->omit = OMIT; - test->duration = DURATION; - test->diskfile_name = (char*) 0; - test->affinity = -1; - test->server_affinity = -1; - test->title = NULL; - test->congestion = NULL; - test->state = 0; - test->server_hostname = NULL; - - test->ctrl_sck = -1; - test->prot_listener = -1; - - test->bytes_sent = 0; - - test->reverse = 0; - test->sender = 0; - test->sender_has_retransmits = 0; - test->no_delay = 0; - - FD_ZERO(&test->read_set); - FD_ZERO(&test->write_set); - FD_SET(test->listener, &test->read_set); - test->max_fd = test->listener; - - test->num_streams = 1; - test->settings->socket_bufsize = 0; - test->settings->blksize = DEFAULT_TCP_BLKSIZE; - test->settings->rate = 0; - test->settings->mss = 0; - memset(test->cookie, 0, COOKIE_SIZE); + close(test->ctrl_sck); } static void -server_stats_timer_proc(TimerClientData client_data, struct timeval *nowP) +server_stats_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { struct iperf_test *test = client_data.p; @@ -323,7 +255,7 @@ server_stats_timer_proc(TimerClientData client_data, s } static void -server_reporter_timer_proc(TimerClientData client_data, struct timeval *nowP) +server_reporter_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { struct iperf_test *test = client_data.p; @@ -336,14 +268,27 @@ server_reporter_timer_proc(TimerClientData client_data static int create_server_timers(struct iperf_test * test) { - struct timeval now; + struct iperf_time now; TimerClientData cd; + int max_rtt = 4; /* seconds */ + int state_transitions = 10; /* number of state transitions in iperf3 */ + int grace_period = max_rtt * state_transitions; - if (gettimeofday(&now, NULL) < 0) { + if (iperf_time_now(&now) < 0) { i_errno = IEINITTEST; return -1; } cd.p = test; + test->timer = test->stats_timer = test->reporter_timer = NULL; + if (test->duration != 0 ) { + test->done = 0; + test->timer = tmr_create(&now, server_timer_proc, cd, (test->duration + test->omit + grace_period) * SEC_TO_US, 0); + if (test->timer == NULL) { + i_errno = IEINITTEST; + return -1; + } + } + test->stats_timer = test->reporter_timer = NULL; if (test->stats_interval != 0) { test->stats_timer = tmr_create(&now, server_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1); @@ -363,7 +308,7 @@ create_server_timers(struct iperf_test * test) } static void -server_omit_timer_proc(TimerClientData client_data, struct timeval *nowP) +server_omit_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { struct iperf_test *test = client_data.p; @@ -371,7 +316,7 @@ server_omit_timer_proc(TimerClientData client_data, st test->omitting = 0; iperf_reset_stats(test); if (test->verbose && !test->json_output && test->reporter_interval == 0) - iprintf(test, "%s", report_omit_done); + iperf_printf(test, "%s", report_omit_done); /* Reset the timers. */ if (test->stats_timer != NULL) @@ -383,14 +328,14 @@ server_omit_timer_proc(TimerClientData client_data, st static int create_server_omit_timer(struct iperf_test * test) { - struct timeval now; + struct iperf_time now; TimerClientData cd; if (test->omit == 0) { test->omit_timer = NULL; test->omitting = 0; } else { - if (gettimeofday(&now, NULL) < 0) { + if (iperf_time_now(&now) < 0) { i_errno = IEINITTEST; return -1; } @@ -409,9 +354,22 @@ create_server_omit_timer(struct iperf_test * test) static void cleanup_server(struct iperf_test *test) { + struct iperf_stream *sp; + + /* Close open streams */ + SLIST_FOREACH(sp, &test->streams, streams) { + FD_CLR(sp->socket, &test->read_set); + FD_CLR(sp->socket, &test->write_set); + close(sp->socket); + } + /* Close open test sockets */ - close(test->ctrl_sck); - close(test->listener); + if (test->ctrl_sck) { + close(test->ctrl_sck); + } + if (test->listener) { + close(test->listener); + } /* Cancel any remaining timers. */ if (test->stats_timer != NULL) { @@ -426,18 +384,36 @@ cleanup_server(struct iperf_test *test) tmr_cancel(test->omit_timer); test->omit_timer = NULL; } + if (test->congestion_used != NULL) { + free(test->congestion_used); + test->congestion_used = NULL; + } + if (test->timer != NULL) { + tmr_cancel(test->timer); + test->timer = NULL; + } } int iperf_run_server(struct iperf_test *test) { - int result, s, streams_accepted; + int result, s; + int send_streams_accepted, rec_streams_accepted; + int streams_to_send = 0, streams_to_rec = 0; +#if defined(HAVE_TCP_CONGESTION) + int saved_errno; +#endif /* HAVE_TCP_CONGESTION */ fd_set read_set, write_set; struct iperf_stream *sp; - struct timeval now; + struct iperf_time now; struct timeval* timeout; + int flag; + if (test->logfile) + if (iperf_open_logfile(test) < 0) + return -1; + if (test->affinity != -1) if (iperf_setaffinity(test, test->affinity) != 0) return -2; @@ -450,9 +426,9 @@ iperf_run_server(struct iperf_test *test) cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version)); cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info())); } else if (test->verbose) { - iprintf(test, "%s\n", version); - iprintf(test, "%s", ""); - iprintf(test, "%s\n", get_system_info()); + iperf_printf(test, "%s\n", version); + iperf_printf(test, "%s", ""); + iperf_printf(test, "%s\n", get_system_info()); iflush(test); } @@ -465,16 +441,25 @@ iperf_run_server(struct iperf_test *test) cpu_util(NULL); test->state = IPERF_START; - streams_accepted = 0; + send_streams_accepted = 0; + rec_streams_accepted = 0; while (test->state != IPERF_DONE) { + // Check if average transfer rate was exceeded (condition set in the callback routines) + if (test->bitrate_limit_exceeded) { + cleanup_server(test); + i_errno = IETOTALRATE; + return -1; + } + memcpy(&read_set, &test->read_set, sizeof(fd_set)); memcpy(&write_set, &test->write_set, sizeof(fd_set)); - (void) gettimeofday(&now, NULL); + iperf_time_now(&now); timeout = tmr_timeout(&now); result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout); + if (result < 0 && errno != EINTR) { cleanup_server(test); i_errno = IESELECT; @@ -488,6 +473,18 @@ iperf_run_server(struct iperf_test *test) return -1; } FD_CLR(test->listener, &read_set); + + // Set streams number + if (test->mode == BIDIRECTIONAL) { + streams_to_send = test->num_streams; + streams_to_rec = test->num_streams; + } else if (test->mode == RECEIVER) { + streams_to_rec = test->num_streams; + streams_to_send = 0; + } else { + streams_to_send = test->num_streams; + streams_to_rec = 0; + } } } if (FD_ISSET(test->ctrl_sck, &read_set)) { @@ -506,38 +503,97 @@ iperf_run_server(struct iperf_test *test) return -1; } - if (!is_closed(s)) { - sp = iperf_new_stream(test, s); - if (!sp) { - cleanup_server(test); - return -1; +#if defined(HAVE_TCP_CONGESTION) + if (test->protocol->id == Ptcp) { + if (test->congestion) { + if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { + /* + * ENOENT means we tried to set the + * congestion algorithm but the algorithm + * specified doesn't exist. This can happen + * if the client and server have different + * congestion algorithms available. In this + * case, print a warning, but otherwise + * continue. + */ + if (errno == ENOENT) { + warning("TCP congestion control algorithm not supported"); + } + else { + saved_errno = errno; + close(s); + cleanup_server(test); + errno = saved_errno; + i_errno = IESETCONGESTION; + return -1; + } + } } + { + socklen_t len = TCP_CA_NAME_MAX; + char ca[TCP_CA_NAME_MAX + 1]; + if (getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len) < 0) { + saved_errno = errno; + close(s); + cleanup_server(test); + errno = saved_errno; + i_errno = IESETCONGESTION; + return -1; + } + test->congestion_used = strdup(ca); + if (test->debug) { + printf("Congestion algorithm is %s\n", test->congestion_used); + } + } + } +#endif /* HAVE_TCP_CONGESTION */ - if (test->sender) - FD_SET(s, &test->write_set); - else - FD_SET(s, &test->read_set); - if (s > test->max_fd) test->max_fd = s; + if (!is_closed(s)) { - /* - * If the protocol isn't UDP, or even if it is but - * we're the receiver, set nonblocking sockets. - * We need this to allow a server receiver to - * maintain interactivity with the control channel. - */ - if (test->protocol->id != Pudp || - !test->sender) { - setnonblocking(s, 1); - } + if (rec_streams_accepted != streams_to_rec) { + flag = 0; + ++rec_streams_accepted; + } else if (send_streams_accepted != streams_to_send) { + flag = 1; + ++send_streams_accepted; + } - streams_accepted++; - if (test->on_new_stream) - test->on_new_stream(sp); + if (flag != -1) { + sp = iperf_new_stream(test, s, flag); + if (!sp) { + cleanup_server(test); + return -1; + } + + if (sp->sender) + FD_SET(s, &test->write_set); + else + FD_SET(s, &test->read_set); + + if (s > test->max_fd) test->max_fd = s; + + /* + * If the protocol isn't UDP, or even if it is but + * we're the receiver, set nonblocking sockets. + * We need this to allow a server receiver to + * maintain interactivity with the control channel. + */ + if (test->protocol->id != Pudp || + !sp->sender) { + setnonblocking(s, 1); + } + + if (test->on_new_stream) + test->on_new_stream(sp); + + flag = -1; + } } FD_CLR(test->prot_listener, &read_set); } - if (streams_accepted == test->num_streams) { + + if (rec_streams_accepted == streams_to_rec && send_streams_accepted == streams_to_send) { if (test->protocol->id != Ptcp) { FD_CLR(test->prot_listener, &test->read_set); close(test->prot_listener); @@ -545,6 +601,7 @@ iperf_run_server(struct iperf_test *test) if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { FD_CLR(test->listener, &test->read_set); close(test->listener); + test->listener = 0; if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { cleanup_server(test); i_errno = IELISTEN; @@ -556,6 +613,17 @@ iperf_run_server(struct iperf_test *test) } } test->prot_listener = -1; + + /* Ensure that total requested data rate is not above limit */ + iperf_size_t total_requested_rate = test->num_streams * test->settings->rate * (test->mode == BIDIRECTIONAL? 2 : 1); + if (test->settings->bitrate_limit > 0 && total_requested_rate > test->settings->bitrate_limit) { + iperf_err(test, "Client total requested throughput rate of %" PRIu64 " bps exceeded %" PRIu64 " bps limit", + total_requested_rate, test->settings->bitrate_limit); + cleanup_server(test); + i_errno = IETOTALRATE; + return -1; + } + if (iperf_set_send_state(test, TEST_START) != 0) { cleanup_server(test); return -1; @@ -572,7 +640,7 @@ iperf_run_server(struct iperf_test *test) cleanup_server(test); return -1; } - if (test->reverse) + if (test->mode != RECEIVER) if (iperf_create_send_timers(test) < 0) { cleanup_server(test); return -1; @@ -585,7 +653,16 @@ iperf_run_server(struct iperf_test *test) } if (test->state == TEST_RUNNING) { - if (test->reverse) { + if (test->mode == BIDIRECTIONAL) { + if (iperf_recv(test, &read_set) < 0) { + cleanup_server(test); + return -1; + } + if (iperf_send(test, &write_set) < 0) { + cleanup_server(test); + return -1; + } + } else if (test->mode == SENDER) { // Reverse mode. Server sends. if (iperf_send(test, &write_set) < 0) { cleanup_server(test); @@ -598,13 +675,13 @@ iperf_run_server(struct iperf_test *test) return -1; } } - } + } } if (result == 0 || (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0)) { /* Run the timers. */ - (void) gettimeofday(&now, NULL); + iperf_time_now(&now); tmr_run(&now); } }