Plan 9 from Bell Labs’s /usr/web/sources/contrib/oraccha/iperf3/iperf_api.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.



/*
 * 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 "uuid.h"
#include "locale.h"

jmp_buf   env;			/* to handle longjmp on signal */

#ifndef socklen_t
typedef int socklen_t;
#endif

/*************************************************************/

/*
 * check to see if client has sent the requested number of bytes to the
 * server yet
 */

/*
 * XXX: should probably just compute this as we go and store it in the
 * iperf_test structure -blt
 */
int
all_data_sent(struct iperf_test * test)
{
    if (test->default_settings->bytes == 0)
	return 0;
    else
    {
	uint64_t  total_bytes = 0;
	struct iperf_stream *sp;

	sp = test->streams;

	while (sp)
	{
	    total_bytes += sp->result->bytes_sent;
	    sp = sp->next;
	}

	if (total_bytes >= (test->num_streams * test->default_settings->bytes))
	{
	    return 1;
	} else
	    return 0;
    }

}

/*********************************************************/

/**
 * exchange_parameters - handles the param_Exchange part for client
 *
 */

void
exchange_parameters(struct iperf_test * test)
{
    int       result;
    struct iperf_stream *sp;
    struct param_exchange *param;

    //printf("in exchange_parameters \n");
    sp = test->streams;
    sp->settings->state = PARAM_EXCHANGE;
    param = (struct param_exchange *) sp->buffer;

    get_uuid(test->default_settings->cookie);
    strncpy(param->cookie, test->default_settings->cookie, COOKIE_SIZE);
    //printf("client cookie: %s \n", param->cookie);

    /* setting up exchange parameters  */
    param->state = PARAM_EXCHANGE;
    param->blksize = test->default_settings->blksize;
    param->recv_window = test->default_settings->socket_bufsize;
    param->send_window = test->default_settings->socket_bufsize;
    param->format = test->default_settings->unit_format;

    //printf(" sending exchange params: size = %d \n", (int) sizeof(struct param_exchange));
    result = sp->snd(sp);
    if (result < 0)
	perror("Error sending exchange params to server");
    result = Nread(sp->socket, sp->buffer, sizeof(struct param_exchange), Ptcp);

    if (result < 0)
	perror("Error getting exchange params ack from server");
    if (result > 0 && sp->buffer[0] == ACCESS_DENIED)
    {
	fprintf(stderr, "Busy server Detected. Try again later. Exiting.\n");
	exit(-1);
    }
    return;
}

/*************************************************************/
/**
 * 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;
    }
}

/************************************************************/

/**
 * receive_result_from_server - Receives result from server
 */

void
receive_result_from_server(struct iperf_test * test)
{
    int       result;
    struct iperf_stream *sp;
    int       size = 0;
    char     *buf = NULL;

    printf("in receive_result_from_server \n");
    sp = test->streams;
    size = MAX_RESULT_STRING;

    buf = (char *) malloc(size);

    printf("receive_result_from_server: send ALL_STREAMS_END to server \n");
    sp->settings->state = ALL_STREAMS_END;
    sp->snd(sp);		/* send message to server */

    printf("receive_result_from_server: send RESULT_REQUEST to server \n");
    sp->settings->state = RESULT_REQUEST;
    sp->snd(sp);		/* send message to server */

    /* receive from server */

    printf("reading results (size=%d) back from server \n", size);
    do
    {
	result = recv(sp->socket, buf, size, 0);
    } while (result == -1 && errno == EINTR);
    printf("Got size of results from server: %d \n", result);

    printf(server_reporting, sp->socket);
    puts(buf);			/* prints results */
    free(buf);

}

/*************************************************************/

/**
 * connect_msg -- displays connection message
 * denoting sender/receiver details
 *
 */

void
connect_msg(struct iperf_stream * sp)
{
    char      ipl[512], ipr[512];
#if 0
    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));
