diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/iperf.h | 18 | ||||
-rw-r--r-- | src/iperf3.1 | 33 | ||||
-rw-r--r-- | src/iperf_api.c | 249 | ||||
-rw-r--r-- | src/iperf_api.h | 35 | ||||
-rw-r--r-- | src/iperf_auth.c | 4 | ||||
-rw-r--r-- | src/iperf_client_api.c | 27 | ||||
-rw-r--r-- | src/iperf_config.h.in | 2 | ||||
-rw-r--r-- | src/iperf_error.c | 44 | ||||
-rw-r--r-- | src/iperf_locale.c | 10 | ||||
-rw-r--r-- | src/iperf_sctp.c | 28 | ||||
-rw-r--r-- | src/iperf_server_api.c | 32 | ||||
-rw-r--r-- | src/iperf_udp.c | 8 | ||||
-rw-r--r-- | src/iperf_util.c | 8 | ||||
-rw-r--r-- | src/iperf_util.h | 2 | ||||
-rw-r--r-- | src/net.c | 4 | ||||
-rw-r--r-- | src/net.h | 4 |
16 files changed, 421 insertions, 87 deletions
diff --git a/src/iperf.h b/src/iperf.h index 6ce77f5..5b1da46 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2019, 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. @@ -68,7 +68,9 @@ #include <openssl/evp.h> #endif // HAVE_SSL +#if !defined(__IPERF_API_H) typedef uint64_t iperf_size_t; +#endif // __IPERF_API_H struct iperf_interval_results { @@ -135,7 +137,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 */ @@ -298,6 +303,8 @@ struct iperf_test int forceflush; /* --forceflush - flushing output at every interval */ int multisend; int repeating_payload; /* --repeating-payload */ + int timestamps; /* --timestamps */ + char *timestamp_format; char *json_output_string; /* rendered JSON output if json_output is set */ /* Select related parameters */ @@ -328,6 +335,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; @@ -385,6 +397,8 @@ struct iperf_test #define MAX_MSS (9 * 1024) #define MAX_STREAMS 128 +#define TIMESTAMP_FORMAT "%c " + extern int gerror; /* error value from getaddrinfo(3), for use in internal error handling */ #endif /* !__IPERF_H */ diff --git a/src/iperf3.1 b/src/iperf3.1 index 93fe7b9..97d66ed 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -1,4 +1,4 @@ -.TH IPERF3 1 "June 2019" ESnet "User Manuals" +.TH IPERF3 1 "July 2020" ESnet "User Manuals" .SH NAME iperf3 \- perform network throughput tests .SH SYNOPSIS @@ -153,6 +153,14 @@ send output to a log file. force flushing output at every interval. Used to avoid buffering when sending output to pipe. .TP +.BR --timestamps " [\fIformat\fR]" +prepend a timestamp at the start of each output line. +By default, timestamps have the format emitted by +.BR ctime ( 1 ). +Optionally, a format specification can be passed to customize the +timestamps, see +.BR strftime ( 3 ). +.TP .BR -d ", " --debug " " emit debugging output. Primarily (perhaps exclusively) of use to developers. @@ -177,6 +185,15 @@ write a file with the process ID, most useful when running as a daemon. .BR -1 ", " --one-off handle one client connection, then exit. .TP +.BR --server-bitrate-limit " \fIn\fR[KMGT]" +set a limit on the server side, which will cause a test to abort if +the client specifies a test of more than \fIn\fR bits per second, or +if the average data sent or received by the client (including all data +streams) is greater than \fIn\fR bits per second. The default limit +is zero, which implies no limit. The interval over which to average +the data rate is 5 seconds by default, but can be specified by adding +a '/' and a number to the bitrate specifier. +.TP .BR --rsa-private-key-path " \fIfile\fR" path to the RSA private key (not password-protected) used to decrypt authentication credentials from the client (if built with OpenSSL @@ -209,7 +226,7 @@ connection establishment. Providing a shorter value may speed up detection of a down iperf3 server. .TP -.BR -b ", " --bitrate " \fIn\fR[KM]" +.BR -b ", " --bitrate " \fIn\fR[KMGT]" set target bitrate to \fIn\fR bits/sec (default 1 Mbit/sec for UDP, unlimited for TCP/SCTP). If there are multiple streams (\-P flag), the throughput limit is applied @@ -226,7 +243,7 @@ Compare with the \--fq-rate flag. This option replaces the \--bandwidth flag, which is now deprecated but (at least for now) still accepted. .TP -.BR --pacing-timer " \fIn\fR[KMG]" +.BR --pacing-timer " \fIn\fR[KMGT]" set pacing timer interval in microseconds (default 1000 microseconds, or 1 ms). This controls iperf3's internal pacing timer for the \-b/\--bitrate @@ -236,7 +253,7 @@ Smaller values of the pacing timer parameter smooth out the traffic emitted by iperf3, but potentially at the cost of performance due to more frequent timer processing. .TP -.BR --fq-rate " \fIn\fR[KM]" +.BR --fq-rate " \fIn\fR[KMGT]" Set a rate to be used with fair-queueing based socket-level pacing, in bits per second. This pacing (if specified) will be in addition to any pacing due to @@ -253,13 +270,13 @@ It is equivalent to specifying --fq-rate=0. .BR -t ", " --time " \fIn\fR" time in seconds to transmit for (default 10 secs) .TP -.BR -n ", " --bytes " \fIn\fR[KM]" +.BR -n ", " --bytes " \fIn\fR[KMGT]" number of bytes to transmit (instead of \-t) .TP -.BR -k ", " --blockcount " \fIn\fR[KM]" +.BR -k ", " --blockcount " \fIn\fR[KMGT]" number of blocks (packets) to transmit (instead of \-t or \-n) .TP -.BR -l ", " --length " \fIn\fR[KM]" +.BR -l ", " --length " \fIn\fR[KMGT]" length of buffer to read or write. For TCP tests, the default value is 128KB. In the case of UDP, iperf3 tries to dynamically determine a reasonable @@ -282,7 +299,7 @@ client test in both directions (normal and reverse), with both the client and server sending and receiving data simultaneously .TP -.BR -w ", " --window " \fIn\fR[KM]" +.BR -w ", " --window " \fIn\fR[KMGT]" window size / socket buffer size (this gets sent to the server and used on that side too) .TP .BR -M ", " --set-mss " \fIn\fR" diff --git a/src/iperf_api.c b/src/iperf_api.c index ed643b7..88fed30 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> @@ -76,9 +77,9 @@ #include "iperf_api.h" #include "iperf_udp.h" #include "iperf_tcp.h" -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) #include "iperf_sctp.h" -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ #include "timer.h" #include "cjson.h" @@ -119,7 +120,7 @@ usage_long(FILE *f) } -void warning(char *str) +void warning(const char *str) { fprintf(stderr, "warning: %s\n", str); } @@ -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; @@ -242,6 +261,18 @@ iperf_get_test_num_streams(struct iperf_test *ipt) } int +iperf_get_test_timestamps(struct iperf_test *ipt) +{ + return ipt->timestamps; +} + +const char * +iperf_get_test_timestamp_format(struct iperf_test *ipt) +{ + return ipt->timestamp_format; +} + +int iperf_get_test_repeating_payload(struct iperf_test *ipt) { return ipt->repeating_payload; @@ -401,7 +432,7 @@ iperf_set_test_blksize(struct iperf_test *ipt, int blksize) } void -iperf_set_test_logfile(struct iperf_test *ipt, char *logfile) +iperf_set_test_logfile(struct iperf_test *ipt, const char *logfile) { ipt->logfile = strdup(logfile); } @@ -413,6 +444,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; @@ -466,6 +515,18 @@ iperf_set_test_repeating_payload(struct iperf_test *ipt, int repeating_payload) ipt->repeating_payload = repeating_payload; } +void +iperf_set_test_timestamps(struct iperf_test *ipt, int timestamps) +{ + ipt->timestamps = timestamps; +} + +void +iperf_set_test_timestamp_format(struct iperf_test *ipt, const char *tf) +{ + ipt->timestamp_format = strdup(tf); +} + static void check_sender_has_retransmits(struct iperf_test *ipt) { @@ -496,13 +557,13 @@ iperf_set_test_role(struct iperf_test *ipt, char role) } void -iperf_set_test_server_hostname(struct iperf_test *ipt, char *server_hostname) +iperf_set_test_server_hostname(struct iperf_test *ipt, const char *server_hostname) { ipt->server_hostname = strdup(server_hostname); } void -iperf_set_test_template(struct iperf_test *ipt, char *tmp_template) +iperf_set_test_template(struct iperf_test *ipt, const char *tmp_template) { ipt->tmp_template = strdup(tmp_template); } @@ -557,38 +618,38 @@ iperf_set_test_unit_format(struct iperf_test *ipt, char unit_format) #if defined(HAVE_SSL) void -iperf_set_test_client_username(struct iperf_test *ipt, char *client_username) +iperf_set_test_client_username(struct iperf_test *ipt, const char *client_username) { ipt->settings->client_username = strdup(client_username); } void -iperf_set_test_client_password(struct iperf_test *ipt, char *client_password) +iperf_set_test_client_password(struct iperf_test *ipt, const char *client_password) { ipt->settings->client_password = strdup(client_password); } void -iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64) +iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, const char *client_rsa_pubkey_base64) { ipt->settings->client_rsa_pubkey = load_pubkey_from_base64(client_rsa_pubkey_base64); } void -iperf_set_test_server_authorized_users(struct iperf_test *ipt, char *server_authorized_users) +iperf_set_test_server_authorized_users(struct iperf_test *ipt, const char *server_authorized_users) { ipt->server_authorized_users = strdup(server_authorized_users); } void -iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, char *server_rsa_privkey_base64) +iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, const char *server_rsa_privkey_base64) { ipt->server_rsa_private_key = load_privkey_from_base64(server_rsa_privkey_base64); } #endif // HAVE_SSL void -iperf_set_test_bind_address(struct iperf_test *ipt, char *bnd_address) +iperf_set_test_bind_address(struct iperf_test *ipt, const char *bnd_address) { ipt->bind_address = strdup(bnd_address); } @@ -612,7 +673,7 @@ iperf_set_test_tos(struct iperf_test *ipt, int tos) } void -iperf_set_test_extra_data(struct iperf_test *ipt, char *dat) +iperf_set_test_extra_data(struct iperf_test *ipt, const char *dat) { ipt->extra_data = strdup(dat); } @@ -816,6 +877,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'}, @@ -840,6 +902,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"omit", required_argument, NULL, 'O'}, {"file", required_argument, NULL, 'F'}, {"repeating-payload", no_argument, NULL, OPT_REPEATING_PAYLOAD}, + {"timestamps", optional_argument, NULL, OPT_TIMESTAMPS}, #if defined(HAVE_CPU_AFFINITY) {"affinity", required_argument, NULL, 'A'}, #endif /* HAVE_CPU_AFFINITY */ @@ -848,7 +911,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"congestion", required_argument, NULL, 'C'}, {"linux-congestion", required_argument, NULL, 'C'}, #endif /* HAVE_TCP_CONGESTION */ -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) {"sctp", no_argument, NULL, OPT_SCTP}, {"nstreams", required_argument, NULL, OPT_NUMSTREAMS}, {"xbind", required_argument, NULL, 'X'}, @@ -968,14 +1031,14 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) client_flag = 1; break; case OPT_SCTP: -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) set_protocol(test, Psctp); client_flag = 1; break; -#else /* HAVE_SCTP */ +#else /* HAVE_SCTP_H */ i_errno = IEUNIMP; return -1; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ case OPT_NUMSTREAMS: #if defined(linux) || defined(__FreeBSD__) @@ -1001,6 +1064,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) { @@ -1149,6 +1227,15 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->repeating_payload = 1; client_flag = 1; break; + case OPT_TIMESTAMPS: + iperf_set_test_timestamps(test, 1); + if (optarg) { + iperf_set_test_timestamp_format(test, optarg); + } + else { + iperf_set_test_timestamp_format(test, TIMESTAMP_FORMAT); + } + break; case 'O': test->omit = atoi(optarg); if (test->omit < 0 || test->omit > 60) { @@ -1292,6 +1379,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) if ((client_password = getenv("IPERF3_PASSWORD")) != NULL) client_password = strdup(client_password); else if (iperf_getpass(&client_password, &s, stdin) < 0){ + i_errno = IESETCLIENTAUTH; return -1; } if (test_load_pubkey_from_file(client_rsa_public_key) < 0){ @@ -1374,6 +1462,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 +1531,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 %" PRIu64 " - throughput %" PRIu64 " bps (limit %" PRIu64 ")\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 %" PRIu64 " bps exceeded %" PRIu64 " 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) { @@ -1480,7 +1614,8 @@ iperf_send(struct iperf_test *test, fd_set *write_setP) if (test->settings->burst != 0) { iperf_time_now(&now); SLIST_FOREACH(sp, &test->streams, streams) - iperf_check_throttle(sp, &now); + if (sp->sender) + iperf_check_throttle(sp, &now); } if (write_setP != NULL) SLIST_FOREACH(sp, &test->streams, streams) @@ -1562,7 +1697,7 @@ iperf_create_send_timers(struct iperf_test * test) } SLIST_FOREACH(sp, &test->streams, streams) { sp->green_light = 1; - if (test->settings->rate != 0) { + if (test->settings->rate != 0 && sp->sender) { cd.p = sp; sp->send_timer = tmr_create(NULL, send_timer_proc, cd, test->settings->pacing_timer, 1); if (sp->send_timer == NULL) { @@ -1654,6 +1789,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; @@ -2289,6 +2425,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; @@ -2322,9 +2466,9 @@ int iperf_defaults(struct iperf_test *testp) { struct protocol *tcp, *udp; -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) struct protocol *sctp; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ testp->omit = OMIT; testp->duration = DURATION; @@ -2356,6 +2500,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; @@ -2403,7 +2550,7 @@ iperf_defaults(struct iperf_test *testp) set_protocol(testp, Ptcp); -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) sctp = protocol_new(); if (!sctp) { protocol_free(tcp); @@ -2421,7 +2568,7 @@ iperf_defaults(struct iperf_test *testp) sctp->init = iperf_sctp_init; SLIST_INSERT_AFTER(udp, sctp, protocols); -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ testp->on_new_stream = iperf_on_new_stream; testp->on_test_start = iperf_on_test_start; @@ -2497,6 +2644,8 @@ iperf_free_test(struct iperf_test *test) free(test->congestion_used); if (test->remote_congestion_used) free(test->remote_congestion_used); + if (test->timestamp_format) + free(test->timestamp_format); if (test->omit_timer != NULL) tmr_cancel(test->omit_timer); if (test->timer != NULL) @@ -2513,6 +2662,15 @@ iperf_free_test(struct iperf_test *test) free(prot); } + if (test->logfile) { + free(test->logfile); + test->logfile = NULL; + if (test->outfile) { + fclose(test->outfile); + test->outfile = NULL; + } + } + if (test->server_output_text) { free(test->server_output_text); test->server_output_text = NULL; @@ -2544,6 +2702,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; @@ -2556,6 +2718,7 @@ void iperf_reset_test(struct iperf_test *test) { struct iperf_stream *sp; + int i; /* Free streams */ while (!SLIST_EMPTY(&test->streams)) { @@ -2609,6 +2772,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; @@ -2712,12 +2882,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 */ @@ -2776,6 +2950,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); + } } /** @@ -4135,11 +4314,24 @@ iperf_clearaffinity(struct iperf_test *test) #endif /* neither HAVE_SCHED_SETAFFINITY nor HAVE_CPUSET_SETAFFINITY nor HAVE_SETPROCESSAFFINITYMASK */ } +char iperf_timestr[100]; + int iperf_printf(struct iperf_test *test, const char* format, ...) { va_list argp; int r = -1; + time_t now; + struct tm *ltm = NULL; + char *ct = NULL; + + /* Timestamp if requested */ + if (iperf_get_test_timestamps(test)) { + time(&now); + ltm = localtime(&now); + strftime(iperf_timestr, sizeof(iperf_timestr), iperf_get_test_timestamp_format(test), ltm); + ct = iperf_timestr; + } /* * There are roughly two use cases here. If we're the client, @@ -4154,6 +4346,9 @@ iperf_printf(struct iperf_test *test, const char* format, ...) * to be buffered up anyway. */ if (test->role == 'c') { + if (ct) { + fprintf(test->outfile, "%s", ct); + } if (test->title) fprintf(test->outfile, "%s: ", test->title); va_start(argp, format); @@ -4162,8 +4357,12 @@ iperf_printf(struct iperf_test *test, const char* format, ...) } else if (test->role == 's') { char linebuffer[1024]; + int i = 0; + if (ct) { + i = sprintf(linebuffer, "%s", ct); + } va_start(argp, format); - r = vsnprintf(linebuffer, sizeof(linebuffer), format, argp); + r = vsnprintf(linebuffer + i, sizeof(linebuffer), format, argp); va_end(argp); fprintf(test->outfile, "%s", linebuffer); diff --git a/src/iperf_api.h b/src/iperf_api.h index 3770b37..a51b773 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -45,6 +45,10 @@ struct iperf_interval_results; struct iperf_stream; struct iperf_time; +#if !defined(__IPERF_H) +typedef uint64_t iperf_size_t; +#endif // __IPERF_H + /* default settings */ #define Ptcp SOCK_STREAM #define Pudp SOCK_DGRAM @@ -73,6 +77,8 @@ struct iperf_time; #define OPT_REPEATING_PAYLOAD 18 #define OPT_EXTRA_DATA 19 #define OPT_BIDIRECTIONAL 20 +#define OPT_SERVER_BITRATE_LIMIT 21 +#define OPT_TIMESTAMPS 22 /* states */ #define TEST_START 1 @@ -113,6 +119,8 @@ double iperf_get_test_reporter_interval( struct iperf_test* ipt ); double iperf_get_test_stats_interval( struct iperf_test* ipt ); int iperf_get_test_num_streams( struct iperf_test* ipt ); int iperf_get_test_repeating_payload( struct iperf_test* ipt ); +int iperf_get_test_timestamps( struct iperf_test* ipt ); +const char* iperf_get_test_timestamp_format( struct iperf_test* ipt ); int iperf_get_test_server_port( struct iperf_test* ipt ); char* iperf_get_test_server_hostname( struct iperf_test* ipt ); char* iperf_get_test_template( struct iperf_test* ipt ); @@ -139,7 +147,7 @@ void iperf_set_test_reporter_interval( struct iperf_test* ipt, double reporter_i void iperf_set_test_stats_interval( struct iperf_test* ipt, double stats_interval ); void iperf_set_test_state( struct iperf_test* ipt, signed char state ); void iperf_set_test_blksize( struct iperf_test* ipt, int blksize ); -void iperf_set_test_logfile( struct iperf_test* ipt, char *logfile ); +void iperf_set_test_logfile( struct iperf_test* ipt, const char *logfile ); void iperf_set_test_rate( struct iperf_test* ipt, uint64_t rate ); void iperf_set_test_pacing_timer( struct iperf_test* ipt, int pacing_timer ); void iperf_set_test_bytes( struct iperf_test* ipt, uint64_t bytes ); @@ -149,28 +157,30 @@ void iperf_set_test_server_port( struct iperf_test* ipt, int server_port ); void iperf_set_test_socket_bufsize( struct iperf_test* ipt, int socket_bufsize ); void iperf_set_test_num_streams( struct iperf_test* ipt, int num_streams ); void iperf_set_test_repeating_payload( struct iperf_test* ipt, int repeating_payload ); +void iperf_set_test_timestamps( struct iperf_test* ipt, int timestamps ); +void iperf_set_test_timestamp_format( struct iperf_test*, const char *tf ); void iperf_set_test_role( struct iperf_test* ipt, char role ); -void iperf_set_test_server_hostname( struct iperf_test* ipt, char* server_hostname ); -void iperf_set_test_template( struct iperf_test *ipt, char *tmp_template ); +void iperf_set_test_server_hostname( struct iperf_test* ipt, const char* server_hostname ); +void iperf_set_test_template( struct iperf_test *ipt, const char *tmp_template ); void iperf_set_test_reverse( struct iperf_test* ipt, int reverse ); void iperf_set_test_json_output( struct iperf_test* ipt, int json_output ); int iperf_has_zerocopy( void ); void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy ); void iperf_set_test_get_server_output( struct iperf_test* ipt, int get_server_output ); -void iperf_set_test_bind_address( struct iperf_test* ipt, char *bind_address ); +void iperf_set_test_bind_address( struct iperf_test* ipt, const char *bind_address ); void iperf_set_test_udp_counters_64bit( struct iperf_test* ipt, int udp_counters_64bit ); void iperf_set_test_one_off( struct iperf_test* ipt, int one_off ); void iperf_set_test_tos( struct iperf_test* ipt, int tos ); -void iperf_set_test_extra_data( struct iperf_test* ipt, char *dat ); +void iperf_set_test_extra_data( struct iperf_test* ipt, const char *dat ); void iperf_set_test_bidirectional( struct iperf_test* ipt, int bidirectional); void iperf_set_test_no_delay( struct iperf_test* ipt, int no_delay); #if defined(HAVE_SSL) -void iperf_set_test_client_username(struct iperf_test *ipt, char *client_username); -void iperf_set_test_client_password(struct iperf_test *ipt, char *client_password); -void iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64); -void iperf_set_test_server_authorized_users(struct iperf_test *ipt, char *server_authorized_users); -void iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, char *server_rsa_privkey_base64); +void iperf_set_test_client_username(struct iperf_test *ipt, const char *client_username); +void iperf_set_test_client_password(struct iperf_test *ipt, const char *client_password); +void iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, const char *client_rsa_pubkey_base64); +void iperf_set_test_server_authorized_users(struct iperf_test *ipt, const char *server_authorized_users); +void iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, const char *server_rsa_privkey_base64); #endif // HAVE_SSL void iperf_set_test_connect_timeout(struct iperf_test *ipt, int ct); @@ -268,7 +278,7 @@ void iperf_catch_sigend(void (*handler)(int)); void iperf_got_sigend(struct iperf_test *test) __attribute__ ((noreturn)); void usage(void); void usage_long(FILE * f); -void warning(char *); +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 *); @@ -301,6 +311,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 +359,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_auth.c b/src/iperf_auth.c index 46211e0..eb4610f 100644 --- a/src/iperf_auth.c +++ b/src/iperf_auth.c @@ -174,6 +174,7 @@ EVP_PKEY *load_pubkey_from_base64(const char *buffer) { BIO* bio = BIO_new(BIO_s_mem()); BIO_write(bio, key, key_len); + free(key); EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); BIO_free(bio); return (pkey); @@ -199,6 +200,7 @@ EVP_PKEY *load_privkey_from_base64(const char *buffer) { BIO* bio = BIO_new(BIO_s_mem()); BIO_write(bio, key, key_len); + free(key); EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); BIO_free(bio); return (pkey); @@ -304,7 +306,7 @@ int encode_auth_setting(const char *username, const char *password, EVP_PKEY *pu return (0); //success } -int decode_auth_setting(int enable_debug, char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){ +int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){ unsigned char *encrypted_b64 = NULL; size_t encrypted_len_b64; Base64Decode(authtoken, &encrypted_b64, &encrypted_len_b64); diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 20ea6fd..d0edf7d 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2018, 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. @@ -478,7 +478,7 @@ iperf_run_client(struct iperf_test * test) /* Start the client and connect to the server */ if (iperf_connect(test) < 0) - return -1; + goto cleanup_and_fail; /* Begin calculating CPU utilization */ cpu_util(NULL); @@ -492,12 +492,12 @@ iperf_run_client(struct iperf_test * test) result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout); if (result < 0 && errno != EINTR) { i_errno = IESELECT; - return -1; + goto cleanup_and_fail; } if (result > 0) { if (FD_ISSET(test->ctrl_sck, &read_set)) { if (iperf_handle_message_client(test) < 0) { - return -1; + goto cleanup_and_fail; } FD_CLR(test->ctrl_sck, &read_set); } @@ -521,17 +521,17 @@ iperf_run_client(struct iperf_test * test) if (test->mode == BIDIRECTIONAL) { if (iperf_send(test, &write_set) < 0) - return -1; + goto cleanup_and_fail; if (iperf_recv(test, &read_set) < 0) - return -1; + goto cleanup_and_fail; } else if (test->mode == SENDER) { // Regular mode. Client sends. if (iperf_send(test, &write_set) < 0) - return -1; + goto cleanup_and_fail; } else { // Reverse mode. Client receives. if (iperf_recv(test, &read_set) < 0) - return -1; + goto cleanup_and_fail; } @@ -557,7 +557,7 @@ iperf_run_client(struct iperf_test * test) cpu_util(test->cpu_util); test->stats_callback(test); if (iperf_set_send_state(test, TEST_END) != 0) - return -1; + goto cleanup_and_fail; } } // If we're in reverse mode, continue draining the data @@ -567,7 +567,7 @@ iperf_run_client(struct iperf_test * test) // from the client side. else if (test->mode == RECEIVER && test->state == TEST_END) { if (iperf_recv(test, &read_set) < 0) - return -1; + goto cleanup_and_fail; } } @@ -582,4 +582,11 @@ iperf_run_client(struct iperf_test * test) iflush(test); return 0; + + cleanup_and_fail: + iperf_client_end(test); + if (test->json_output) + iperf_json_finish(test); + iflush(test); + return -1; } diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in index e543cbd..1a7cbec 100644 --- a/src/iperf_config.h.in +++ b/src/iperf_config.h.in @@ -40,7 +40,7 @@ #undef HAVE_SCHED_SETAFFINITY /* Have SCTP support. */ -#undef HAVE_SCTP +#undef HAVE_SCTP_H /* Define to 1 if you have the `sendfile' function. */ #undef HAVE_SENDFILE diff --git a/src/iperf_error.c b/src/iperf_error.c index c6e5ee4..cfe4cbd 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -35,12 +35,25 @@ int gerror; +char iperf_timestrerr[100]; + /* Do a printf to stderr. */ void iperf_err(struct iperf_test *test, const char *format, ...) { va_list argp; char str[1000]; + time_t now; + struct tm *ltm = NULL; + char *ct = NULL; + + /* Timestamp if requested */ + if (test != NULL && test->timestamps) { + time(&now); + ltm = localtime(&now); + strftime(iperf_timestrerr, sizeof(iperf_timestrerr), test->timestamp_format, ltm); + ct = iperf_timestrerr; + } va_start(argp, format); vsnprintf(str, sizeof(str), format, argp); @@ -48,9 +61,15 @@ iperf_err(struct iperf_test *test, const char *format, ...) cJSON_AddStringToObject(test->json_top, "error", str); else if (test && test->outfile && test->outfile != stdout) { + if (ct) { + fprintf(test->outfile, "%s", ct); + } fprintf(test->outfile, "iperf3: %s\n", str); } else { + if (ct) { + fprintf(stderr, "%s", ct); + } fprintf(stderr, "iperf3: %s\n", str); } va_end(argp); @@ -62,6 +81,17 @@ iperf_errexit(struct iperf_test *test, const char *format, ...) { va_list argp; char str[1000]; + time_t now; + struct tm *ltm = NULL; + char *ct = NULL; + + /* Timestamp if requested */ + if (test != NULL && test->timestamps) { + time(&now); + ltm = localtime(&now); + strftime(iperf_timestrerr, sizeof(iperf_timestrerr), "%c ", ltm); + ct = iperf_timestrerr; + } va_start(argp, format); vsnprintf(str, sizeof(str), format, argp); @@ -70,9 +100,15 @@ iperf_errexit(struct iperf_test *test, const char *format, ...) iperf_json_finish(test); } else if (test && test->outfile && test->outfile != stdout) { + if (ct) { + fprintf(test->outfile, "%s", ct); + } fprintf(test->outfile, "iperf3: %s\n", str); } else { + if (ct) { + fprintf(stderr, "%s", ct); + } fprintf(stderr, "iperf3: %s\n", str); } va_end(argp); @@ -385,7 +421,13 @@ 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; + default: + snprintf(errstr, len, "int_errno=%d", int_errno); + perr = 1; + break; } /* Append the result of strerror() or gai_strerror() if appropriate */ diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 2dd5a5b..d5a5354 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -109,6 +109,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " -J, --json output in JSON format\n" " --logfile f send output to a log file\n" " --forceflush force flushing output at every interval\n" + " --timestamps <format> emit a timestamp at the start of each output line\n" + " (using optional format string as per strftime(3))\n" + " -d, --debug emit debugging output\n" " -v, --version show version information and quit\n" " -h, --help show this message and quit\n" @@ -117,6 +120,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" @@ -125,11 +131,11 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" #endif //HAVE_SSL "Client specific:\n" " -c, --client <host> run in client mode, connecting to <host>\n" -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) " --sctp use SCTP rather than TCP\n" " -X, --xbind <name> bind SCTP association to links\n" " --nstreams # number of SCTP streams\n" -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ " -u, --udp use UDP rather than TCP\n" " --connect-timeout # timeout for control connection setup (ms)\n" " -b, --bitrate #[KMG][/#] target bitrate in bits/sec (0 for unlimited)\n" diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c index 0bc98ba..e0c1ec1 100644 --- a/src/iperf_sctp.c +++ b/src/iperf_sctp.c @@ -57,7 +57,7 @@ int iperf_sctp_recv(struct iperf_stream *sp) { -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) int r; r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp); @@ -78,7 +78,7 @@ iperf_sctp_recv(struct iperf_stream *sp) #else i_errno = IENOSCTP; return -1; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ } @@ -89,7 +89,7 @@ iperf_sctp_recv(struct iperf_stream *sp) int iperf_sctp_send(struct iperf_stream *sp) { -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) int r; r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp); @@ -103,7 +103,7 @@ iperf_sctp_send(struct iperf_stream *sp) #else i_errno = IENOSCTP; return -1; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ } @@ -115,7 +115,7 @@ iperf_sctp_send(struct iperf_stream *sp) int iperf_sctp_accept(struct iperf_test * test) { -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) int s; signed char rbuf = ACCESS_DENIED; char cookie[COOKIE_SIZE]; @@ -148,7 +148,7 @@ iperf_sctp_accept(struct iperf_test * test) #else i_errno = IENOSCTP; return -1; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ } @@ -159,7 +159,7 @@ iperf_sctp_accept(struct iperf_test * test) int iperf_sctp_listen(struct iperf_test *test) { -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) struct addrinfo hints, *res; char portstr[6]; int s, opt, saved_errno; @@ -270,7 +270,7 @@ iperf_sctp_listen(struct iperf_test *test) #else i_errno = IENOSCTP; return -1; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ } @@ -281,7 +281,7 @@ iperf_sctp_listen(struct iperf_test *test) int iperf_sctp_connect(struct iperf_test *test) { -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) int s, opt, saved_errno; char portstr[6]; struct addrinfo hints, *local_res, *server_res; @@ -527,7 +527,7 @@ iperf_sctp_connect(struct iperf_test *test) #else i_errno = IENOSCTP; return -1; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ } @@ -535,12 +535,12 @@ iperf_sctp_connect(struct iperf_test *test) int iperf_sctp_init(struct iperf_test *test) { -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) return 0; #else i_errno = IENOSCTP; return -1; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ } @@ -552,7 +552,7 @@ iperf_sctp_init(struct iperf_test *test) int iperf_sctp_bindx(struct iperf_test *test, int s, int is_server) { -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) struct addrinfo hints; char portstr[6]; char *servname; @@ -701,5 +701,5 @@ out: #else i_errno = IENOSCTP; return -1; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ } diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 40d99bc..e2ddf7f 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2018 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. @@ -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 %" 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; @@ -647,7 +675,7 @@ iperf_run_server(struct iperf_test *test) return -1; } } - } + } } if (result == 0 || diff --git a/src/iperf_udp.c b/src/iperf_udp.c index ab6be5e..2fd7bf5 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -52,7 +52,13 @@ #if defined(HAVE_INTTYPES_H) # include <inttypes.h> #else -# define PRIu64 "llu" +# ifndef PRIu64 +# if sizeof(long) == 8 +# define PRIu64 "lu" +# else +# define PRIu64 "llu" +# endif +# endif #endif /* iperf_udp_recv diff --git a/src/iperf_util.c b/src/iperf_util.c index 412397a..9ca1eec 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -111,7 +111,7 @@ void fill_with_repeating_pattern(void *out, size_t outsize) */ void -make_cookie(char *cookie) +make_cookie(const char *cookie) { unsigned char *out = (unsigned char*)cookie; size_t pos; @@ -267,7 +267,7 @@ get_optional_features(void) numfeatures++; #endif /* HAVE_FLOWLABEL */ -#if defined(HAVE_SCTP) +#if defined(HAVE_SCTP_H) if (numfeatures > 0) { strncat(features, ", ", sizeof(features) - strlen(features) - 1); @@ -275,7 +275,7 @@ get_optional_features(void) strncat(features, "SCTP", sizeof(features) - strlen(features) - 1); numfeatures++; -#endif /* HAVE_SCTP */ +#endif /* HAVE_SCTP_H */ #if defined(HAVE_TCP_CONGESTION) if (numfeatures > 0) { @@ -402,7 +402,7 @@ iperf_json_printf(const char *format, ...) /* Debugging routine to dump out an fd_set. */ void -iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds) +iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds) { int fd; int comma; diff --git a/src/iperf_util.h b/src/iperf_util.h index 76bfd20..b109af2 100644 --- a/src/iperf_util.h +++ b/src/iperf_util.h @@ -54,7 +54,7 @@ const char* get_optional_features(void); cJSON* iperf_json_printf(const char *format, ...); -void iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds); +void iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds); #ifndef HAVE_DAEMON extern int daemon(int nochdir, int noclose); @@ -121,7 +121,7 @@ timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, /* make connection to server */ int -netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout) +netdial(int domain, int proto, const char *local, int local_port, const char *server, int port, int timeout) { struct addrinfo hints, *local_res, *server_res; int s, saved_errno; @@ -218,7 +218,7 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po /***************************************************************/ int -netannounce(int domain, int proto, char *local, int port) +netannounce(int domain, int proto, const char *local, int port) { struct addrinfo hints, *res; char portstr[6]; @@ -28,8 +28,8 @@ #define __NET_H int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout); -int netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout); -int netannounce(int domain, int proto, char *local, int port); +int netdial(int domain, int proto, const char *local, int local_port, const char *server, int port, int timeout); +int netannounce(int domain, int proto, const char *local, int port); int Nread(int fd, char *buf, size_t count, int prot); int Nwrite(int fd, const char *buf, size_t count, int prot) /* __attribute__((hot)) */; int has_sendfile(void); |