diff options
-rw-r--r-- | src/iperf.h | 10 | ||||
-rw-r--r-- | src/iperf_api.c | 134 | ||||
-rw-r--r-- | src/iperf_api.h | 6 | ||||
-rw-r--r-- | src/iperf_error.c | 4 | ||||
-rw-r--r-- | src/iperf_locale.c | 3 | ||||
-rw-r--r-- | src/iperf_server_api.c | 30 |
6 files changed, 183 insertions, 4 deletions
diff --git a/src/iperf.h b/src/iperf.h index 6ce77f5..3e0edb6 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -135,7 +135,10 @@ struct iperf_settings int domain; /* AF_INET or AF_INET6 */ int socket_bufsize; /* window size for TCP */ int blksize; /* size of read/writes (-l) */ - uint64_t rate; /* target data rate for application pacing*/ + iperf_size_t rate; /* target data rate for application pacing*/ + iperf_size_t bitrate_limit; /* server's maximum allowed total data rate for all streams*/ + double bitrate_limit_interval; /* interval for avaraging total data rate */ + int bitrate_limit_stats_per_interval; /* calculated number of stats periods for averaging total data rate */ uint64_t fqrate; /* target data rate for FQ pacing*/ int pacing_timer; /* pacing timer in microseconds */ int burst; /* packets per burst */ @@ -328,6 +331,11 @@ struct iperf_test iperf_size_t bytes_received; iperf_size_t blocks_received; + iperf_size_t bitrate_limit_stats_count; /* Number of stats periods accumulated for server's total bitrate average */ + iperf_size_t *bitrate_limit_intervals_traffic_bytes; /* Pointer to a cyclic array that includes the last interval's bytes transferred */ + iperf_size_t bitrate_limit_last_interval_index; /* Index of the last interval traffic insrted into the cyclic array */ + int bitrate_limit_exceeded; /* Set by callback routine when average data rate exceeded the server's bitrate limit */ + char cookie[COOKIE_SIZE]; // struct iperf_stream *streams; /* pointer to list of struct stream */ SLIST_HEAD(slisthead, iperf_stream) streams; diff --git a/src/iperf_api.c b/src/iperf_api.c index 412c527..626167c 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -57,6 +57,7 @@ #include <sched.h> #include <setjmp.h> #include <stdarg.h> +#include <math.h> #if defined(HAVE_CPUSET_SETAFFINITY) #include <sys/param.h> @@ -164,6 +165,24 @@ iperf_get_test_rate(struct iperf_test *ipt) } uint64_t +iperf_get_test_bitrate_limit(struct iperf_test *ipt) +{ + return ipt->settings->bitrate_limit; +} + +double +iperf_get_test_bitrate_limit_interval(struct iperf_test *ipt) +{ + return ipt->settings->bitrate_limit_interval; +} + +int +iperf_get_test_bitrate_limit_stats_per_interval(struct iperf_test *ipt) +{ + return ipt->settings->bitrate_limit_stats_per_interval; +} + +uint64_t iperf_get_test_fqrate(struct iperf_test *ipt) { return ipt->settings->fqrate; @@ -413,6 +432,24 @@ iperf_set_test_rate(struct iperf_test *ipt, uint64_t rate) } void +iperf_set_test_bitrate_limit_maximum(struct iperf_test *ipt, uint64_t total_rate) +{ + ipt->settings->bitrate_limit = total_rate; +} + +void +iperf_set_test_bitrate_limit_interval(struct iperf_test *ipt, uint64_t bitrate_limit_interval) +{ + ipt->settings->bitrate_limit_interval = bitrate_limit_interval; +} + +void +iperf_set_test_bitrate_limit_stats_per_interval(struct iperf_test *ipt, uint64_t bitrate_limit_stats_per_interval) +{ + ipt->settings->bitrate_limit_stats_per_interval = bitrate_limit_stats_per_interval; +} + +void iperf_set_test_fqrate(struct iperf_test *ipt, uint64_t fqrate) { ipt->settings->fqrate = fqrate; @@ -816,6 +853,7 @@ 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}, {"time", required_argument, NULL, 't'}, {"bytes", required_argument, NULL, 'n'}, {"blockcount", required_argument, NULL, 'k'}, @@ -1001,6 +1039,21 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) rate_flag = 1; client_flag = 1; break; + case OPT_SERVER_BITRATE_LIMIT: + slash = strchr(optarg, '/'); + if (slash) { + *slash = '\0'; + ++slash; + test->settings->bitrate_limit_interval = atof(slash); + if (test->settings->bitrate_limit_interval != 0 && /* Using same Max/Min limits as for Stats Interval */ + (test->settings->bitrate_limit_interval < MIN_INTERVAL || test->settings->bitrate_limit_interval > MAX_INTERVAL) ) { + i_errno = IETOTALINTERVAL; + return -1; + } + } + test->settings->bitrate_limit = unit_atof_rate(optarg); + server_flag = 1; + break; case 't': test->duration = atoi(optarg); if (test->duration > MAX_TIME) { @@ -1374,6 +1427,13 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) return -1; } + /* Set Total-rate average interval to multiplicity of State interval */ + if (test->settings->bitrate_limit_interval != 0) { + test->settings->bitrate_limit_stats_per_interval = + (test->settings->bitrate_limit_interval <= test->stats_interval ? + 1 : round(test->settings->bitrate_limit_interval/test->stats_interval) ); + } + /* Show warning if JSON output is used with explicit report format */ if ((test->json_output) && (test->settings->unit_format != 'a')) { warning("Report format (-f) flag ignored with JSON output (-J)"); @@ -1436,6 +1496,45 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) } } +/* Verify that average traffic is not greater than the specifid limit */ +void +iperf_check_total_rate(struct iperf_test *test, iperf_size_t last_interval_bytes_transferred) +{ + double seconds; + uint64_t bits_per_second; + iperf_size_t total_bytes; + int i; + + if (test->done || test->settings->bitrate_limit == 0) // Continue only if check should be done + return; + + /* Add last inetrval's transffered bytes to the array */ + if (++test->bitrate_limit_last_interval_index >= test->settings->bitrate_limit_stats_per_interval) + test->bitrate_limit_last_interval_index = 0; + test->bitrate_limit_intervals_traffic_bytes[test->bitrate_limit_last_interval_index] = last_interval_bytes_transferred; + + /* Ensure that enough stats periods passed to allow averaging throughput */ + test->bitrate_limit_stats_count += 1; + if (test->bitrate_limit_stats_count < test->settings->bitrate_limit_stats_per_interval) + return; + + /* Calculating total bytes traffic to be averaged */ + for (total_bytes = 0, i = 0; i < test->settings->bitrate_limit_stats_per_interval; i++) { + total_bytes += test->bitrate_limit_intervals_traffic_bytes[i]; + } + + seconds = test->stats_interval * test->settings->bitrate_limit_stats_per_interval; + bits_per_second = total_bytes * 8 / seconds; + if (test->debug) { + iperf_printf(test,"Interval %ld - throughput %ld bps (limit %ld)\n", test->bitrate_limit_stats_count, bits_per_second, test->settings->bitrate_limit); + } + + if (bits_per_second > test->settings->bitrate_limit) { + iperf_err(test, "Total throughput of %ld bps exceeded %ld bps limit", bits_per_second, test->settings->bitrate_limit); + test->bitrate_limit_exceeded = 1; + } +} + int iperf_send(struct iperf_test *test, fd_set *write_setP) { @@ -1655,6 +1754,7 @@ iperf_exchange_parameters(struct iperf_test *test) } return -1; } + FD_SET(s, &test->read_set); test->max_fd = (s > test->max_fd) ? s : test->max_fd; test->prot_listener = s; @@ -2290,6 +2390,14 @@ iperf_new_test() } memset(test->settings, 0, sizeof(struct iperf_settings)); + test->bitrate_limit_intervals_traffic_bytes = (iperf_size_t *) malloc(sizeof(iperf_size_t) * MAX_INTERVAL); + if (!test->bitrate_limit_intervals_traffic_bytes) { + free(test); + i_errno = IENEWTEST; + return NULL; + } + memset(test->bitrate_limit_intervals_traffic_bytes, 0, sizeof(sizeof(iperf_size_t) * MAX_INTERVAL)); + /* By default all output goes to stdout */ test->outfile = stdout; @@ -2357,6 +2465,9 @@ iperf_defaults(struct iperf_test *testp) testp->settings->socket_bufsize = 0; /* use autotuning */ testp->settings->blksize = DEFAULT_TCP_BLKSIZE; testp->settings->rate = 0; + testp->settings->bitrate_limit = 0; + testp->settings->bitrate_limit_interval = 5; + testp->settings->bitrate_limit_stats_per_interval = 0; testp->settings->fqrate = 0; testp->settings->pacing_timer = 1000; testp->settings->burst = 0; @@ -2545,6 +2656,10 @@ iperf_free_test(struct iperf_test *test) } } + /* Free interval's traffic array for avrage rate calculations */ + if (test->bitrate_limit_intervals_traffic_bytes != NULL) + free(test->bitrate_limit_intervals_traffic_bytes); + /* XXX: Why are we setting these values to NULL? */ // test->streams = NULL; test->stats_callback = NULL; @@ -2557,6 +2672,7 @@ void iperf_reset_test(struct iperf_test *test) { struct iperf_stream *sp; + int i; /* Free streams */ while (!SLIST_EMPTY(&test->streams)) { @@ -2610,6 +2726,13 @@ iperf_reset_test(struct iperf_test *test) test->other_side_has_retransmits = 0; + test->bitrate_limit_stats_count = 0; + test->bitrate_limit_last_interval_index = 0; + test->bitrate_limit_exceeded = 0; + + for (i = 0; i < MAX_INTERVAL; i++) + test->bitrate_limit_intervals_traffic_bytes[i] = 0; + test->reverse = 0; test->bidirectional = 0; test->no_delay = 0; @@ -2713,12 +2836,16 @@ iperf_stats_callback(struct iperf_test *test) struct iperf_stream_result *rp = NULL; struct iperf_interval_results *irp, temp; struct iperf_time temp_time; + iperf_size_t total_interval_bytes_transferred = 0; temp.omitted = test->omitting; SLIST_FOREACH(sp, &test->streams, streams) { rp = sp->result; temp.bytes_transferred = sp->sender ? rp->bytes_sent_this_interval : rp->bytes_received_this_interval; - + + // Total bytes transferred this interval + total_interval_bytes_transferred += rp->bytes_sent_this_interval + rp->bytes_received_this_interval; + irp = TAILQ_LAST(&rp->interval_results, irlisthead); /* result->end_time contains timestamp of previous interval */ if ( irp != NULL ) /* not the 1st interval */ @@ -2777,6 +2904,11 @@ iperf_stats_callback(struct iperf_test *test) add_to_interval_list(rp, &temp); rp->bytes_sent_this_interval = rp->bytes_received_this_interval = 0; } + + /* Verify that total server's throughput is not above specified limit */ + if (test->role == 's') { + iperf_check_total_rate(test, total_interval_bytes_transferred); + } } /** diff --git a/src/iperf_api.h b/src/iperf_api.h index 4c12e9f..443f12f 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -45,6 +45,8 @@ struct iperf_interval_results; struct iperf_stream; struct iperf_time; +typedef uint64_t iperf_size_t; + /* default settings */ #define Ptcp SOCK_STREAM #define Pudp SOCK_DGRAM @@ -73,6 +75,7 @@ struct iperf_time; #define OPT_REPEATING_PAYLOAD 18 #define OPT_EXTRA_DATA 19 #define OPT_BIDIRECTIONAL 20 +#define OPT_SERVER_BITRATE_LIMIT 21 /* states */ #define TEST_START 1 @@ -301,6 +304,7 @@ int iperf_accept(struct iperf_test *); int iperf_handle_message_server(struct iperf_test *); int iperf_create_pidfile(struct iperf_test *); int iperf_delete_pidfile(struct iperf_test *); +void iperf_check_total_rate(struct iperf_test *, iperf_size_t); /* JSON output routines. */ int iperf_json_start(struct iperf_test *); @@ -348,6 +352,8 @@ enum { IEBADFORMAT = 24, // Bad format argument to -f IEREVERSEBIDIR = 25, // Iperf cannot be both reverse and bidirectional IEBADPORT = 26, // Bad port number + IETOTALRATE = 27, // Total required bandwidth is larger than server's limit + IETOTALINTERVAL = 28, // Invalid time interval for calculating average data rate /* 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 c6e5ee4..51445d5 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -385,7 +385,9 @@ iperf_strerror(int int_errno) case IEREVERSEBIDIR: snprintf(errstr, len, "cannot be both reverse and bidirectional"); break; - + case IETOTALRATE: + snprintf(errstr, len, "total required bandwidth is larger than server limit"); + break; } /* Append the result of strerror() or gai_strerror() if appropriate */ diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 5f39aae..8d6dd0b 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -117,6 +117,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " -D, --daemon run the server as a daemon\n" " -I, --pidfile file write PID file\n" " -1, --one-off handle one client connection then exit\n" + " --server-bitrate-limit #[KMG][/#] server's total bit rate limit (default 0 = no limit)\n" + " (optional slash and number of secs interval for averaging\n" + " total data rate. Default is 5 seconds)\n" #if defined(HAVE_SSL) " --rsa-private-key-path path to the RSA private key used to decrypt\n" " authentication credentials\n" diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 40d99bc..9b74790 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -354,6 +354,15 @@ 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 */ if (test->ctrl_sck) { close(test->ctrl_sck); @@ -437,12 +446,20 @@ iperf_run_server(struct iperf_test *test) 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)); 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; @@ -596,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 %ld bps exceeded %ld 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; @@ -647,7 +675,7 @@ iperf_run_server(struct iperf_test *test) return -1; } } - } + } } if (result == 0 || |