diff --git a/src/iperf.h b/src/iperf.h index d1abb7e47..ee5b47a7b 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -160,6 +160,8 @@ 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 num_of_blocks; /* number of different (mainly for randomized) blocks to send (-l) */ + int last_block_sent; /* last block sent from the `num_of_blocks` */ 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 averaging total data rate */ @@ -219,6 +221,7 @@ struct iperf_stream int green_light; int buffer_fd; /* data to send, file descriptor */ char *buffer; /* data to send, mmapped */ + size_t buffer_size; /* size of the buffer */ int pending_size; /* pending data to send */ int diskfile_fd; /* file to send, file descriptor */ int diskfile_left; /* remaining file data on disk */ diff --git a/src/iperf_api.c b/src/iperf_api.c index f7110a0eb..ee9eba73e 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1357,7 +1357,19 @@ 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; + test->settings->num_of_blocks = atof(slash); + if (test->settings->num_of_blocks < 1) { + i_errno = IETNUMOFBLOCKS; + return -1; + } + } + if (strlen(optarg) > 0) { + blksize = unit_atoi(optarg); + } client_flag = 1; break; case 'P': @@ -1835,6 +1847,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) i_errno = IEUDPBLOCKSIZE; return -1; } + if (blksize * test->settings->num_of_blocks > MAX_BLOCKSIZE) { + i_errno = IETNUMOFBLOCKS; + return -1; + } test->settings->blksize = blksize; if (!rate_flag) @@ -2357,6 +2373,7 @@ 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); + cJSON_AddNumberToObject(j, "numblocks", test->settings->num_of_blocks); if (test->settings->rate) cJSON_AddNumberToObject(j, "bandwidth", test->settings->rate); if (test->settings->fqrate) @@ -2479,6 +2496,8 @@ get_parameters(struct iperf_test *test) test->settings->socket_bufsize = j_p->valueint; if ((j_p = iperf_cJSON_GetObjectItemType(j, "len", cJSON_Number)) != NULL) test->settings->blksize = j_p->valueint; + if ((j_p = iperf_cJSON_GetObjectItemType(j, "numblocks", cJSON_Number)) != NULL) + test->settings->num_of_blocks = j_p->valueint; if ((j_p = iperf_cJSON_GetObjectItemType(j, "bandwidth", cJSON_Number)) != NULL) test->settings->rate = j_p->valueint; if ((j_p = iperf_cJSON_GetObjectItemType(j, "fqrate", cJSON_Number)) != NULL) @@ -3125,6 +3144,8 @@ 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->num_of_blocks = 1; + testp->settings->last_block_sent = 0; testp->settings->rate = 0; testp->settings->bitrate_limit = 0; testp->settings->bitrate_limit_interval = 5; @@ -3440,6 +3461,8 @@ iperf_reset_test(struct iperf_test *test) test->num_streams = 1; test->settings->socket_bufsize = 0; test->settings->blksize = DEFAULT_TCP_BLKSIZE; + test->settings->num_of_blocks = 1; + test->settings->last_block_sent = 0; test->settings->rate = 0; test->settings->fqrate = 0; test->settings->burst = 0; @@ -4584,7 +4607,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->buffer_size); close(sp->buffer_fd); if (sp->diskfile_fd >= 0) close(sp->diskfile_fd); @@ -4604,6 +4627,7 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) { struct iperf_stream *sp; int ret = 0; + size_t buffer_size = test->settings->blksize * test->settings->num_of_blocks; char template[1024]; if (test->tmp_template) { @@ -4662,19 +4686,20 @@ 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, buffer_size) < 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, buffer_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, sp->buffer_fd, 0); if (sp->buffer == MAP_FAILED) { i_errno = IECREATESTREAM; free(sp->result); free(sp); return NULL; } + sp->buffer_size = buffer_size; sp->pending_size = 0; /* Set socket */ @@ -4687,7 +4712,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->buffer_size); free(sp->result); free(sp); return NULL; @@ -4699,15 +4724,18 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) } else sp->diskfile_fd = -1; - /* Initialize stream */ - if (test->repeating_payload) - fill_with_repeating_pattern(sp->buffer, test->settings->blksize); - else - ret = readentropy(sp->buffer, test->settings->blksize); + /* Initialize stream send buffer */ + if (sender) { + if (test->repeating_payload) + fill_with_repeating_pattern(sp->buffer, sp->buffer_size); + else { + ret = readentropy(sp->buffer, sp->buffer_size); + } + } if ((ret < 0) || (iperf_init_stream(sp, test) < 0)) { close(sp->buffer_fd); - munmap(sp->buffer, sp->test->settings->blksize); + munmap(sp->buffer, sp->buffer_size); free(sp->result); free(sp); return NULL; diff --git a/src/iperf_api.h b/src/iperf_api.h index 4701264ea..8a9c6f67c 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -436,6 +436,7 @@ enum { IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP IESERVERAUTHUSERS = 35, // Cannot access authorized users file IECNTLKA = 36, // Control connection Keepalive period should be larger than the full retry period (interval * count) + IETNUMOFBLOCKS = 37, // Invalid number of different (randomized) blocks to send /* Test errors */ IENEWTEST = 100, // Unable to create a new test (check perror) IEINITTEST = 101, // Test initialization failed (check perror) diff --git a/src/iperf_error.c b/src/iperf_error.c index f76ad5f21..bf4970bc1 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -206,6 +206,9 @@ iperf_strerror(int int_errno) case IEUDPBLOCKSIZE: snprintf(errstr, len, "block size invalid (minimum = %d bytes, maximum = %d bytes)", MIN_UDP_BLOCKSIZE, MAX_UDP_BLOCKSIZE); break; + case IETNUMOFBLOCKS: + snprintf(errstr, len, "number of different (randomized) blocks to send invalid (minimum = 1, max total size of all blocks = %d)", MAX_BLOCKSIZE); + break; case IEBADTOS: snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)"); break; diff --git a/src/iperf_locale.c b/src/iperf_locale.c index c59fd116b..dd01efc89 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -181,8 +181,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " (per direction) at least this number of bytes (instead of -t or -k)\n" " -k, --blockcount #[KMG] transmit until the end of the interval when the client sent or received\n" " (per direction) at least this number of blocks (instead of -t or -n)\n" - " -l, --length #[KMG] length of buffer to read or write\n" - " (default %d KB for TCP, dynamic or %d for UDP)\n" + " -l, --length #[KMG]/[/#] length of buffer to read or write\n" + " (default %d KB for TCP, dynamic or %d for UDP)\n" + " (optional slash and number of different (randomized) blocks to send)\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" diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c index f2e4baef0..2c7a08f19 100644 --- a/src/iperf_sctp.c +++ b/src/iperf_sctp.c @@ -87,8 +87,21 @@ iperf_sctp_send(struct iperf_stream *sp) { #if defined(HAVE_SCTP_H) int r; + int size = sp->settings->blksize; + char *buffer = sp->buffer; + int num_of_blocks = sp->settings->num_of_blocks; + int block_to_send; + + if (num_of_blocks > 1) { // Send the next buffered block (mainly used for randomized blocks) + block_to_send = sp->settings->last_block_sent + 1; + if (block_to_send >= num_of_blocks) { + block_to_send = 0; + } + sp->settings->last_block_sent = block_to_send; + buffer += (size * block_to_send); + } - r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp); + r = Nwrite(sp->socket, buffer, size, Psctp); if (r < 0) return r; diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c index 98b9b51b2..bd74be8ab 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c @@ -93,14 +93,31 @@ int iperf_tcp_send(struct iperf_stream *sp) { int r; - - if (!sp->pending_size) - sp->pending_size = sp->settings->blksize; + int size = sp->settings->blksize; + char *buffer = sp->buffer; + int num_of_blocks = sp->settings->num_of_blocks; + int block_to_send; + + if (!sp->pending_size) { + sp->pending_size = size; + if (num_of_blocks > 1) { // Send the next buffered block (mainly used for randomized blocks) + block_to_send = sp->settings->last_block_sent + 1; + if (block_to_send >= num_of_blocks) { + block_to_send = 0; + } + sp->settings->last_block_sent = block_to_send; + buffer += (size * block_to_send); + } + } else { + if (num_of_blocks > 1) { // Send the next buffered block (mainly used for randomized blocks) + buffer += (size * sp->settings->last_block_sent) - sp->pending_size; + } + } if (sp->test->zerocopy) - r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size); + r = Nsendfile(sp->buffer_fd, sp->socket, buffer, sp->pending_size); else - r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp); + r = Nwrite(sp->socket, buffer, sp->pending_size, Ptcp); if (r < 0) return r; diff --git a/src/iperf_udp.c b/src/iperf_udp.c index c8835e6d7..780ad3313 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -212,10 +212,22 @@ iperf_udp_send(struct iperf_stream *sp) { int r; int size = sp->settings->blksize; + char *buffer = sp->buffer; + int num_of_blocks = sp->settings->num_of_blocks; + int block_to_send; struct iperf_time before; iperf_time_now(&before); + if (num_of_blocks > 1) { // Send the next buffered block (mainly used for randomized blocks) + block_to_send = sp->settings->last_block_sent + 1; + if (block_to_send >= num_of_blocks) { + block_to_send = 0; + } + sp->settings->last_block_sent = block_to_send; + buffer += (size * block_to_send); + } + ++sp->packet_count; if (sp->test->udp_counters_64bit) { @@ -227,9 +239,9 @@ iperf_udp_send(struct iperf_stream *sp) usec = htonl(before.usecs); pcount = htobe64(sp->packet_count); - memcpy(sp->buffer, &sec, sizeof(sec)); - memcpy(sp->buffer+4, &usec, sizeof(usec)); - memcpy(sp->buffer+8, &pcount, sizeof(pcount)); + memcpy(buffer, &sec, sizeof(sec)); + memcpy(buffer+4, &usec, sizeof(usec)); + memcpy(buffer+8, &pcount, sizeof(pcount)); } else { @@ -240,13 +252,13 @@ iperf_udp_send(struct iperf_stream *sp) usec = htonl(before.usecs); pcount = htonl(sp->packet_count); - memcpy(sp->buffer, &sec, sizeof(sec)); - memcpy(sp->buffer+4, &usec, sizeof(usec)); - memcpy(sp->buffer+8, &pcount, sizeof(pcount)); + memcpy(buffer, &sec, sizeof(sec)); + memcpy(buffer+4, &usec, sizeof(usec)); + memcpy(buffer+8, &pcount, sizeof(pcount)); } - r = Nwrite(sp->socket, sp->buffer, size, Pudp); + r = Nwrite(sp->socket, buffer, size, Pudp); if (r <= 0) { --sp->packet_count; /* Don't count messages that no data was sent from them. diff --git a/src/iperf_util.c b/src/iperf_util.c index b5c661bbc..374b63412 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -59,7 +59,6 @@ int readentropy(void *out, size_t outsize) { static FILE *frandom; static const char rndfile[] = "/dev/urandom"; - if (!outsize) return 0; if (frandom == NULL) {