#else
    char	*ip;

    ip = inet_ntoa(sp->local_addr.sin_addr);
    memcpy(ipl, ip, sizeof(ipl));
    ip = inet_ntoa(sp->remote_addr.sin_addr);
    memcpy(ipr, ip, sizeof(ipr));
    /* XXX: sin_ports are not network byte order!? */
    printf("[%3d] local %s port %d connected to %s port %d\n",
	   sp->socket,
	   ipl, ((struct sockaddr_in *) & sp->local_addr)->sin_port,
	   ipr, ((struct sockaddr_in *) & sp->remote_addr)->sin_port);
#endif
}

/*************************************************************/
/**
 * Display -- Displays results for test
 * Mainly for DEBUG purpose
 *
 */

void
Display(struct iperf_test * test)
{
    struct iperf_stream *n;

    n = test->streams;
    int       count = 1;

    printf("===============DISPLAY==================\n");

    while (n != NULL)
    {
	 if (test->role == 'c')
		printf("position-%d\tsp=%d\tsocket=%d\tMbytes sent=%u\n", count++, (int) n, n->socket, (unsigned int) (n->result->bytes_sent / (float)MB));
	 else
		printf("position-%d\tsp=%d\tsocket=%d\tMbytes received=%u\n", count++, (int) 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;

    //printf("in iperf_new_test: reinit default settings \n");
    testp = (struct iperf_test *) malloc(sizeof(struct iperf_test));
    if (!testp)
    {
	perror("malloc");
	return (NULL);
    }
    /* initialise 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 an empty iperf_test* with memory alloted. */
    return testp;
}

/**************************************************************************/
void
iperf_defaults(struct iperf_test * testp)
{
    testp->protocol = Ptcp;
    testp->role = 's';
    testp->duration = DURATION;
    testp->server_port = PORT;

    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);
}

/**************************************************************************/

void
iperf_init_test(struct iperf_test * test)
{
    char      ubuf[UNIT_LEN];
    struct iperf_stream *sp;
    int       i, s = 0;

    //printf("in iperf_init_test \n");
    if (test->role == 's')
    {				/* server */
	if (test->protocol == Pudp)
	{
	    test->listener_sock_udp = netannounce(Pudp, NULL, test->server_port);
	    if (test->listener_sock_udp < 0)
		exit(0);
	}
	/* always create TCP connection for control messages */
	test->listener_sock_tcp = netannounce(Ptcp, NULL, test->server_port);
	if (test->listener_sock_tcp < 0)
	    exit(0);

	if (test->protocol == Ptcp)
	{
	    if (set_tcp_windowsize(test->listener_sock_tcp, test->default_settings->socket_bufsize, SO_RCVBUF) < 0)
		perror("unable to set TCP window");
	}
	/* make sure that accept call does not block */
	setnonblocking(test->listener_sock_tcp);
	setnonblocking(test->listener_sock_udp);

	printf("-----------------------------------------------------------\n");
	printf("Server listening on %d\n", test->server_port);
	int       x;

	/* make sure we got what we asked for */
	if ((x = get_tcp_windowsize(test->listener_sock_tcp, SO_RCVBUF)) < 0)
	    perror("SO_RCVBUF");

	if (test->protocol == Ptcp)
	{
	    {
		if (test->default_settings->socket_bufsize > 0)
		{
		    unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A');
		    printf("TCP window size: %s\n", ubuf);
		} else
		{
		    printf("Using TCP Autotuning \n");
		}
	    }
	}
	printf("-----------------------------------------------------------\n");

    } else if (test->role == 'c')
    {				/* Client */
	FD_ZERO(&test->write_set);
	FD_SET(s, &test->write_set);

	/*
         * XXX: I think we need to create a TCP control socket here too for
         * UDP mode -blt
         */
	for (i = 0; i < test->num_streams; i++)
	{
	    s = netdial(test->protocol, test->server_hostname, test->server_port);
	    if (s < 0)
	    {
		fprintf(stderr, "netdial failed\n");
		exit(0);
	    }
	    FD_SET(s, &test->write_set);
	    test->max_fd = (test->max_fd < s) ? s : test->max_fd;

	    sp = test->new_stream(test);
	    sp->socket = s;
	    iperf_init_stream(sp, test);
	    iperf_add_stream(test, sp);

	    connect_msg(sp);	/* print connection established message */
	}
    }
}

/**************************************************************************/
void
iperf_free_test(struct iperf_test * test)
{
    free(test->default_settings);

    close(test->listener_sock_tcp);
    close(test->listener_sock_udp);

    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
 *
 *returns void *
 *
 */


void     *
iperf_stats_callback(struct iperf_test * test)
{
    struct iperf_stream *sp = test->streams;
    struct iperf_stream_result *rp = NULL;
    struct iperf_interval_results *ip = NULL, temp;

    //printf("in stats_callback: num_streams = %d role = %c\n", test->num_streams, test->role);

    while (sp != NULL)
    {
	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;

	/* for debugging */
	//display_interval_list(rp, test->tcp_info); 
	sp = sp->next;
    }				/* for each stream */

    return 0;
}

/**************************************************************************/

/**
 * iperf_reporter_callback -- handles the report printing
 *
 *returns report
 *
 */

char     *
iperf_reporter_callback(struct iperf_test * test)
{
    int       total_packets = 0, lost_packets = 0, curr_state = 0;
    char     *message = NULL;
    char     *message_final = NULL;
    char      ubuf[UNIT_LEN];
    char      nbuf[UNIT_LEN];
    struct iperf_stream *sp = NULL;
    iperf_size_t bytes = 0, total_bytes = 0;
    double    start_time, end_time;
    struct iperf_interval_results *ip = NULL;

    message = (char *)calloc(MAX_RESULT_STRING, sizeof(char));
    message_final = (char *)calloc(MAX_RESULT_STRING, sizeof(char));

    sp = test->streams;
    curr_state = sp->settings->state;
    //printf("in iperf_reporter_callback: state = %d \n", curr_state);

    if (curr_state == TEST_RUNNING || curr_state == STREAM_RUNNING)
    {
	/* print interval results for each stream */
	while (sp)
	{
	    message_final = print_interval_results(test, sp, message_final);
            bytes += sp->result->interval_results->bytes_transferred; /* sum up all streams */
	    sp = sp->next;
	}
        if (bytes <=0 )  /* this can happen if timer goes off just when client exits */
	     return NULL;

	/* next build string with sum of all streams */
        sp = test->streams; /* reset back to 1st stream */
	if (test->num_streams > 1)
	{
	    ip = test->streams->result->last_interval_results;	/* use 1st stream for timing info */
	    unit_snprintf(ubuf, UNIT_LEN, (double) (bytes), 'A');

            start_time = timeval_diff(&sp->result->start_time,&ip->interval_start_time);
            end_time = timeval_diff(&sp->result->start_time,&ip->interval_end_time);
            unit_snprintf(nbuf, UNIT_LEN, (double) (bytes / ip->interval_duration),
			      test->default_settings->unit_format);
	    sprintf(message, report_sum_bw_format, start_time, end_time, ubuf, nbuf);
            //printf("iperf_reporter_callback 1: start_time: %.3f  end_time: %.3f \n", start_time, end_time);
            //printf("iperf_reporter_callback 1: message = %s \n", message);
	    safe_strcat(message_final, message);

#ifdef NOT_DONE			/* is it usful to figure out a way so sum
				 * TCP_info acrross multiple streams? */
	    if (test->tcp_info)
	    {
		build_tcpinfo_message(ip, message);
		safe_strcat(message_final, message);
	    }
#endif
	}
    } else
    {
	/* print final summary for all intervals */
	if (curr_state == ALL_STREAMS_END || curr_state == RESULT_REQUEST)
	{
	    sp = test->streams;
	    start_time = 0.;
	    end_time = timeval_diff(&sp->result->start_time, &sp->result->end_time);

	    while (sp)
	    {
		if (test->role == 'c')
		    bytes = sp->result->bytes_sent;
		else
		    bytes = sp->result->bytes_received;

                total_bytes += bytes;
		if (test->protocol == Pudp)
		{
		    total_packets += sp->packet_count;
		    lost_packets += sp->cnt_error;
		}

                if (bytes > 0 ) 
	        {
		    unit_snprintf(ubuf, UNIT_LEN, (double) (bytes), 'A');
		    unit_snprintf(nbuf, UNIT_LEN, (double) (bytes / end_time), test->default_settings->unit_format);

		    if (test->protocol == Ptcp)
		    {
		        sprintf(message, report_bw_format, sp->socket, start_time, end_time, ubuf, nbuf);
                        //printf("iperf_reporter_callback 2: message = %s \n", message);
		        safe_strcat(message_final, message);
#if defined(linux) || defined(__FreeBSD__)
		        if (test->tcp_info)
		        {
			    //printf("Final TCP_INFO results: \n");
	                    ip = sp->result->last_interval_results;	
			    build_tcpinfo_message(ip, message);
			    safe_strcat(message_final, message);
		        }
#endif
		    } else
		    {		/* UDP mode */
		        sprintf(message, 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));
		        safe_strcat(message_final, message);
    
		        if (test->role == 'c')
		        {
			    sprintf(message, report_datagrams, sp->socket, sp->packet_count);
			    safe_strcat(message_final, message);
		        }
		        if (sp->outoforder_packets > 0)
			    printf(report_sum_outoforder, start_time, end_time, sp->cnt_error);
		    }
		}
		sp = sp->next;
	    }
	}			/* while (sp) */

	unit_snprintf(ubuf, UNIT_LEN, (double) total_bytes, 'A');
	unit_snprintf(nbuf, UNIT_LEN, (double) total_bytes / end_time, test->default_settings->unit_format);

	if ((test->role == 'c' || (test->role == 's')) && test->num_streams > 1)
	{
	    if (test->protocol == Ptcp)
	    {
		sprintf(message, report_sum_bw_format, start_time, end_time, ubuf, nbuf);
		safe_strcat(message_final, message);
	    } else
	    {
		sprintf(message, report_sum_bw_jitter_loss_format, start_time, end_time, ubuf, nbuf, sp->jitter, lost_packets, total_packets, (double) (100.0 * lost_packets / total_packets));
		safe_strcat(message_final, message);
	    }


	    if ((test->print_mss != 0) && (test->role == 'c'))
	    {
		sprintf(message, "\nThe TCP maximum segment size mss = %d \n", getsock_tcp_mss(sp->socket));
		safe_strcat(message_final, message);
	    }
	}
    }
    free(message);
    return message_final;
}

/**************************************************************************/
char     *
print_interval_results(struct iperf_test * test, struct iperf_stream * sp, char *message_final)
{
    static int       first_stream = 1;
    char      ubuf[UNIT_LEN];
    char      nbuf[UNIT_LEN];
    double    st = 0., et = 0.;
    struct iperf_interval_results *ir = NULL;
    char     *message = (char *) malloc(MAX_RESULT_STRING);

    //printf("in print_interval_results for stream %d \n", sp->socket);
    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 NULL;
    }
    if (first_stream)		/* only print header for 1st stream */
    {
	sprintf(message, report_bw_header);
	safe_strcat(message_final, message);
	first_stream = 0;
    }
    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);
    sprintf(message, report_bw_format, sp->socket, st, et, ubuf, nbuf);
    //printf("print_interval_results 1: message = %s \n", message);
    safe_strcat(message_final, message);

