diff --git a/src/iperf.h b/src/iperf.h index 0707614bb..3f75e23a7 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -137,12 +137,15 @@ struct iperf_settings int domain; /* AF_INET or AF_INET6 */ int socket_bufsize; /* window size for TCP */ int blksize; /* size of read/writes (-l) */ + int blksize_max; /* maximum UDP read/write size */ 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 pacing_timer; /* pacing timer in microseconds */ + int gap_time; /* (minimum) gap time between packets in miliseconds */ + int gap_time_max; /* maximum gap time between packets in miliseconds */ int burst; /* packets per burst */ int mss; /* for TCP MSS */ int ttl; /* IP TTL option */ @@ -185,7 +188,7 @@ struct iperf_stream char *buffer; /* data to send, mmapped */ int diskfile_fd; /* file to send, file descriptor */ int diskfile_left; /* remaining file data on disk */ - + int gap_packet_count; /* number of packets that do not require wait - to average sleep time for small gaps */ /* * for udp measurements - This can be a structure outside stream, and * stream can have a pointer to this diff --git a/src/iperf_api.c b/src/iperf_api.c index 3657ae14c..3398d83a8 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -230,6 +230,24 @@ iperf_get_test_blksize(struct iperf_test *ipt) return ipt->settings->blksize; } +int +iperf_get_test_blksize_max(struct iperf_test *ipt) +{ + return ipt->settings->blksize_max; +} + +int +iperf_get_test_gap_time(struct iperf_test *ipt) +{ + return ipt->settings->gap_time; +} + +int +iperf_get_test_gap_time_max(struct iperf_test *ipt) +{ + return ipt->settings->gap_time_max; +} + FILE * iperf_get_test_outfile (struct iperf_test *ipt) { @@ -431,6 +449,24 @@ iperf_set_test_blksize(struct iperf_test *ipt, int blksize) ipt->settings->blksize = blksize; } +void +iperf_set_test_blksize_max(struct iperf_test *ipt, int blksize_max) +{ + ipt->settings->blksize_max = blksize_max; +} + +void +iperf_set_test_gap_time(struct iperf_test *ipt, int gap_time) +{ + ipt->settings->gap_time = gap_time; +} + +void +iperf_set_test_sleep_timer_max(struct iperf_test *ipt, int gap_time) +{ + ipt->settings->gap_time_max = gap_time; +} + void iperf_set_test_logfile(struct iperf_test *ipt, const char *logfile) { @@ -755,15 +791,15 @@ 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", 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)); + cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d blksize_max: %d gap_time: %d gap_time_max: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->settings->blksize_max, (int64_t) test->settings->gap_time, (int64_t) test->settings->gap_time_max, (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)); } else { if (test->verbose) { 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->protocol->name, test->num_streams, test->settings->blksize, test->settings->blksize_max, test->settings->gap_time, test->settings->gap_time_max, test->omit, test->settings->bytes, test->settings->tos); 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->protocol->name, test->num_streams, test->settings->blksize, test->settings->blksize_max, test->settings->gap_time, test->settings->gap_time_max, test->omit, test->settings->blocks, test->settings->tos); 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->protocol->name, test->num_streams, test->settings->blksize, test->settings->blksize_max, test->settings->gap_time, test->settings->gap_time_max, test->omit, test->duration, test->settings->tos); } } } @@ -938,6 +974,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"fq-rate", required_argument, NULL, OPT_FQ_RATE}, {"pacing-timer", required_argument, NULL, OPT_PACING_TIMER}, {"connect-timeout", required_argument, NULL, OPT_CONNECT_TIMEOUT}, + {"gap-time", required_argument, NULL, OPT_GAP_TIME}, {"debug", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} @@ -945,6 +982,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) int flag; int portno; int blksize; + int blksize_max; int server_flag, client_flag, rate_flag, duration_flag; char *endptr; #if defined(HAVE_CPU_AFFINITY) @@ -955,6 +993,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) double farg; blksize = 0; + blksize_max = 0; server_flag = client_flag = rate_flag = duration_flag = 0; #if defined(HAVE_SSL) char *client_username = NULL, *client_rsa_public_key = NULL, *server_rsa_private_key = NULL; @@ -1104,7 +1143,21 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) client_flag = 1; break; case 'l': - blksize = unit_atoi(optarg); + slash = strchr(optarg, '/'); + if (slash) { + *slash = '\0'; + ++slash; + blksize_max = atoi(slash); + if (blksize_max <= 0) { + i_errno = IEUDPBLOCKSIZE; + return -1; + } + } + blksize = unit_atoi(optarg); + if (blksize <= 0 && blksize_max > 0) { + i_errno = IEUDPBLOCKSIZE; + return -1; + } client_flag = 1; break; case 'P': @@ -1357,6 +1410,23 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->settings->connect_timeout = unit_atoi(optarg); client_flag = 1; break; + case OPT_GAP_TIME: + slash = strchr(optarg, '/'); + if (slash) { + *slash = '\0'; + ++slash; + test->settings->gap_time_max = atoi(slash); + } + test->settings->gap_time = unit_atoi(optarg); + if (test->settings->gap_time_max == 0) + test->settings->gap_time_max = test->settings->gap_time; + 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; case 'h': usage_long(stdout); exit(0); @@ -1443,17 +1513,22 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) blksize = DEFAULT_TCP_BLKSIZE; } if ((test->protocol->id != Pudp && blksize <= 0) - || blksize > MAX_BLOCKSIZE) { + || blksize > MAX_BLOCKSIZE || blksize_max > MAX_BLOCKSIZE) { i_errno = IEBLOCKSIZE; return -1; } - if (test->protocol->id == Pudp && - (blksize > 0 && - (blksize < MIN_UDP_BLOCKSIZE || blksize > MAX_UDP_BLOCKSIZE))) { - i_errno = IEUDPBLOCKSIZE; - return -1; + + if (blksize_max == 0) + blksize_max = blksize; + if (test->protocol->id == Pudp) { + if (test->protocol->id == Pudp && + (blksize > 0 && (blksize < MIN_UDP_BLOCKSIZE || blksize_max > MAX_UDP_BLOCKSIZE))) { + i_errno = IEUDPBLOCKSIZE; + return -1; + } } test->settings->blksize = blksize; + test->settings->blksize_max = blksize_max; if (!rate_flag) test->settings->rate = test->protocol->id == Pudp ? UDP_RATE : 0; @@ -1674,6 +1749,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; @@ -1888,12 +1965,18 @@ send_parameters(struct iperf_test *test) cJSON_AddNumberToObject(j, "window", test->settings->socket_bufsize); if (test->settings->blksize) cJSON_AddNumberToObject(j, "len", test->settings->blksize); + if (test->settings->blksize_max) + cJSON_AddNumberToObject(j, "len_max", test->settings->blksize_max); if (test->settings->rate) cJSON_AddNumberToObject(j, "bandwidth", test->settings->rate); if (test->settings->fqrate) cJSON_AddNumberToObject(j, "fqrate", test->settings->fqrate); if (test->settings->pacing_timer) cJSON_AddNumberToObject(j, "pacing_timer", test->settings->pacing_timer); + 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 (test->settings->burst) cJSON_AddNumberToObject(j, "burst", test->settings->burst); if (test->settings->tos) @@ -1996,12 +2079,18 @@ get_parameters(struct iperf_test *test) test->settings->socket_bufsize = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "len")) != NULL) test->settings->blksize = j_p->valueint; + if ((j_p = cJSON_GetObjectItem(j, "len_max")) != NULL) + test->settings->blksize_max = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "bandwidth")) != NULL) test->settings->rate = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "fqrate")) != NULL) test->settings->fqrate = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "pacing_timer")) != NULL) test->settings->pacing_timer = j_p->valueint; + if ((j_p = cJSON_GetObjectItem(j, "gap_time")) != NULL) + test->settings->gap_time = j_p->valueint; + if ((j_p = cJSON_GetObjectItem(j, "gap_time_max")) != NULL) + test->settings->gap_time_max = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "burst")) != NULL) test->settings->burst = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "TOS")) != NULL) @@ -2521,12 +2610,15 @@ iperf_defaults(struct iperf_test *testp) testp->settings->unit_format = 'a'; testp->settings->socket_bufsize = 0; /* use autotuning */ testp->settings->blksize = DEFAULT_TCP_BLKSIZE; + testp->settings->blksize_max = testp->settings->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->gap_time = 0; + testp->settings->gap_time_max = 0; testp->settings->burst = 0; testp->settings->mss = 0; testp->settings->bytes = 0; @@ -2811,10 +2903,13 @@ iperf_reset_test(struct iperf_test *test) test->num_streams = 1; test->settings->socket_bufsize = 0; test->settings->blksize = DEFAULT_TCP_BLKSIZE; + test->settings->blksize_max = test->settings->blksize; test->settings->rate = 0; test->settings->burst = 0; test->settings->mss = 0; test->settings->tos = 0; + test->settings->gap_time = 0; + test->settings->gap_time_max = 0; #if defined(HAVE_SSL) if (test->settings->authtoken) { @@ -3828,7 +3923,7 @@ iperf_free_stream(struct iperf_stream *sp) struct iperf_interval_results *irp, *nirp; /* XXX: need to free interval list too! */ - munmap(sp->buffer, sp->test->settings->blksize); + munmap(sp->buffer, sp->test->settings->blksize_max); close(sp->buffer_fd); if (sp->diskfile_fd >= 0) close(sp->diskfile_fd); @@ -3902,13 +3997,13 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) free(sp); return NULL; } - if (ftruncate(sp->buffer_fd, test->settings->blksize) < 0) { + if (ftruncate(sp->buffer_fd, test->settings->blksize_max) < 0) { i_errno = IECREATESTREAM; free(sp->result); free(sp); return NULL; } - sp->buffer = (char *) mmap(NULL, test->settings->blksize, PROT_READ|PROT_WRITE, MAP_PRIVATE, sp->buffer_fd, 0); + sp->buffer = (char *) mmap(NULL, test->settings->blksize_max, PROT_READ|PROT_WRITE, MAP_PRIVATE, sp->buffer_fd, 0); if (sp->buffer == MAP_FAILED) { i_errno = IECREATESTREAM; free(sp->result); @@ -3916,6 +4011,8 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) return NULL; } + sp->gap_packet_count = 0; + /* Set socket */ sp->socket = s; @@ -3926,7 +4023,7 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) sp->diskfile_fd = open(test->diskfile_name, sender ? O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), S_IRUSR|S_IWUSR); if (sp->diskfile_fd == -1) { i_errno = IEFILE; - munmap(sp->buffer, sp->test->settings->blksize); + munmap(sp->buffer, sp->test->settings->blksize_max); free(sp->result); free(sp); return NULL; @@ -3940,13 +4037,13 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) /* Initialize stream */ if (test->repeating_payload) - fill_with_repeating_pattern(sp->buffer, test->settings->blksize); + fill_with_repeating_pattern(sp->buffer, test->settings->blksize_max); else - ret = readentropy(sp->buffer, test->settings->blksize); + ret = readentropy(sp->buffer, test->settings->blksize_max); if ((ret < 0) || (iperf_init_stream(sp, test) < 0)) { close(sp->buffer_fd); - munmap(sp->buffer, sp->test->settings->blksize); + munmap(sp->buffer, sp->test->settings->blksize_max); free(sp->result); free(sp); return NULL; @@ -4034,13 +4131,13 @@ diskfile_send(struct iperf_stream *sp) static int rtot; /* if needed, read enough data from the disk to fill up the buffer */ - if (sp->diskfile_left < sp->test->settings->blksize && !sp->test->done) { - r = read(sp->diskfile_fd, sp->buffer, sp->test->settings->blksize - + if (sp->diskfile_left < sp->test->settings->blksize_max && !sp->test->done) { + r = read(sp->diskfile_fd, sp->buffer, sp->test->settings->blksize_max - sp->diskfile_left); rtot += r; if (sp->test->debug) { printf("read %d bytes from file, %d total\n", r, rtot); - if (r != sp->test->settings->blksize - sp->diskfile_left) + if (r != sp->test->settings->blksize_max - sp->diskfile_left) printf("possible eof\n"); } /* If there's no data left in the file or in the buffer, we're done */ @@ -4061,13 +4158,13 @@ diskfile_send(struct iperf_stream *sp) * front of the buffer so they can hopefully go out on the next * pass. */ - sp->diskfile_left = sp->test->settings->blksize - r; - if (sp->diskfile_left && sp->diskfile_left < sp->test->settings->blksize) { + sp->diskfile_left = sp->test->settings->blksize_max - r; + if (sp->diskfile_left && sp->diskfile_left < sp->test->settings->blksize_max) { memcpy(sp->buffer, - sp->buffer + (sp->test->settings->blksize - sp->diskfile_left), + sp->buffer + (sp->test->settings->blksize_max - sp->diskfile_left), sp->diskfile_left); if (sp->test->debug) - printf("Shifting %d bytes by %d\n", sp->diskfile_left, (sp->test->settings->blksize - sp->diskfile_left)); + printf("Shifting %d bytes by %d\n", sp->diskfile_left, (sp->test->settings->blksize_max - sp->diskfile_left)); } return r; } diff --git a/src/iperf_api.h b/src/iperf_api.h index 5d71e7983..df532e6ea 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -80,6 +80,7 @@ typedef uint64_t iperf_size_t; #define OPT_SERVER_BITRATE_LIMIT 21 #define OPT_TIMESTAMPS 22 #define OPT_SERVER_SKEW_THRESHOLD 23 +#define OPT_GAP_TIME 24 /* states */ #define TEST_START 1 @@ -109,9 +110,12 @@ int iperf_get_test_duration( struct iperf_test* ipt ); char iperf_get_test_role( struct iperf_test* ipt ); int iperf_get_test_reverse( struct iperf_test* ipt ); int iperf_get_test_blksize( struct iperf_test* ipt ); +int iperf_get_test_blksize_max( struct iperf_test* ipt ); FILE* iperf_get_test_outfile( struct iperf_test* ipt ); uint64_t iperf_get_test_rate( struct iperf_test* ipt ); int iperf_get_test_pacing_timer( struct iperf_test* ipt ); +int iperf_get_test_gap_time( struct iperf_test* ipt ); +int iperf_get_test_gap_time_max( struct iperf_test* ipt ); uint64_t iperf_get_test_bytes( struct iperf_test* ipt ); uint64_t iperf_get_test_blocks( struct iperf_test* ipt ); int iperf_get_test_burst( struct iperf_test* ipt ); @@ -148,9 +152,12 @@ 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_blksize_step( struct iperf_test* ipt, int step ); 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_gap_time( struct iperf_test* ipt, int sleep_timer ); +void iperf_set_test_gap_time_max( struct iperf_test* ipt, int sleep_timer_max ); void iperf_set_test_bytes( struct iperf_test* ipt, uint64_t bytes ); void iperf_set_test_blocks( struct iperf_test* ipt, uint64_t blocks ); void iperf_set_test_burst( struct iperf_test* ipt, int burst ); @@ -364,6 +371,7 @@ enum { IETOTALRATE = 27, // Total required bandwidth is larger than server's limit IETOTALINTERVAL = 28, // Invalid time interval for calculating average data rate IESKEWTHRESHOLD = 29, // Invalid value specified as skew threshold + IEGAP = 30, // Illegal gap time value /* Test errors */ IENEWTEST = 100, // Unable to create a new test (check perror) IEINITTEST = 101, // Test initialization failed (check perror) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 59bebd70a..c3ca3f847 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -405,8 +405,10 @@ iperf_connect(struct iperf_test *test) else { test->settings->blksize = DEFAULT_UDP_BLKSIZE; } + if (test->settings->blksize_max < test->settings->blksize ) + test->settings->blksize_max = test->settings->blksize; if (test->verbose) { - printf("Setting UDP block size to %d\n", test->settings->blksize); + printf("Setting UDP block size to %d, max block size to %d\n", test->settings->blksize, test->settings->blksize_max); } } @@ -456,6 +458,7 @@ iperf_run_client(struct iperf_test * test) { int startup; int result = 0; + float min_sleep_time; fd_set read_set, write_set; struct iperf_time now; struct timeval* timeout = NULL; diff --git a/src/iperf_error.c b/src/iperf_error.c index 215da153a..450f73fa6 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -427,6 +427,9 @@ iperf_strerror(int int_errno) case IESKEWTHRESHOLD: snprintf(errstr, len, "skew threshold must be a positive number"); break; + case IEGAP: + snprintf(errstr, len, "gap time must be n1[/n2] where n1<=n2 are non-negative integers of miliseconds"); + break; default: snprintf(errstr, len, "int_errno=%d", int_errno); perr = 1; diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 04602b1be..e34ec20b5 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -151,8 +151,11 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " -t, --time # time in seconds to transmit for (default %d secs)\n" " -n, --bytes #[KMG] number of bytes to transmit (instead of -t)\n" " -k, --blockcount #[KMG] number of blocks (packets) to transmit (instead of -t or -n)\n" - " -l, --length #[KMG] length of buffer to read or write\n" + " -l, --length #[KMG][/#[KMG]] length of buffer to read or write\n" " (default %d KB for TCP, dynamic or %d for UDP)\n" + " optional for UDP - slash and maximum second size; when specified,\n" + " random packet length between the two sizes range is used\n" + " --cport bind to a specific client port (TCP and UDP, default: ephemeral port)\n" " -P, --parallel # number of parallel client streams to run\n" " -R, --reverse run in reverse mode (server sends, client receives)\n" @@ -170,6 +173,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " The usual prefixes for octal and hex can be used,\n" " i.e. 52, 064 and 0x34 all specify the same value.\n" + " --gap-time #[KMG][/#[KMG]] for UDP - time in miliseconds to wait between packets sending;\n" + " when range is given - each gap time is random time in the range.\n" + " --dscp N or --dscp val set the IP dscp value, either 0-63 or symbolic.\n" " Numeric values can be specified in decimal,\n" " octal and hex (see --tos above).\n" @@ -261,13 +267,13 @@ const char wait_server_threads[] = "Waiting for server threads to complete. Interrupt again to force quit.\n"; const char test_start_time[] = -"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %d second test, tos %d\n"; +"Starting Test: protocol: %s, %d streams, %d byte blocks with %d max bytes, gap between packets %d to %d ms, omitting %d seconds, %d second test, tos %d\n"; const char test_start_bytes[] = -"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %llu bytes to send, tos %d\n"; +"Starting Test: protocol: %s, %d streams, %d byte blocks with %d max bytes, gap between packets %d to %d ms, omitting %d seconds, %llu bytes to send, tos %d\n"; const char test_start_blocks[] = -"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %d blocks to send, tos %d\n"; +"Starting Test: protocol: %s, %d streams, %d byte blocks with %d max bytes, gap between packets %d to %d ms, omitting %d seconds, %d blocks to send, tos %d\n"; /* ------------------------------------------------------------------- diff --git a/src/iperf_udp.c b/src/iperf_udp.c index 2fd7bf5de..5e93c6473 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -48,6 +48,7 @@ #include "net.h" #include "cjson.h" #include "portable_endian.h" +#include #if defined(HAVE_INTTYPES_H) # include @@ -72,19 +73,27 @@ iperf_udp_recv(struct iperf_stream *sp) uint64_t pcount; int r; int size = sp->settings->blksize; + int size_max = sp->settings->blksize_max; int first_packet = 0; double transit = 0, d = 0; struct iperf_time sent_time, arrival_time, temp_time; - r = Nread(sp->socket, sp->buffer, size, Pudp); + /* If fixed size - read it all, otherwise read next message (as expected size is unknown) */ + if (size == size_max) + r = Nread(sp->socket, sp->buffer, size, Pudp); + else + r = Pread(sp->socket, sp->buffer, size_max, Pudp); /* * If we got an error in the read, or if we didn't read anything * because the underlying read(2) got a EAGAIN, then skip packet * processing. */ - if (r <= 0) + if (r <= 0) { + if (sp->test->debug) + fprintf(stderr, "iperf_udp_recv: No bytes read: %d;\n", r); return r; + } /* Only count bytes received while we're in the correct state. */ if (sp->test->state == TEST_RUNNING) { @@ -124,7 +133,7 @@ iperf_udp_recv(struct iperf_stream *sp) } if (sp->test->debug) - fprintf(stderr, "pcount %" PRIu64 " packet_count %d\n", pcount, sp->packet_count); + fprintf(stderr, "pcount %" PRIu64 " packet_count %d size %d\n", pcount, sp->packet_count, r); /* * Try to handle out of order packets. The way we do this @@ -166,7 +175,7 @@ iperf_udp_recv(struct iperf_stream *sp) sp->cnt_error--; /* Log the out-of-order packet */ - if (sp->test->debug) + if (sp->test->debug || (sp->test->verbose && (sp->test->settings->gap_time != sp->test->settings->gap_time_max))) fprintf(stderr, "OUT OF ORDER - incoming packet sequence %" PRIu64 " but expected sequence %d on stream %d", pcount, sp->packet_count + 1, sp->socket); } @@ -213,9 +222,36 @@ int iperf_udp_send(struct iperf_stream *sp) { int r; - int size = sp->settings->blksize; + int size, size_max; + int gap_time , gap_time_max, gap_packet_count; struct iperf_time before; + /* calculate block size to send */ + size = sp->settings->blksize; + size_max = sp->settings->blksize_max; + if (size_max > size) { + size += round(((float)rand()/RAND_MAX)*(size_max - size)); + } + + /* Sleep gap time after sending the packet */ + gap_time_max = sp->settings->gap_time_max; + gap_time = 0; + if (gap_time_max > 0) { + if (sp->gap_packet_count > 0) // No need to wait to average smapp gap times + sp->gap_packet_count--; + else { // Wait before sending the packet + gap_time = sp->settings->gap_time; + if (gap_time < gap_time_max) + gap_time += round(((float)rand()/RAND_MAX)*(gap_time_max - gap_time)); + if (gap_time > 0) { + /* wait before sending the packt */ + gap_packet_count = sleep_by_min_sleep_time(gap_time); + /* set numebr of packets that will not require wait - in case gap is small */ + sp->gap_packet_count = gap_packet_count - 1; + } + } + } + iperf_time_now(&before); ++sp->packet_count; @@ -257,7 +293,7 @@ iperf_udp_send(struct iperf_stream *sp) sp->result->bytes_sent_this_interval += r; if (sp->test->debug) - printf("sent %d bytes of %d, total %" PRIu64 "\n", r, sp->settings->blksize, sp->result->bytes_sent); + printf("sent %d bytes of %d after waiting %d[ms], total %" PRIu64 "\n", r, size, gap_time, sp->result->bytes_sent); return r; } diff --git a/src/iperf_util.c b/src/iperf_util.c index 9ca1eecd2..afd5b165c 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -45,10 +45,12 @@ #include #include #include +#include #include "cjson.h" #include "iperf.h" #include "iperf_api.h" +#include "iperf_time.h" /* * Read entropy from /dev/urandom @@ -565,3 +567,71 @@ getline(char **buf, size_t *bufsiz, FILE *fp) } #endif + + +/* + * Sleep for number of miliseconds + */ +void iperf_sleep(int sleep_time) +{ +#ifdef __WINDOWS__ + sleep(sleep_timer); +#else + struct timespec ts; + int res; + ts.tv_sec = sleep_time / 1000; + ts.tv_nsec = (sleep_time % 1000) * 1000000; + do { + res = nanosleep(&ts, NULL); + } while (res && errno == EINTR); +#endif +} + + +/* + * Since different OS support different minimum resolution of possible + * sleep time, calculate estimated average actual minimum sleep time + * (assume minimum sleep time is greater than 1 ms) + */ +float calculate_minimum_sleep_time() +{ + struct iperf_time start_time, end_time, diff_time; + int n, i; + float delta; + static float min_sleep_time = 0; + + if (min_sleep_time == 0) { // Not calculated yet + n = 10; + iperf_time_now(&start_time); + for (i = 0; i < n; i++) + iperf_sleep(1); + iperf_time_now(&end_time); + iperf_time_diff(&start_time, &end_time, &diff_time); + delta = ((float)iperf_time_in_usecs(&diff_time))/1000; + min_sleep_time = delta/n; + } + + return min_sleep_time; +} + +/* + * Sleep for required time, but since there is a minimum sleep time then sleep time + * may be much longer than required (e.g. is min time is 15ms and required sleep time is 2ms). + * Therefore retuen estimation of the factor between required sleep time and actual sleep time. + */ +int sleep_by_min_sleep_time(int sleep_time) +{ + float min_sleep_time; + int num_of_min_sleeps; + + min_sleep_time = calculate_minimum_sleep_time(); + num_of_min_sleeps = round(min_sleep_time / (float)sleep_time); + if (num_of_min_sleeps == 0) + num_of_min_sleeps = 1; + + iperf_sleep(sleep_time); + + return num_of_min_sleeps; +} + + diff --git a/src/iperf_util.h b/src/iperf_util.h index b109af2c6..a4071ff3a 100644 --- a/src/iperf_util.h +++ b/src/iperf_util.h @@ -56,6 +56,12 @@ cJSON* iperf_json_printf(const char *format, ...); void iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds); +void iperf_sleep(int sleep_timer); + +float calculate_minimum_sleep_time(); + +int sleep_by_min_sleep_time(int sleep_time); + #ifndef HAVE_DAEMON extern int daemon(int nochdir, int noclose); #endif /* HAVE_DAEMON */ diff --git a/src/main.c b/src/main.c index c82ee43c1..b2d064a23 100644 --- a/src/main.c +++ b/src/main.c @@ -134,6 +134,10 @@ run(struct iperf_test *test) /* Ignore SIGPIPE to simplify error handling */ signal(SIGPIPE, SIG_IGN); + calculate_minimum_sleep_time(); // Init min sleep() time + if (test->debug) + printf("Minimum sleep() time is %f[ms]\n", calculate_minimum_sleep_time()); + switch (test->role) { case 's': if (test->daemon) { diff --git a/src/net.c b/src/net.c index 8fde9c38d..01df49780 100644 --- a/src/net.c +++ b/src/net.c @@ -338,6 +338,25 @@ Nread(int fd, char *buf, size_t count, int prot) return count - nleft; } +/*******************************************/ +/* reads Packet from socket */ +/*******************************************/ +int +Pread(int fd, char *buf, size_t count, int prot) +{ + register ssize_t r; + + r = read(fd, buf, count); + if (r < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + r = 0; + else + return NET_HARDERROR; + } + + return r; +} + /* * N W R I T E diff --git a/src/net.h b/src/net.h index 80a2161b6..1a4c3bceb 100644 --- a/src/net.h +++ b/src/net.h @@ -31,6 +31,7 @@ int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int t 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 Pread(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); int Nsendfile(int fromfd, int tofd, const char *buf, size_t count) /* __attribute__((hot)) */; diff --git a/test_commands.sh b/test_commands.sh index 1cf2f4363..f90aa20a2 100755 --- a/test_commands.sh +++ b/test_commands.sh @@ -72,4 +72,14 @@ host=$1 # test congestion control option (linux only) ./src/iperf3 -c $host -C reno -V - +###### test different UDP traffic profiles with random packet size and wait before sending packet +# -l flag - fixed packet length +./src/iperf3 -c $host -u -d -k 7 -l 1000 +# -l flag - random length change in range +./src/iperf3 -c $host -u -d -k 7 -l 500/1200 +# --sleep flag - fixed gap time before each packet sending +./src/iperf3 -c $host -u -d -k 7 -l 500/1200 --gap 100 +# --sleep flag - random gap time in tange before each packet sending +./src/iperf3 -c $host -u -d -k 7 -l 100/1200 --gap 100/1000 +# -R flag - a UDP traffic profile with reverse option +./src/iperf3 -c $host -u -d -k 250 -l 100/200 --gap 5/50 -R