Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ struct iperf_settings
uint64_t fqrate; /* target data rate for FQ pacing*/
int pacing_timer; /* pacing timer in microseconds */
int burst; /* packets per burst */
uint64_t gap_time; /* (minimum) gap time between packets in miliseconds */
uint64_t gap_time_max; /* maximum gap time between packets in miliseconds */
int mss; /* for TCP MSS */
int ttl; /* IP TTL option */
int tos; /* type of service bit */
Expand Down
148 changes: 128 additions & 20 deletions src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,18 @@ iperf_get_mapped_v4(struct iperf_test* ipt)
return ipt->mapped_v4;
}

uint64_t
iperf_get_test_gap_time(struct iperf_test *ipt)
{
return ipt->settings->gap_time;
}

uint64_t
iperf_get_test_gap_time_max(struct iperf_test *ipt)
{
return ipt->settings->gap_time_max;
}

/************** Setter routines for some fields inside iperf_test *************/

void
Expand Down Expand Up @@ -876,6 +888,18 @@ iperf_set_test_mss(struct iperf_test *ipt, int mss)
ipt->settings->mss = mss;
}

void
iperf_set_test_gap_time(struct iperf_test *ipt, uint64_t gap_time)
{
ipt->settings->gap_time = gap_time;
}

void
iperf_set_test_gap_time_max(struct iperf_test *ipt, uint64_t gap_time)
{
ipt->settings->gap_time_max = gap_time;
}

/********************** Get/set test protocol structure ***********************/

struct protocol *
Expand Down Expand Up @@ -924,15 +948,23 @@ void
iperf_on_test_start(struct iperf_test *test)
{
if (test->json_output) {
cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d interval: %f gso: %d gro: %d", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, test->stats_interval, (uint64_t) test->settings->gso, (uint64_t) test->settings->gro));
cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d interval: %f gso: %d gro: %d gap_time: %llu gap_time_max: %llu", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, test->stats_interval, (uint64_t) test->settings->gso, (uint64_t) test->settings->gro, (int64_t) test->settings->gap_time, (int64_t) test->settings->gap_time_max));
} else {
if (test->verbose) {
iperf_printf(test, test_start_begin, test->protocol->name, test->num_streams, test->settings->blksize, test->omit);
if (test->settings->bytes)
iperf_printf(test, test_start_bytes, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->settings->bytes, test->settings->tos);
iperf_printf(test, test_start_bytes, test->settings->bytes);
else if (test->settings->blocks)
iperf_printf(test, test_start_blocks, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->settings->blocks, test->settings->tos);
iperf_printf(test, test_start_blocks, test->settings->blocks);
else
iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->duration, test->settings->tos);
iperf_printf(test, test_start_time, test->duration);

if (test->settings->gap_time_max > 0)
iperf_printf(test, test_start_gap, test->settings->gap_time, test->settings->gap_time_max);
else if (test->settings->rate > 0)
iperf_printf(test, test_start_rate, test->settings->rate);

iperf_printf(test, test_start_end, test->settings->tos);
}
}
if (test->json_stream) {
Expand Down Expand Up @@ -1195,6 +1227,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
#endif
{"gsro", no_argument, NULL, OPT_GSRO},
{"debug", optional_argument, NULL, 'd'},
#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
{"gap", required_argument, NULL, OPT_GAP_TIME},
#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP */
{"debug", no_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
Expand All @@ -1214,6 +1250,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
struct xbind_entry *xbe;
double farg;
int rcv_timeout_in = 0;
int pacing_timer_flag = 0;

blksize = 0;
server_flag = client_flag = rate_flag = duration_flag = rcv_timeout_flag = snd_timeout_flag =0;
Expand Down Expand Up @@ -1588,6 +1625,32 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
case 'F':
test->diskfile_name = optarg;
break;
#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
case OPT_GAP_TIME: // Gap-time option is in [ms] but saving in [us]
slash = strchr(optarg, '/');
if (slash) {
*slash = '\0';
++slash;
test->settings->gap_time_max = unit_time_atoi(slash);
if (i_errno != 0) {
return -1;
}
}
test->settings->gap_time = unit_time_atoi(optarg);
if (i_errno != 0) {
return -1;
}
if (test->settings->gap_time_max == 0)
test->settings->gap_time_max = test->settings->gap_time * 5;
if (test->settings->gap_time < 0 || test->settings->gap_time_max < 0 ||
test->settings->gap_time > test->settings->gap_time_max)
{
i_errno = IEGAP;
return -1;
}
client_flag = 1;
break;
#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP */
case OPT_IDLE_TIMEOUT:
test->settings->idle_timeout = atoi(optarg);
if (test->settings->idle_timeout < 1 || test->settings->idle_timeout > MAX_TIME) {
Expand Down Expand Up @@ -1966,9 +2029,20 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)

test->settings->blksize = blksize;

if (!rate_flag)
/* Disallow setting gap and either/bot bitrate or pace, as gap practically sets both */
if (test->settings->gap_time != 0 && (rate_flag || pacing_timer_flag != 0)) {
i_errno = IEGAPCONDITIONS;
return -1;
}

/* Set UDP default rate if rate was not set */
if (!rate_flag && test->settings->gap_time == 0)
test->settings->rate = test->protocol->id == Pudp ? UDP_RATE : 0;

/* If gap was set and bust was not set - set burst to 1 */
if (test->settings->burst == 0 && test->settings->gap_time != 0)
test->settings->burst = 1;

/* if no bytes or blocks specified, nor a duration_flag, and we have -F,
** get the file-size as the bytes count to be transferred
*/
Expand Down Expand Up @@ -2081,6 +2155,7 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP)
uint64_t bits_per_second;
int64_t missing_rate;
uint64_t bits_sent;
uint64_t gap_time = sp->test->settings->gap_time;

#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
struct timespec nanosleep_time;
Expand All @@ -2091,27 +2166,40 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP)
int64_t ns;
#endif /* HAVE_CLOCK_NANOSLEEP */

