blob: 719a4a5690d9b9a45712cac2435367c18e4789ff [file] [log] [blame]
/*
* Copyright (c) 2009, The Regents of the University of California, through
* Lawrence Berkeley National Laboratory (subject to receipt of any required
* approvals from the U.S. Dept. of Energy). All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <stdint.h>
#include <netinet/tcp.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sched.h>
#include <signal.h>
#include <setjmp.h>
#include "iperf.h"
#include "iperf_api.h"
#include "iperf_udp.h"
#include "iperf_tcp.h"
#include "timer.h"
#include "net.h"
#include "units.h"
#include "tcp_window_size.h"
#include "iperf_util.h"
#include "locale.h"
jmp_buf env; /* to handle longjmp on signal */
/*************************************************************/
void
usage()
{
fprintf(stderr, usage_short);
}
void
usage_long()
{
fprintf(stderr, usage_long1);
fprintf(stderr, usage_long2);
}
int
all_data_sent(struct iperf_test * test)
{
if (test->default_settings->bytes > 0) {
if (test->bytes_sent >= (test->num_streams * test->default_settings->bytes)) {
return 1;
}
}
return 0;
}
int
iperf_send(struct iperf_test *test)
{
int result;
iperf_size_t bytes_sent;
fd_set temp_write_set;
struct timeval tv;
struct iperf_stream *sp;
memcpy(&temp_write_set, &test->write_set, sizeof(fd_set));
tv.tv_sec = 15;
tv.tv_usec = 0;
result = select(test->max_fd + 1, NULL, &temp_write_set, NULL, &tv);
if (result < 0 && errno != EINTR) {
perror("select iperf_send");
return -1;
}
if (result > 0) {
for (sp = test->streams; sp != NULL; sp = sp->next) {
if (FD_ISSET(sp->socket, &temp_write_set)) {
if ((bytes_sent = sp->snd(sp)) < 0) {
// XXX: Do better error handling
perror("iperf stream->snd");
return -1;
}
test->bytes_sent += bytes_sent;
FD_CLR(sp->socket, &temp_write_set);
}
}
}
return 0;
}
int
iperf_recv(struct iperf_test *test)
{
int result;
iperf_size_t bytes_sent;
fd_set temp_read_set;
struct timeval tv;
struct iperf_stream *sp;
memcpy(&temp_read_set, &test->read_set, sizeof(fd_set));
tv.tv_sec = 15;
tv.tv_usec = 0;
result = select(test->max_fd + 1, &temp_read_set, NULL, NULL, &tv);
if (result < 0) {
perror("select iperf_recv");
return -1;
}
if (result > 0) {
for (sp = test->streams; sp != NULL; sp = sp->next) {
if (FD_ISSET(sp->socket, &temp_read_set)) {
if ((bytes_sent = sp->rcv(sp)) < 0) {
// XXX: Do better error handling
perror("sp->rcv(sp)");
return -1;
}
test->bytes_sent += bytes_sent;
FD_CLR(sp->socket, &temp_read_set);
}
}
}
return 0;
}
void
iperf_init_test(struct iperf_test *test)
{
char *prot;
struct iperf_stream *sp;
int64_t dtargus;
if (test->protocol == Pudp) {
prot = "UDP";
dtargus = (int64_t) test->default_settings->blksize * SEC_TO_US * 8;
dtargus /= test->default_settings->rate;
assert(dtargus != 0);
for (sp = test->streams; sp; sp = sp->next)
sp->send_timer = new_timer(dtargus / SEC_TO_US, dtargus % SEC_TO_US);
} else {
prot = "TCP";
}
/* Set timers */
if (test->default_settings->bytes == 0) {
test->timer = new_timer(test->duration, 0);
printf(test_start_time, prot, test->num_streams, test->default_settings->blksize,
test->duration);
} else {
printf(test_start_bytes, prot, test->num_streams, test->default_settings->blksize,
test->default_settings->bytes);
}
if (test->stats_interval != 0)
test->stats_timer = new_timer(test->stats_interval, 0);
if (test->reporter_interval != 0)
test->reporter_timer = new_timer(test->reporter_interval, 0);
/* Set start time */
for (sp = test->streams; sp; sp = sp->next)
gettimeofday(&sp->result->start_time, NULL);
}
/*********************************************************/
int
package_parameters(struct iperf_test *test)
{
char pstring[256];
char optbuf[128];
memset(pstring, 0, 256*sizeof(char));
*pstring = ' ';
if (test->protocol == Ptcp) {
strncat(pstring, "-p ", sizeof(pstring));
} else if (test->protocol == Pudp) {
strncat(pstring, "-u ", sizeof(pstring));
}
snprintf(optbuf, sizeof(optbuf), "-P %d ", test->num_streams);
strncat(pstring, optbuf, sizeof(pstring));
if (test->reverse)
strncat(pstring, "-R ", sizeof(pstring));
if (test->default_settings->socket_bufsize) {
snprintf(optbuf, sizeof(optbuf), "-w %d ", test->default_settings->socket_bufsize);
strncat(pstring, optbuf, sizeof(pstring));
}
if (test->default_settings->rate) {
snprintf(optbuf, sizeof(optbuf), "-b %llu ", test->default_settings->rate);
strncat(pstring, optbuf, sizeof(pstring));
}
if (test->default_settings->mss) {
snprintf(optbuf, sizeof(optbuf), "-m %d ", test->default_settings->mss);
strncat(pstring, optbuf, sizeof(pstring));
}
if (test->no_delay) {
snprintf(optbuf, sizeof(optbuf), "-N ");
strncat(pstring, optbuf, sizeof(pstring));
}
if (test->default_settings->bytes) {
snprintf(optbuf, sizeof(optbuf), "-n %llu ", test->default_settings->bytes);
strncat(pstring, optbuf, sizeof(pstring));
}
if (test->duration) {
snprintf(optbuf, sizeof(optbuf), "-t %d ", test->duration);
strncat(pstring, optbuf, sizeof(pstring));
}
if (test->default_settings->blksize) {
snprintf(optbuf, sizeof(optbuf), "-l %d ", test->default_settings->blksize);
strncat(pstring, optbuf, sizeof(pstring));
}
*pstring = (char) (strlen(pstring) - 1);
if (Nwrite(test->ctrl_sck, pstring, (size_t) strlen(pstring), Ptcp) < 0) {
perror("Nwrite pstring");
return -1;
}
return 0;
}
int
parse_parameters(struct iperf_test *test)
{
int n;
char *param, **params;
char len, ch;
char pstring[256];
char readbuf[256];
memset(pstring, 0, 256 * sizeof(char));
if (read(test->ctrl_sck, &len, sizeof(char)) < 0) {
perror("read len");
return -1;
}
while (len > 0) {
memset(readbuf, 0, 256 * sizeof(char));
if ((len -= read(test->ctrl_sck, readbuf, (size_t) len)) < 0) {
perror("read pstring");
return -1;
}
strcat(pstring, readbuf);
}
for (param = strtok(pstring, " "), n = 0, params = NULL; param; param = strtok(NULL, " ")) {
if ((params = realloc(params, (n+1)*sizeof(char *))) == NULL) {
perror("realloc");
return -1;
}
params[n] = param;
n++;
}
while ((ch = getopt(n, params, "pt:n:m:uNP:Rw:l:b:")) != -1) {
switch (ch) {
case 'p':
test->protocol = Ptcp;
break;
case 't':
test->duration = atoi(optarg);
break;
case 'n':
test->default_settings->bytes = atoll(optarg);
break;
case 'm':
test->default_settings->mss = atoi(optarg);
break;
case 'u':
test->protocol = Pudp;
test->new_stream = iperf_new_udp_stream;
break;
case 'N':
test->no_delay = 1;
break;
case 'P':
test->num_streams = atoi(optarg);
break;
case 'R':
test->reverse = 1;
break;
case 'w':
test->default_settings->socket_bufsize = atoi(optarg);
break;
case 'l':
test->default_settings->blksize = atoi(optarg);
break;
case 'b':
test->default_settings->rate = atoll(optarg);
break;
}
}
#ifdef __APPLE__
optreset = 1;
#endif
optind = 0;
free(params);
return 0;
}
/**
* iperf_exchange_parameters - handles the param_Exchange part for client
*
*/
int
iperf_exchange_parameters(struct iperf_test * test)
{
int s, opt, len;
struct sockaddr_in sa;
if (test->role == 'c') {
package_parameters(test);
} else {
parse_parameters(test);
printf(" cookie: %s\n", test->default_settings->cookie);
if (test->protocol == Pudp) {
test->listener_udp = netannounce(test->protocol, NULL, test->server_port);
FD_SET(test->listener_udp, &test->read_set);
test->max_fd = (test->listener_udp > test->max_fd) ? test->listener_udp : test->max_fd;
} else if (test->protocol == Ptcp) {
if (test->no_delay || test->default_settings->mss) {
FD_CLR(test->listener_tcp, &test->read_set);
close(test->listener_tcp);
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket tcp listener mss");
return (-1);
}
if (test->no_delay) {
opt = 1;
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
perror("setsockopt TCP_NODELAY");
return (-1);
}
printf(" TCP NODELAY: on\n");
}
// XXX: Setting MSS is very buggy!
if (opt = test->default_settings->mss) {
if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
perror("setsockopt TCP_MAXSEG");
return (-1);
}
printf(" TCP MSS: %d\n", opt);
}
opt = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("setsockopt SO_REUSEADDR");
return (-1);
}
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_ANY);
sa.sin_port = htons(test->server_port);
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
close(s);
perror("bind tcp mss/nodelay listener");
return (-1);
}
listen(s, 5);
test->listener_tcp = s;
test->max_fd = (s > test->max_fd) ? s : test->max_fd;
FD_SET(test->listener_tcp, &test->read_set);
}
}
// Send the control message to create streams and start the test
test->state = CREATE_STREAMS;
if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
perror("Nwrite CREATE_STREAMS");
return -1;
}
}
return 0;
}
/*************************************************************/
int
iperf_exchange_results(struct iperf_test *test)
{
unsigned int size;
char buf[128];
char *results;
struct iperf_stream *sp;
iperf_size_t bytes_transferred;
if (test->role == 'c') {
/* Prepare results string and send to server */
results = NULL;
size = 0;
for (sp = test->streams; sp; sp = sp->next) {
bytes_transferred = (test->reverse ? sp->result->bytes_received : sp->result->bytes_sent);
snprintf(buf, 128, "%d:%llu,%lf,%d,%d\n", sp->id, bytes_transferred,sp->jitter,
sp->cnt_error, sp->packet_count);
size += strlen(buf);
if ((results = realloc(results, size+1)) == NULL) {
perror("realloc results");
return -1;
}
if (sp == test->streams)
*results = '\0';
strncat(results, buf, size+1);
}
size++;
size = htonl(size);
if (Nwrite(test->ctrl_sck, &size, sizeof(size), Ptcp) < 0) {
perror("Nwrite size");
return (-1);
}
if (Nwrite(test->ctrl_sck, results, ntohl(size), Ptcp) < 0) {
perror("Nwrite results");
return (-1);
}
free(results);
/* Get server results string */
if (Nread(test->ctrl_sck, &size, sizeof(size), Ptcp) < 0) {
perror("Nread size");
return (-1);
}
size = ntohl(size);
results = (char *) malloc(size * sizeof(char));
if (results == NULL) {
perror("malloc results");
return (-1);
}
if (Nread(test->ctrl_sck, results, size, Ptcp) < 0) {
perror("Nread results");
return (-1);
}
parse_results(test, results);
free(results);
} else {
/* Get client results string */
if (Nread(test->ctrl_sck, &size, sizeof(size), Ptcp) < 0) {
perror("Nread size");
return (-1);
}
size = ntohl(size);
results = (char *) malloc(size * sizeof(char));
if (results == NULL) {
perror("malloc results");
return (-1);
}
if (Nread(test->ctrl_sck, results, size, Ptcp) < 0) {
perror("Nread results");
return (-1);
}
parse_results(test, results);
free(results);
/* Prepare results string and send to client */
results = NULL;
size = 0;
for (sp = test->streams; sp; sp = sp->next) {
bytes_transferred = (test->reverse ? sp->result->bytes_sent : sp->result->bytes_received);
snprintf(buf, 128, "%d:%llu,%lf,%d,%d\n", sp->id, bytes_transferred, sp->jitter,
sp->cnt_error, sp->packet_count);
size += strlen(buf);
if ((results = realloc(results, size+1)) == NULL) {
perror("realloc results");
return (-1);
}
if (sp == test->streams)
*results = '\0';
strncat(results, buf, size+1);
}
size++;
size = htonl(size);
if (Nwrite(test->ctrl_sck, &size, sizeof(size), Ptcp) < 0) {
perror("Nwrite size");
return (-1);
}
if (Nwrite(test->ctrl_sck, results, ntohl(size), Ptcp) < 0) {
perror("Nwrite results");
return (-1);
}
free(results);
}
return 0;
}
/*************************************************************/
int
parse_results(struct iperf_test *test, char *results)
{
int sid, cerror, pcount;
double jitter;
char *strp;
iperf_size_t bytes_transferred;
struct iperf_stream *sp;
for (strp = results; *strp; strp = strchr(strp, '\n')+1) {
sscanf(strp, "%d:%llu,%lf,%d,%d\n", &sid, &bytes_transferred, &jitter,
&cerror, &pcount);
for (sp = test->streams; sp; sp = sp->next)
if (sp->id == sid) break;
if (sp == NULL) {
fprintf(stderr, "error: No stream with id %d\n", sid);
return (-1);
}
if ((test->role == 'c' && !test->reverse) || (test->role == 's' && test->reverse)) {
sp->jitter = jitter;
sp->cnt_error = cerror;
sp->packet_count = pcount;
sp->result->bytes_received = bytes_transferred;
} else
sp->result->bytes_sent = bytes_transferred;
}
return 0;
}
/*************************************************************/
/**
* add_to_interval_list -- adds new interval to the interval_list
*
*/
void
add_to_interval_list(struct iperf_stream_result * rp, struct iperf_interval_results * new)
{
struct iperf_interval_results *ip = NULL;
ip = (struct iperf_interval_results *) malloc(sizeof(struct iperf_interval_results));
memcpy(ip, new, sizeof(struct iperf_interval_results));
ip->next = NULL;
if (rp->interval_results == NULL) { /* if 1st interval */
rp->interval_results = ip;
rp->last_interval_results = ip; /* pointer to last element in list */
} else { /* add to end of list */
rp->last_interval_results->next = ip;
rp->last_interval_results = ip;
}
}
/*************************************************************/
/* for debugging only */
void
display_interval_list(struct iperf_stream_result * rp, int tflag)
{
struct iperf_interval_results *n;
float gb = 0.;
n = rp->interval_results;
printf("----------------------------------------\n");
while (n != NULL) {
gb = (float) n->bytes_transferred / (1024. * 1024. * 1024.);
printf("Interval = %f\tGBytes transferred = %.3f\n", n->interval_duration, gb);
if (tflag)
print_tcpinfo(n);
n = n->next;
}
}
/************************************************************/
/**
* connect_msg -- displays connection message
* denoting sender/receiver details
*
*/
void
connect_msg(struct iperf_stream * sp)
{
char ipl[512], ipr[512];
inet_ntop(AF_INET, (void *) (&((struct sockaddr_in *) & sp->local_addr)->sin_addr), (void *) ipl, sizeof(ipl));
inet_ntop(AF_INET, (void *) (&((struct sockaddr_in *) & sp->remote_addr)->sin_addr), (void *) ipr, sizeof(ipr));
printf("[%3d] local %s port %d connected to %s port %d\n",
sp->socket,
ipl, ntohs(((struct sockaddr_in *) & sp->local_addr)->sin_port),
ipr, ntohs(((struct sockaddr_in *) & sp->remote_addr)->sin_port));
}
/*************************************************************/
/**
* Display -- Displays results for test
* Mainly for DEBUG purpose
*
*/
void
Display(struct iperf_test * test)
{
int count = 1;
struct iperf_stream *n;
n = test->streams;
printf("===============DISPLAY==================\n");
while (n != NULL) {
if (test->role == 'c') {
printf("position-%d\tsp=%llu\tsocket=%d\tMbytes sent=%u\n",
count++, (uint64_t) n, n->socket, (unsigned int) (n->result->bytes_sent / (float) MB));
} else {
printf("position-%d\tsp=%llu\tsocket=%d\tMbytes received=%u\n",
count++, (uint64_t) n, n->socket, (unsigned int) (n->result->bytes_received / (float) MB));
}
n = n->next;
}
printf("=================END====================\n");
fflush(stdout);
}
/**************************************************************************/
struct iperf_test *
iperf_new_test()
{
struct iperf_test *testp;
testp = (struct iperf_test *) malloc(sizeof(struct iperf_test));
if (!testp) {
perror("malloc");
return (NULL);
}
/* initialize everything to zero */
memset(testp, 0, sizeof(struct iperf_test));
testp->default_settings = (struct iperf_settings *) malloc(sizeof(struct iperf_settings));
memset(testp->default_settings, 0, sizeof(struct iperf_settings));
return testp;
}
/**************************************************************************/
void
iperf_defaults(struct iperf_test * testp)
{
testp->protocol = Ptcp;
testp->duration = DURATION;
testp->server_port = PORT;
testp->ctrl_sck = -1;
testp->new_stream = iperf_new_tcp_stream;
testp->stats_callback = iperf_stats_callback;
testp->reporter_callback = iperf_reporter_callback;
testp->stats_interval = 0;
testp->reporter_interval = 0;
testp->num_streams = 1;
testp->default_settings->unit_format = 'a';
testp->default_settings->socket_bufsize = 0; /* use autotuning */
testp->default_settings->blksize = DEFAULT_TCP_BLKSIZE;
testp->default_settings->rate = RATE; /* UDP only */
testp->default_settings->state = TEST_START;
testp->default_settings->mss = 0;
testp->default_settings->bytes = 0;
memset(testp->default_settings->cookie, 0, COOKIE_SIZE);
}
/**************************************************************************/
int
iperf_create_streams(struct iperf_test *test)
{
struct iperf_stream *sp;
struct sockaddr_in sa;
struct hostent *hent;
int i, s, buf, opt;
for (i = 0; i < test->num_streams; ++i) {
if (test->protocol == Ptcp && (test->no_delay || test->default_settings->mss)) {
if ((hent = gethostbyname(test->server_hostname)) == 0) {
perror("gethostbyname");
return (-1);
}
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return (-1);
}
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
memcpy(&sa.sin_addr.s_addr, hent->h_addr, sizeof(sa.sin_addr.s_addr));
sa.sin_port = htons(test->server_port);
if (test->no_delay) {
opt = 1;
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
perror("setsockopt");
return (-1);
}
}
if (opt = test->default_settings->mss) {
if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
perror("setsockopt");
return (-1);
}
}
if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0 && errno != EINPROGRESS) {
perror("connect tcp stream");
return (-1);
}
} else {
s = netdial(test->protocol, test->server_hostname, test->server_port);
if (s < 0) {
perror("netdial stream");
return -1;
}
}
if (test->protocol == Ptcp) {
if (Nwrite(s, test->default_settings->cookie, COOKIE_SIZE, Ptcp) < 0) {
perror("Nwrite COOKIE\n");
return -1;
}
} else {
if (write(s, &buf, sizeof(i)) < 0) {
perror("write data");
return -1;
}
// XXX: Should this read be TCP instead?
if (read(s, &buf, sizeof(i)) < 0) {
perror("read data");
return -1;
}
}
FD_SET(s, &test->read_set);
FD_SET(s, &test->write_set);
test->max_fd = (test->max_fd < s) ? s : test->max_fd;
// XXX: This doesn't fit our API model!
sp = test->new_stream(test);
sp->socket = s;
iperf_init_stream(sp, test);
iperf_add_stream(test, sp);
connect_msg(sp);
}
return 0;
}
int
iperf_handle_message_client(struct iperf_test *test)
{
int rval;
if ((rval = read(test->ctrl_sck, &test->state, sizeof(char))) <= 0) {
if (rval == 0) {
fprintf(stderr, "The server has unexpectedly closed the connection. Exiting...\n");
exit(1);
} else {
perror("read ctrl_sck");
return -1;
}
}
switch (test->state) {
case PARAM_EXCHANGE:
iperf_exchange_parameters(test);
break;
case CREATE_STREAMS:
iperf_create_streams(test);
break;
case TEST_START:
iperf_init_test(test);
break;
case TEST_RUNNING:
break;
case EXCHANGE_RESULTS:
iperf_exchange_results(test);
break;
case DISPLAY_RESULTS:
iperf_client_end(test);
break;
case IPERF_DONE:
break;
case SERVER_TERMINATE:
fprintf(stderr, "The server has terminated. Exiting...\n");
exit(1);
case ACCESS_DENIED:
fprintf(stderr, "The server is busy running a test. Try again later.\n");
exit(0);
default:
// XXX: This needs to be removed from the production version
printf("How did you get here? test->state = %d\n", test->state);
break;
}
return 0;
}
/* iperf_connect -- client to server connection function */
int
iperf_connect(struct iperf_test *test)
{
printf("Connecting to host %s, port %d\n", test->server_hostname, test->server_port);
FD_ZERO(&test->read_set);
FD_ZERO(&test->write_set);
get_uuid(test->default_settings->cookie);
/* Create and connect the control channel */
test->ctrl_sck = netdial(Ptcp, test->server_hostname, test->server_port);
if (test->ctrl_sck < 0) {
return -1;
}
if (Nwrite(test->ctrl_sck, test->default_settings->cookie, COOKIE_SIZE, Ptcp) < 0) {
perror("Nwrite COOKIE\n");
return -1;
}
FD_SET(test->ctrl_sck, &test->read_set);
FD_SET(test->ctrl_sck, &test->write_set);
test->max_fd = (test->ctrl_sck > test->max_fd) ? test->ctrl_sck : test->max_fd;
return 0;
}
/**************************************************************************/
void
iperf_free_test(struct iperf_test * test)
{
free(test->default_settings);
free_timer(test->timer);
free_timer(test->stats_timer);
free_timer(test->reporter_timer);
test->streams = NULL;
test->accept = NULL;
test->stats_callback = NULL;
test->reporter_callback = NULL;
test->new_stream = NULL;
free(test);
}
/**************************************************************************/
/**
* iperf_stats_callback -- handles the statistic gathering for both the client and server
*
* XXX: This function needs to be updated to reflect the new code
*/
void
iperf_stats_callback(struct iperf_test * test)
{
struct iperf_stream *sp;
struct iperf_stream_result *rp = NULL;
struct iperf_interval_results *ip = NULL, temp;
for (sp = test->streams; sp != NULL; sp = sp->next) {
rp = sp->result;
if (test->role == 'c')
temp.bytes_transferred = rp->bytes_sent_this_interval;
else
temp.bytes_transferred = rp->bytes_received_this_interval;
ip = sp->result->interval_results;
/* result->end_time contains timestamp of previous interval */
if ( ip != NULL ) /* not the 1st interval */
memcpy(&temp.interval_start_time, &sp->result->end_time, sizeof(struct timeval));
else /* or use timestamp from beginning */
memcpy(&temp.interval_start_time, &sp->result->start_time, sizeof(struct timeval));
/* now save time of end of this interval */
gettimeofday(&sp->result->end_time, NULL);
memcpy(&temp.interval_end_time, &sp->result->end_time, sizeof(struct timeval));
temp.interval_duration = timeval_diff(&temp.interval_start_time, &temp.interval_end_time);
//temp.interval_duration = timeval_diff(&temp.interval_start_time, &temp.interval_end_time);
if (test->tcp_info)
get_tcpinfo(test, &temp);
//printf(" iperf_stats_callback: adding to interval list: \n");
add_to_interval_list(rp, &temp);
rp->bytes_sent_this_interval = rp->bytes_received_this_interval = 0;
}
}
/**************************************************************************/
/**
* iperf_reporter_callback -- handles the report printing
*
*/
void
iperf_reporter_callback(struct iperf_test * test)
{
int total_packets = 0, lost_packets = 0;
char ubuf[UNIT_LEN];
char nbuf[UNIT_LEN];
struct iperf_stream *sp = NULL;
iperf_size_t bytes = 0, bytes_sent = 0, bytes_received = 0;
iperf_size_t total_sent = 0, total_received = 0;
double start_time, end_time, avg_jitter;
struct iperf_interval_results *ip = NULL;
switch (test->state) {
case TEST_RUNNING:
case STREAM_RUNNING:
/* print interval results for each stream */
for (sp = test->streams; sp != NULL; sp = sp->next) {
print_interval_results(test, sp);
bytes += sp->result->interval_results->bytes_transferred; /* sum up all streams */
}
if (bytes <=0 ) { /* this can happen if timer goes off just when client exits */
fprintf(stderr, "error: bytes <= 0!\n");
break;
}
/* next build string with sum of all streams */
if (test->num_streams > 1) {
sp = test->streams; /* reset back to 1st stream */
ip = test->streams->result->last_interval_results; /* use 1st stream for timing info */
unit_snprintf(ubuf, UNIT_LEN, (double) (bytes), 'A');
unit_snprintf(nbuf, UNIT_LEN, (double) (bytes / ip->interval_duration),
test->default_settings->unit_format);
start_time = timeval_diff(&sp->result->start_time,&ip->interval_start_time);
end_time = timeval_diff(&sp->result->start_time,&ip->interval_end_time);
printf(report_sum_bw_format, start_time, end_time, ubuf, nbuf);
#if defined(linux) || defined(__FreeBSD__) /* is it usful to figure out a way so sum * TCP_info acrross multiple streams? */
if (test->tcp_info)
print_tcpinfo(ip);
#endif
}
break;
case DISPLAY_RESULTS:
/* print final summary for all intervals */
printf(report_bw_header);
start_time = 0.;
sp = test->streams;
end_time = timeval_diff(&sp->result->start_time, &sp->result->end_time);
for (sp = test->streams; sp != NULL; sp = sp->next) {
bytes_sent = sp->result->bytes_sent;
bytes_received = sp->result->bytes_received;
total_sent += bytes_sent;
total_received += bytes_received;
if (test->protocol == Pudp) {
total_packets += sp->packet_count;
lost_packets += sp->cnt_error;
avg_jitter += sp->jitter;
}
if (bytes_sent > 0) {
unit_snprintf(ubuf, UNIT_LEN, (double) (bytes_sent), 'A');
unit_snprintf(nbuf, UNIT_LEN, (double) (bytes_sent / end_time), test->default_settings->unit_format);
if (test->protocol == Ptcp) {
printf(" Sent\n");
printf(report_bw_format, sp->socket, start_time, end_time, ubuf, nbuf);
#if defined(linux) || defined(__FreeBSD__)
if (test->tcp_info) {
ip = sp->result->last_interval_results;
print_tcpinfo(ip);
}
#endif
} else {
printf(report_bw_jitter_loss_format, sp->socket, start_time,
end_time, ubuf, nbuf, sp->jitter * 1000, sp->cnt_error,
sp->packet_count, (double) (100.0 * sp->cnt_error / sp->packet_count));
if (test->role == 'c') {
printf(report_datagrams, sp->socket, sp->packet_count);
}
if (sp->outoforder_packets > 0)
printf(report_sum_outoforder, start_time, end_time, sp->cnt_error);
}
}
if (bytes_received > 0) {
unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
unit_snprintf(nbuf, UNIT_LEN, (double) (bytes_received / end_time), test->default_settings->unit_format);
if (test->protocol == Ptcp) {
printf(" Received\n");
printf(report_bw_format, sp->socket, start_time, end_time, ubuf, nbuf);
}
}
}
if (test->num_streams > 1) {
unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A');
unit_snprintf(nbuf, UNIT_LEN, (double) total_sent / end_time, test->default_settings->unit_format);
if (test->protocol == Ptcp) {
printf(" Total sent\n");
printf(report_sum_bw_format, start_time, end_time, ubuf, nbuf);
unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A');
unit_snprintf(nbuf, UNIT_LEN, (double) (total_received / end_time), test->default_settings->unit_format);
printf(" Total received\n");
printf(report_sum_bw_format, start_time, end_time, ubuf, nbuf);
} else {
avg_jitter /= test->num_streams;
printf(report_sum_bw_jitter_loss_format, start_time, end_time, ubuf, nbuf, avg_jitter,
lost_packets, total_packets, (double) (100.0 * lost_packets / total_packets));
}
// XXX: Why is this here?
if ((test->print_mss != 0) && (test->role == 'c')) {
printf("The TCP maximum segment size mss = %d\n", getsock_tcp_mss(sp->socket));
}
}
break;
}
}
/**************************************************************************/
void
print_interval_results(struct iperf_test * test, struct iperf_stream * sp)
{
char ubuf[UNIT_LEN];
char nbuf[UNIT_LEN];
double st = 0., et = 0.;
struct iperf_interval_results *ir = NULL;
ir = sp->result->last_interval_results; /* get last entry in linked list */
if (ir == NULL) {
printf("print_interval_results Error: interval_results = NULL \n");
return;
}
if (sp == test->streams) {
printf(report_bw_header);
}
unit_snprintf(ubuf, UNIT_LEN, (double) (ir->bytes_transferred), 'A');
unit_snprintf(nbuf, UNIT_LEN, (double) (ir->bytes_transferred / ir->interval_duration),
test->default_settings->unit_format);
st = timeval_diff(&sp->result->start_time,&ir->interval_start_time);
et = timeval_diff(&sp->result->start_time,&ir->interval_end_time);
printf(report_bw_format, sp->socket, st, et, ubuf, nbuf);
#if defined(linux) || defined(__FreeBSD__)
if (test->tcp_info)
print_tcpinfo(ir);
#endif
}
/**************************************************************************/
void
iperf_free_stream(struct iperf_stream * sp)
{
struct iperf_interval_results *ip, *np;
/* XXX: need to free interval list too! */
free(sp->buffer);
free(sp->settings);
for (ip = sp->result->interval_results; ip; ip = np) {
np = ip->next;
free(ip);
}
free(sp->result);
free(sp->send_timer);
free(sp);
}
/**************************************************************************/
struct iperf_stream *
iperf_new_stream(struct iperf_test *testp)
{
int i;
struct iperf_stream *sp;
sp = (struct iperf_stream *) malloc(sizeof(struct iperf_stream));
if (!sp) {
perror("malloc");
return (NULL);
}
memset(sp, 0, sizeof(struct iperf_stream));
sp->buffer = (char *) malloc(testp->default_settings->blksize);
sp->settings = (struct iperf_settings *) malloc(sizeof(struct iperf_settings));
sp->result = (struct iperf_stream_result *) malloc(sizeof(struct iperf_stream_result));
if (!sp->buffer) {
perror("Malloc sp->buffer");
return NULL;
}
if (!sp->settings) {
perror("Malloc sp->settings");
return NULL;
}
if (!sp->result) {
perror("Malloc sp->result");
return NULL;
}
/* Make a per stream copy of default_settings in each stream structure */
// XXX: These settings need to be moved to the test struct
memcpy(sp->settings, testp->default_settings, sizeof(struct iperf_settings));
/* Randomize the buffer */
srandom(time(0));
for (i = 0; i < testp->default_settings->blksize; ++i)
sp->buffer[i] = random();
sp->socket = -1;
// XXX: Some of this code is needed, even though everything is already zero.
sp->packet_count = 0;
sp->jitter = 0.0;
sp->prev_transit = 0.0;
sp->outoforder_packets = 0;
sp->cnt_error = 0;
sp->send_timer = NULL;
sp->next = NULL;
sp->result->interval_results = NULL;
sp->result->last_interval_results = NULL;
sp->result->bytes_received = 0;
sp->result->bytes_sent = 0;
sp->result->bytes_received_this_interval = 0;
sp->result->bytes_sent_this_interval = 0;
sp->settings->state = STREAM_BEGIN;
return sp;
}
/**************************************************************************/
void
iperf_init_stream(struct iperf_stream * sp, struct iperf_test * testp)
{
socklen_t len;
len = sizeof(struct sockaddr_in);
if (getsockname(sp->socket, (struct sockaddr *) &sp->local_addr, &len) < 0) {
perror("getsockname");
}
if (getpeername(sp->socket, (struct sockaddr *) &sp->remote_addr, &len) < 0) {
perror("getpeername");
}
if (testp->protocol == Ptcp) {
if (set_tcp_windowsize(sp->socket, testp->default_settings->socket_bufsize,
testp->role == 's' ? SO_RCVBUF : SO_SNDBUF) < 0)
fprintf(stderr, "unable to set window size\n");
/* set TCP_NODELAY and TCP_MAXSEG if requested */
set_tcp_options(sp->socket, testp->no_delay, testp->default_settings->mss);
}
}
/**************************************************************************/
int
iperf_add_stream(struct iperf_test * test, struct iperf_stream * sp)
{
int i;
struct iperf_stream *n;
if (!test->streams) {
test->streams = sp;
sp->id = 1;
return 1;
} else {
for (n = test->streams, i = 2; n->next; n = n->next, ++i);
n->next = sp;
sp->id = i;
return 1;
}
return 0;
}
/**************************************************************************/
int
iperf_client_end(struct iperf_test *test)
{
struct iperf_stream *sp, *np;
printf("Test Complete. Summary Results:\n");
/* show final summary */
test->reporter_callback(test);
/* Deleting all streams - CAN CHANGE FREE_STREAM FN */
for (sp = test->streams; sp; sp = np) {
close(sp->socket);
np = sp->next;
iperf_free_stream(sp);
}
test->state = IPERF_DONE;
if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
perror("Nwrite IPERF_DONE");
return -1;
}
return 0;
}
void
sig_handler(int sig)
{
longjmp(env, 1);
}
int
iperf_run_client(struct iperf_test * test)
{
int result;
fd_set temp_read_set, temp_write_set;
struct timeval tv;
/* Start the client and connect to the server */
if (iperf_connect(test) < 0) {
// set error and return
return -1;
}
signal(SIGINT, sig_handler);
if (setjmp(env)) {
fprintf(stderr, "Exiting...\n");
test->state = CLIENT_TERMINATE;
if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
fprintf(stderr, "Unable to send CLIENT_TERMINATE message to server\n");
}
exit(1);
}
while (test->state != IPERF_DONE) {
memcpy(&temp_read_set, &test->read_set, sizeof(fd_set));
memcpy(&temp_write_set, &test->write_set, sizeof(fd_set));
tv.tv_sec = 15;
tv.tv_usec = 0;
result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv);
if (result < 0 && errno != EINTR) {
perror("select");
exit(1);
} else if (result > 0) {
if (FD_ISSET(test->ctrl_sck, &temp_read_set)) {
iperf_handle_message_client(test);
FD_CLR(test->ctrl_sck, &temp_read_set);
}
if (test->state == TEST_RUNNING) {
if (test->reverse) {
// Reverse mode. Client receives.
iperf_recv(test);
} else {
// Regular mode. Client sends.
iperf_send(test);
}
/* Perform callbacks */
if (timer_expired(test->stats_timer)) {
test->stats_callback(test);
update_timer(test->stats_timer, test->stats_interval, 0);
}
if (timer_expired(test->reporter_timer)) {
test->reporter_callback(test);
update_timer(test->reporter_timer, test->reporter_interval, 0);
}
/* Send TEST_END if all data has been sent or timer expired */
if (all_data_sent(test) || timer_expired(test->timer)) {
test->stats_callback(test);
test->state = TEST_END;
if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
perror("Nwrite TEST_END");
return -1;
}
}
}
}
}
return 0;
}