#if defined(linux) || defined(__FreeBSD__)
    if (test->tcp_info)
    {
	build_tcpinfo_message(ir, message);
	safe_strcat(message_final, message);
    }
#endif
    //printf("reporter_callback: built interval string: %s \n", message_final);
    free(message);
    return message_final;
}

/**************************************************************************/
void
safe_strcat(char *s1, char *s2)
{
    //printf(" adding string %s to end of string %s \n", s1, s1);
    if (strlen(s1) + strlen(s2) < MAX_RESULT_STRING)
	strcat(s1, s2);
    else
    {
	printf("Error: results string too long \n");
	exit(-1);		/* XXX: should return an error instead! */
	/* but code that calls this needs to check for error first */
	//return -1;
    }
}

/**************************************************************************/
void
iperf_free_stream(struct iperf_stream * sp)
{
    /* XXX: need to free interval list too! */
    free(sp->buffer);
    free(sp->settings);
    free(sp->result);
    free(sp->send_timer);
    free(sp);
}

/**************************************************************************/
struct iperf_stream *
iperf_new_stream(struct iperf_test * testp)
{
    int       i = 0;
    struct iperf_stream *sp;

    //printf("in iperf_new_stream \n");
    sp = (struct iperf_stream *) malloc(sizeof(struct iperf_stream));
    if (!sp)
    {
	perror("malloc");
	return (NULL);
    }
    memset(sp, 0, sizeof(struct iperf_stream));

    //printf("iperf_new_stream: Allocating new stream buffer: size = %d \n", testp->default_settings->blksize);
    sp->buffer = (char *) malloc(testp->default_settings->blksize);
    sp->settings = (struct iperf_settings *) malloc(sizeof(struct iperf_settings));
    /* make a per stream copy of default_settings in each stream structure */
    memcpy(sp->settings, testp->default_settings, sizeof(struct iperf_settings));
    sp->result = (struct iperf_stream_result *) malloc(sizeof(struct iperf_stream_result));

    /* fill in buffer with random stuff */
    srand(time(0));
    for (i = 0; i < testp->default_settings->blksize; i++)
	sp->buffer[i] = rand();

    sp->socket = -1;

    sp->packet_count = 0;
    sp->stream_id = (int) sp;
    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;
    gettimeofday(&sp->result->start_time, NULL);

    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");
    }
    //printf("in init_stream: calling set_tcp_windowsize: %d \n", testp->default_settings->socket_bufsize);
    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)
{
    struct iperf_stream *n;

    if (!test->streams)
    {
	test->streams = sp;
	return 1;
    } else
    {
	n = test->streams;
	while (n->next)
	    n = n->next;
	n->next = sp;
	return 1;
    }

    return 0;
}


