aboutsummaryrefslogtreecommitdiff
path: root/src/iperf_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/iperf_api.c')
-rw-r--r--src/iperf_api.c249
1 files changed, 224 insertions, 25 deletions
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);