if (sp->test->done || sp->test->settings->rate == 0)
if (sp->test->done || (sp->test->settings->rate == 0 && gap_time == 0))
return;
iperf_time_diff(&sp->result->start_time_fixed, nowP, &temp_time);
seconds = iperf_time_in_secs(&temp_time);
bits_sent = sp->result->bytes_sent * 8;
bits_per_second = bits_sent / seconds;
missing_rate = sp->test->settings->rate - bits_per_second;

if (missing_rate > 0) {
sp->green_light = 1;
} else {
// Determine there is already a green light for sending the next message
if (gap_time == 0) { // Check per bit rate
iperf_time_diff(&sp->result->start_time_fixed, nowP, &temp_time);
seconds = iperf_time_in_secs(&temp_time);
bits_sent = sp->result->bytes_sent * 8;
bits_per_second = bits_sent / seconds;
missing_rate = sp->test->settings->rate - bits_per_second;

if (missing_rate > 0) {
sp->green_light = 1;
} else {
sp->green_light = 0;
}
} else { // Check per gap time - need to wait the gap time
sp->green_light = 0;
}

#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
// If estimated time to next send is large enough, sleep instead of just CPU looping until green light is set
if (missing_rate < 0) {
delta_bits = bits_sent - (seconds * sp->test->settings->rate);
// Calculate time until next data send is required
time_to_green_light = (SEC_TO_NS * delta_bits / sp->test->settings->rate);
// Whether should wait before next send
if (sp->green_light == 0) {
if (gap_time == 0) { // Wait per bits rate
delta_bits = bits_sent - (seconds * sp->test->settings->rate);
// Calculate time until next data send is required
time_to_green_light = (SEC_TO_NS * delta_bits / sp->test->settings->rate);
// Whether should wait before next send
} else { // Wait per gap time
time_to_green_light = uS_TO_NS * (gap_time + round(((float)rand()/RAND_MAX)*(sp->test->settings->gap_time_max - gap_time)));
if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG) {
printf("Gap time for sending next message is %lu ns\n", time_to_green_light);
}
}
if (time_to_green_light >= 0) {
#if defined(HAVE_CLOCK_NANOSLEEP)
if (clock_gettime(CLOCK_MONOTONIC, &nanosleep_time) == 0) {
Expand Down Expand Up @@ -2209,7 +2297,7 @@ iperf_send_mt(struct iperf_stream *sp)

/* Should bitrate throttle be checked for every send */
#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
if (test->settings->rate != 0) {
if (test->settings->rate != 0 || test->settings->gap_time != 0) {
throttle_check = 1;
if (test->settings->burst == 0)
throttle_check_per_message = 1;
Expand Down Expand Up @@ -2286,6 +2374,8 @@ iperf_init_test(struct iperf_test *test)
struct iperf_time now;
struct iperf_stream *sp;

srand(time(0)); /* reset random seed using current time for each new test */

if (test->protocol->init) {
if (test->protocol->init(test) < 0)
return -1;
Expand Down Expand Up @@ -2500,6 +2590,10 @@ send_parameters(struct iperf_test *test)
cJSON_AddNumberToObject(j, "repeating_payload", test->repeating_payload);
if (test->zerocopy)
cJSON_AddNumberToObject(j, "zerocopy", test->zerocopy);
if (test->settings->gap_time)
cJSON_AddNumberToObject(j, "gap_time", test->settings->gap_time);
if (test->settings->gap_time_max)
cJSON_AddNumberToObject(j, "gap_time_max", test->settings->gap_time_max);
#if defined(HAVE_DONT_FRAGMENT)
if (test->settings->dont_fragment)
cJSON_AddNumberToObject(j, "dont_fragment", test->settings->dont_fragment);
Expand Down Expand Up @@ -2782,6 +2876,18 @@ get_parameters(struct iperf_test *test)
if ((j_p = iperf_cJSON_GetObjectItemType(j, "zerocopy", cJSON_Number)) != NULL){
test->zerocopy = (j_p->valueint) ? 1: 0;
}
#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gap_time", cJSON_Number)) != NULL)
test->settings->gap_time = j_p->valueint;
if ((j_p = iperf_cJSON_GetObjectItemType(j, "gap_time_max", cJSON_Number)) != NULL)
test->settings->gap_time_max = j_p->valueint;
if (test->settings->gap_time < 0 || test->settings->gap_time_max < 0 ||
test->settings->gap_time > test->settings->gap_time_max)
{
i_errno = IEGAP;
r = -1;
}
#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP)*/
#if defined(HAVE_DONT_FRAGMENT)
if ((j_p = iperf_cJSON_GetObjectItemType(j, "dont_fragment", cJSON_Number)) != NULL){
test->settings->dont_fragment = (j_p->valueint) ? 1: 0;
Expand Down Expand Up @@ -3780,6 +3886,8 @@ iperf_reset_test(struct iperf_test *test)
test->settings->dont_fragment = 0;
test->zerocopy = 0;
test->settings->skip_rx_copy = 0;
test->settings->gap_time = 0;
test->settings->gap_time_max = 0;

#if defined(HAVE_SSL)
if (test->settings->authtoken) {
Expand Down
7 changes: 7 additions & 0 deletions src/iperf_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t;
#define OPT_JSON_STREAM_FULL_OUTPUT 33
#define OPT_SERVER_MAX_DURATION 34
#define OPT_GSRO 35
#define OPT_GAP_TIME 36

/* states */
#define TEST_START 1
Expand Down Expand Up @@ -175,6 +176,8 @@ int iperf_get_dont_fragment( struct iperf_test* ipt );
char* iperf_get_test_congestion_control(struct iperf_test* ipt);
int iperf_get_test_mss(struct iperf_test* ipt);
int iperf_get_mapped_v4(struct iperf_test* ipt);
uint64_t iperf_get_test_gap_time( struct iperf_test* ipt );
uint64_t iperf_get_test_gap_time_max( struct iperf_test* ipt );

/* Setter routines for some fields inside iperf_test. */
void iperf_set_verbose( struct iperf_test* ipt, int verbose );
Expand Down Expand Up @@ -227,6 +230,8 @@ void iperf_set_on_test_start_callback(struct iperf_test* ipt, void (*callback
void iperf_set_on_test_connect_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *));
void iperf_set_on_test_finish_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *));

void iperf_set_test_gap_time( struct iperf_test* ipt, uint64_t sleep_timer );
void iperf_set_test_gap_time_max( struct iperf_test* ipt, uint64_t sleep_timer_max );
#if defined(HAVE_SSL)
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);
Expand Down Expand Up @@ -445,6 +450,8 @@ enum {
IECNTLKA = 36, // Control connection Keepalive period should be larger than the full retry period (interval * count)
IEMAXSERVERTESTDURATIONEXCEEDED = 37, // Client's duration exceeds server's maximum duration
IEUNITVAL = 38, // Invalid unit value or suffix
IEGAP = 39, // Illegal gap time value
IEGAPCONDITIONS = 40, // --gap is mutual exclusive with --bitrate and --pacing-timer
/* Test errors */
IENEWTEST = 100, // Unable to create a new test (check perror)
IEINITTEST = 101, // Test initialization failed (check perror)
Expand Down
14 changes: 10 additions & 4 deletions src/iperf_error.c
Original file line number Diff line number Diff line change
Expand Up @@ -563,10 +563,16 @@ iperf_strerror(int int_errno)
snprintf(errstr, len, "server test duration expired");
perr = 1;
break;
default:
snprintf(errstr, len, "int_errno=%d", int_errno);
perr = 1;
break;
case IEGAP:
snprintf(errstr, len, "illegal gap time value");
break;
case IEGAPCONDITIONS:
snprintf(errstr, len, "--gap is mutual exclusive with --bitrate and --pacing-timer");
break;
default:
snprintf(errstr, len, "int_errno=%d", int_errno);
perr = 1;
break;
}

/* Append the result of strerror() or gai_strerror() if appropriate */
Expand Down
22 changes: 19 additions & 3 deletions src/iperf_locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" (optional slash and packet count for burst mode)\n"
" --pacing-timer #[KMG] set the Server timing for pacing, in microseconds (default %d)\n"
" (deprecated - for servers using older versions backward compatibility)\n"
#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
" --gap #[SMU][/#[SMU]] range for random time to delay between (burst of) packets - min/max;\n"
" max is optional - default is 5 times the min (units default is M (ms)),\n"
#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP */
#if defined(HAVE_SO_MAX_PACING_RATE)
" --fq-rate #[KMG] enable fair-queuing based socket pacing in\n"
" bits/sec (Linux only)\n"
Expand Down Expand Up @@ -236,6 +240,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"

"\n"
"[KMG] indicates options that support a K/M/G suffix for kilo-, mega-, or giga-\n"
"[SMU] indicates options that support a S/M/U time suffix for Sec, Mili, or Micro\n"
"\n"
#ifdef PACKAGE_URL
"iperf3 homepage at: " PACKAGE_URL "\n"
Expand Down Expand Up @@ -304,15 +309,26 @@ const char window_default[] =
const char wait_server_threads[] =
"Waiting for server threads to complete. Interrupt again to force quit.\n";

const char test_start_begin[] =
"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds";

const char test_start_time[] =
"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %d second test, tos %d\n";
", %d second test";

const char test_start_bytes[] =
"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %"PRIuFAST64" bytes to send, tos %d\n";
", %llu bytes to send";

const char test_start_blocks[] =
"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %"PRIuFAST64" blocks to send, tos %d\n";
", %d blocks to send";

const char test_start_gap[] =
", gap between packets %d to %d us";

const char test_start_rate[] =
", %d bps";

const char test_start_end[] =
", tos %d\n";

/* -------------------------------------------------------------------
* reports
Expand Down
4 changes: 4 additions & 0 deletions src/iperf_locale.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@ extern const char tcp_window_size[] ;
extern const char udp_buffer_size[] ;
extern const char window_default[] ;
extern const char wait_server_threads[] ;
extern const char test_start_begin[];
extern const char test_start_time[];
extern const char test_start_bytes[];
extern const char test_start_blocks[];
extern const char test_start_gap[];
extern const char test_start_rate[];
extern const char test_start_end[];

extern const char report_time[] ;
extern const char report_connecting[] ;
Expand Down
Loading
Loading