diff options
Diffstat (limited to 'src/iperf_api.c')
-rw-r--r-- | src/iperf_api.c | 249 |
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); |