/**************************************************************************/
void
catcher(int sig)
{
    longjmp(env, sig);
}

/**************************************************************************/
void
iperf_run_client(struct iperf_test * test)
{
    int       i, result = 0;
    struct iperf_stream *sp, *np;
    struct timer *timer, *stats_interval, *reporter_interval;
    char     *result_string = NULL;
    char     *prot = NULL;
    int64_t   delayus, adjustus, dtargus;
    struct timeval tv;
    struct sigaction sact;

    //printf("in iperf_run_client \n");
    tv.tv_sec = 15;		/* timeout interval in seconds */
    tv.tv_usec = 0;

    sigemptyset(&sact.sa_mask);
    sact.sa_flags = 0;
    sact.sa_handler = catcher;
    sigaction(SIGINT, &sact, NULL);

    if (test->protocol == Pudp)
    {
	dtargus = (int64_t) (test->default_settings->blksize) * SEC_TO_US * 8;
	dtargus /= test->default_settings->rate;

	assert(dtargus != 0);

	delayus = dtargus;
	adjustus = 0;
	printf("iperf_run_client: adjustus: %lld, delayus: %lld \n", adjustus, delayus);

	sp = test->streams;
	for (i = 0; i < test->num_streams; i++)
	{
	    sp->send_timer = new_timer(0, dtargus);
	    sp = sp->next;
	}
    }
    /* if -n specified, set zero timer */
    if (test->default_settings->bytes == 0)
	timer = new_timer(test->duration, 0);
    else
	timer = new_timer(0, 0);

    if (test->stats_interval != 0)
	stats_interval = new_timer(test->stats_interval, 0);

    if (test->reporter_interval != 0)
	reporter_interval = new_timer(test->reporter_interval, 0);

    if (test->protocol == Pudp)
	prot = "UDP";
    else
	prot = "TCP";
    if (test->default_settings->bytes == 0)
	printf("Starting Test: protocol: %s, %d streams, %d byte blocks, %d second test \n",
	       prot, test->num_streams, test->default_settings->blksize, test->duration);
    else
	printf("Starting Test: protocol: %s, %d streams, %d byte blocks, %d bytes to send\n",
	       prot, test->num_streams, test->default_settings->blksize, (int) test->default_settings->bytes);

    /* send data till the timer expires or bytes sent */
    while (!all_data_sent(test) && !timer->expired(timer))
    {
	sp = test->streams;
	for (i = 0; i < test->num_streams; i++)
	{
	    //printf("sending data to stream %d \n", i);
	    result += sp->snd(sp);

	    if (sp->next == NULL)
		break;
	    sp = sp->next;
	}


	if ((test->stats_interval != 0) && stats_interval->expired(stats_interval))
	{
	    test->stats_callback(test);
	    update_timer(stats_interval, test->stats_interval, 0);
	}
	if ((test->reporter_interval != 0) && reporter_interval->expired(reporter_interval))
	{
	    result_string = test->reporter_callback(test);
	    //printf("interval expired: printing results: \n");
	    puts(result_string);
	    update_timer(reporter_interval, test->reporter_interval, 0);
	    free(result_string);
	}
	/* detecting Ctrl+C */
	if (setjmp(env))
	    break;

    }				/* while outer timer  */
    /* show last interval if necessary */
    // I think this is un-necesary, but maybe needed with -n option? 
    //test->stats_callback(test); /* dont need this I think .. */
    //result_string = test->reporter_callback(test);
    //puts(result_string);


    printf("Test Complete. Summary Results:\n");
    /* send STREAM_END packets */
    np = test->streams;
    do
    {				/* send STREAM_END to all sockets */
	sp = np;
	sp->settings->state = STREAM_END;
	//printf("sending state = STREAM_END to stream %d \n", sp->socket);
	result = sp->snd(sp);
	if (result < 0)
	    break;
	np = sp->next;
    } while (np);
    //printf("Done Sending STREAM_END. \n");

    /* send ALL_STREAMS_END packet to 1st socket */
    sp = test->streams;
    sp->settings->state = ALL_STREAMS_END;
    sp->snd(sp);
    //printf("Done Sending ALL_STREAMS_END. \n");

    /* show final summary */
    /* XXX: check to make sure this is needed */
    test->stats_callback(test);
    result_string = test->reporter_callback(test);
    puts(result_string);
    free(result_string);

    /* Requesting for result from Server */
    test->default_settings->state = RESULT_REQUEST;
    //receive_result_from_server(test);	/* XXX: currently broken! */
    //result_string = test->reporter_callback(test);
    //printf("Summary results as measured by the server: \n");
    //puts(result_string);

    //printf("Done getting/printing results. \n");

    //printf("send TEST_END to server \n");
    sp->settings->state = TEST_END;
    sp->snd(sp);		/* send message to server */

    /* Deleting all streams - CAN CHANGE FREE_STREAM FN */
    sp = test->streams;
    np = sp;
    do
    {
	sp = np;
	close(sp->socket);
	np = sp->next;
	iperf_free_stream(sp);
    } while (np);

    if (test->stats_interval != 0)
	free_timer(stats_interval);
    if (test->reporter_interval != 0)
	free_timer(reporter_interval);
    free_timer(timer);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].