C++程序  |  4870行  |  149.3 KB

#ifndef lint
char	nettest_sctp[]="\
@(#)nettest_sctp.c (c) Copyright 2005-2007 Hewlett-Packard Co. Version 2.4.3";
#else
#define DIRTY
#define WANT_HISTOGRAM
#define WANT_INTERVALS
#endif /* lint */

/****************************************************************/
/*								*/
/*	nettest_sctp.c						*/
/*								*/
/*                                                              */
/*      scan_sctp_args()        get the sctp command line args  */
/*                                                              */
/*	the actual test routines...				*/
/*								*/
/*	send_sctp_stream()	perform a sctp stream test	*/
/*	recv_sctp_stream()					*/
/*	send_sctp_rr()		perform a sctp request/response	*/
/*	recv_sctp_rr()						*/
/*	send_sctp_stream_udp()	perform a sctp request/response	*/
/*	recv_sctp_stream_upd()	using UDP style API		*/
/*	send_sctp_rr_udp()	perform a sctp request/response	*/
/*	recv_sctp_rr_upd()	using UDP style API		*/
/*								*/
/*      relies on create_data_socket in nettest_bsd.c           */
/****************************************************************/

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if defined(WANT_SCTP)
     
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef NOSTDLIBH
#include <malloc.h>
#else /* NOSTDLIBH */
#include <stdlib.h>
#endif /* NOSTDLIBH */

#if !defined(__VMS)
#include <sys/ipc.h>
#endif /* !defined(__VMS) */
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <netdb.h>

/* would seem that not all sctp.h files define a MSG_EOF, but that
   MSG_EOF can be the same as MSG_FIN so lets work with that
   assumption.  initial find by Jon Pedersen. raj 2006-02-01 */
#ifndef MSG_EOF
#ifdef MSG_FIN
#define MSG_EOF MSG_FIN
#else
#error Must have either MSG_EOF or MSG_FIN defined
#endif
#endif 

#include "netlib.h"
#include "netsh.h"
/* get some of the functions from nettest_bsd.c */
#include "nettest_bsd.h"
#include "nettest_sctp.h"

#ifdef WANT_HISTOGRAM
#ifdef __sgi
#include <sys/time.h>
#endif /* __sgi */
#include "hist.h"
#endif /* WANT_HISTOGRAM */

#ifdef WANT_FIRST_BURST
extern int first_burst_size;
#endif /* WANT_FIRST_BURST */



/* these variables are specific to SCTP tests. declare */
/* them static to make them global only to this file. */

static int	
  msg_count = 0,	/* number of messages to transmit on association */
  non_block = 0,	/* default to blocking sockets */
  num_associations = 1; /* number of associations on the endpoint */

static  int confidence_iteration;
static  char  local_cpu_method;
static  char  remote_cpu_method;

#ifdef WANT_HISTOGRAM
static struct timeval time_one;
static struct timeval time_two;
static HIST time_hist;
#endif /* WANT_HISTOGRAM */


char sctp_usage[] = "\n\
Usage: netperf [global options] -- [test options] \n\
\n\
SCTP Sockets Test Options:\n\
    -b number         Send number requests at the start of _RR tests\n\
    -D [L][,R]        Set SCTP_NODELAY locally and/or remotely\n\
    -h                Display this text\n\
    -H name,fam       Use name (or IP) and family as target of data connection\n\
    -L name,fam       Use name (or IP) and family as source of data connextion\n\
    -m bytes          Set the size of each sent message\n\
    -M bytes          Set the size of each received messages\n\
    -P local[,remote] Set the local/remote port for the data socket\n\
    -r req,[rsp]      Set request/response sizes (_RR tests)\n\
    -s send[,recv]    Set local socket send/recv buffer sizes\n\
    -S send[,recv]    Set remote socket send/recv buffer sizes\n\
    -V 		      Enable copy avoidance if supported\n\
    -N number	      Specifies the number of messages to send (_STREAM tests)\n\
    -B		      run the test in non-blocking mode\n\
    -T number	      Number of associations to create (_MANY tests)\n\
    -4                Use AF_INET (eg IPv4) on both ends of the data conn\n\
    -6                Use AF_INET6 (eg IPv6) on both ends of the data conn\n\
\n\
For those options taking two parms, at least one must be specified;\n\
specifying one value without a comma will set both parms to that\n\
value, specifying a value with a leading comma will set just the second\n\
parm, a value with a trailing comma will set just the first. To set\n\
each parm to unique values, specify both and separate them with a\n\
comma.\n"; 
     

 /* This routine is intended to retrieve interesting aspects of tcp */
 /* for the data connection. at first, it attempts to retrieve the */
 /* maximum segment size. later, it might be modified to retrieve */
 /* other information, but it must be information that can be */
 /* retrieved quickly as it is called during the timing of the test. */
 /* for that reason, a second routine may be created that can be */
 /* called outside of the timing loop */
static
void
get_sctp_info(socket, mss)
     int socket;
     int *mss;
{

  int sock_opt_len;

  if (sctp_opt_info(socket,
		    0,
		    SCTP_MAXSEG,
		    mss,
		    &sock_opt_len) < 0) {
    lss_size = -1;
  }
}


static
void
sctp_enable_events(socket, ev_mask)
    int socket;
    int ev_mask;
{
    struct sctp_event_subscribe ev;

    bzero(&ev, sizeof(ev));

    if (ev_mask & SCTP_SNDRCV_INFO_EV)
	ev.sctp_data_io_event = 1;

    if (ev_mask & SCTP_ASSOC_CHANGE_EV)
	ev.sctp_association_event = 1;

    if (ev_mask & SCTP_PEERADDR_CHANGE_EV)
	ev.sctp_address_event = 1;

    if (ev_mask & SCTP_SND_FAILED_EV)
	ev.sctp_send_failure_event = 1;

    if (ev_mask & SCTP_REMOTE_ERROR_EV)
	ev.sctp_peer_error_event = 1;

    if (ev_mask & SCTP_SHUTDOWN_EV)
	ev.sctp_shutdown_event = 1;

    if (ev_mask & SCTP_PD_EV)
	ev.sctp_partial_delivery_event = 1;

    if (ev_mask & SCTP_ADAPT_EV)
#ifdef HAVE_SCTP_ADAPTATION_LAYER_EVENT
	ev.sctp_adaptation_layer_event = 1;
#else
	ev.sctp_adaption_layer_event = 1;
#endif

    if (setsockopt(socket,
		   IPPROTO_SCTP,
#ifdef SCTP_EVENTS
		   SCTP_EVENTS,
#else
		   SCTP_SET_EVENTS,
#endif
		   (const char*)&ev,
		   sizeof(ev)) != 0 ) {
      fprintf(where,
	      "sctp_enable_event: could not set sctp events errno %d\n",
	      errno);
      fflush(where);
      exit(1);
    }
}


static
sctp_disposition_t
sctp_process_event(socket, buf)
    int socket;
    void *buf;
{

    struct sctp_assoc_change *sac;
    struct sctp_send_failed *ssf;
    struct sctp_paddr_change *spc;
    struct sctp_remote_error *sre;
    union sctp_notification *snp;

    snp = buf;

    switch (snp->sn_header.sn_type) {
    case SCTP_ASSOC_CHANGE:
	if (debug) {
	    fprintf(where, "\tSCTP_ASSOC_CHANGE event, type:");
	    fflush(where);
	}
	sac = &snp->sn_assoc_change;
	switch (sac->sac_type) {
	    case SCTP_COMM_UP:
		if (debug) {
		    fprintf(where, "  SCTP_COMM_UP\n");
		    fflush(where);
		}
		break;
	    case SCTP_RESTART:
		if (debug) {
		    fprintf(where, "  SCTP_RESTART\n");
		    fflush(where);
		}
		break;
	    case SCTP_CANT_STR_ASSOC:
		if (debug) {
		    fprintf(where, "  SCTP_CANT_STR_ASSOC\n");
		    fflush(where);
		}
		break;	/* FIXME ignore above status changes */
	    case SCTP_COMM_LOST:
		if (debug) {
		    fprintf(where, "  SCTP_COMM_LOST\n");
		    fflush(where);
		}
		return SCTP_CLOSE;
	    case SCTP_SHUTDOWN_COMP:
		if (debug) {
		    fprintf(where, "  SCTP_SHUTDOWN_COMPLETE\n");
		    fflush(where);
		}
		return SCTP_CLOSE;
		break;
	}

    case SCTP_SEND_FAILED:
	if (debug) {
	    fprintf(where, "\tSCTP_SEND_FAILED event\n");
	    fflush(where);
	}
	ssf = &snp->sn_send_failed;
	break;  /* FIXME ??? ignore this for now */

    case SCTP_PEER_ADDR_CHANGE:
	if (debug) {
	    fprintf(where, "\tSCTP_PEER_ADDR_CHANGE event\n");
	    fflush(where);
	}
	spc = &snp->sn_paddr_change;
	break;	/* FIXME ??? ignore this for now */

    case SCTP_REMOTE_ERROR:
	if (debug) {
	    fprintf(where, "\tSCTP_REMOTE_ERROR event\n");
	    fflush(where);
	}
	sre = &snp->sn_remote_error;
	break;	/* FIXME ??? ignore this for now */
    case SCTP_SHUTDOWN_EVENT:
	if (debug) {
	    fprintf(where, "\tSCTP_SHUTDOWN event\n");
	    fflush(where);
	}
	return SCTP_CLOSE;
    default:
	fprintf(where, "unknown type: %hu\n", snp->sn_header.sn_type);
	fflush(where);
	break;
    }
    return SCTP_OK;
}



/* This routine implements the SCTP unidirectional data transfer test */
/* (a.k.a. stream) for the sockets interface. It receives its */
/* parameters via global variables from the shell and writes its */
/* output to the standard output. */


void 
send_sctp_stream(remote_host)
char	remote_host[];
{
  
  char *tput_title = "\
Recv   Send    Send                          \n\
Socket Socket  Message  Elapsed              \n\
Size   Size    Size     Time     Throughput  \n\
bytes  bytes   bytes    secs.    %s/sec  \n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1 =
    "%6d %6d %6d    %-6.2f   %7.2f   \n";
  
  char *cpu_title = "\
Recv   Send    Send                          Utilization       Service Demand\n\
Socket Socket  Message  Elapsed              Send     Recv     Send    Recv\n\
Size   Size    Size     Time     Throughput  local    remote   local   remote\n\
bytes  bytes   bytes    secs.    %-8.8s/s  %% %c      %% %c      us/KB   us/KB\n\n";
  
  char *cpu_fmt_0 =
    "%6.3f %c\n";

  char *cpu_fmt_1 =
    "%6d %6d %6d    %-6.2f     %7.2f   %-6.2f   %-6.2f   %-6.3f  %-6.3f\n";
  
  char *ksink_fmt = "\n\
Alignment      Offset         %-8.8s %-8.8s    Sends   %-8.8s Recvs\n\
Local  Remote  Local  Remote  Xfered   Per                 Per\n\
Send   Recv    Send   Recv             Send (avg)          Recv (avg)\n\
%5d   %5d  %5d   %5d %6.4g  %6.2f    %6d   %6.2f %6d\n";

  char *ksink_fmt2 = "\n\
Maximum\n\
Segment\n\
Size (bytes)\n\
%6d\n";
  
  
  float			elapsed_time;
  
#ifdef WANT_INTERVALS
  int interval_count;
  sigset_t signal_set;
#endif
  
  /* what we want is to have a buffer space that is at least one */
  /* send-size greater than our send window. this will insure that we */
  /* are never trying to re-use a buffer that may still be in the hands */
  /* of the transport. This buffer will be malloc'd after we have found */
  /* the size of the local senc socket buffer. We will want to deal */
  /* with alignment and offset concerns as well. */
  
#ifdef DIRTY
  int	*message_int_ptr;
#endif

  struct ring_elt *send_ring;
  
  int len;
  unsigned int nummessages = 0;
  int send_socket;
  int bytes_remaining;
  int sctp_mss;
  int timed_out;

  /* with links like fddi, one can send > 32 bits worth of bytes */
  /* during a test... ;-) at some point, this should probably become a */
  /* 64bit integral type, but those are not entirely common yet */
  double	bytes_sent = 0.0;
  
#ifdef DIRTY
  int	i;
#endif /* DIRTY */
  
  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;

  double	thruput;
  
  struct addrinfo	*remote_res;
  struct addrinfo	*local_res;
  struct addrinfo	*local_remote_res;
  struct addrinfo	*local_local_res;

  struct	sctp_stream_request_struct	*sctp_stream_request;
  struct	sctp_stream_response_struct	*sctp_stream_response;
  struct	sctp_stream_results_struct	*sctp_stream_result;
  
  sctp_stream_request  = 
    (struct sctp_stream_request_struct *)netperf_request.content.test_specific_data;
  sctp_stream_response =
    (struct sctp_stream_response_struct *)netperf_response.content.test_specific_data;
  sctp_stream_result   = 
    (struct sctp_stream_results_struct *)netperf_response.content.test_specific_data;

#ifdef WANT_HISTOGRAM
  time_hist = HIST_new();
#endif /* WANT_HISTOGRAM */
  /* since we are now disconnected from the code that established the */
  /* control socket, and since we want to be able to use different */
  /* protocols and such, we are passed the name of the remote host and */
  /* must turn that into the test specific addressing information. */
  
  /* complete_addrinfos will either succede or exit the process */
  complete_addrinfos(&remote_res,
		     &local_res,
		     remote_host,
		     SOCK_STREAM,
		     IPPROTO_SCTP,
		     0);

  if ( print_headers ) {
    print_top_test_header("SCTP STREAM TEST", local_res, remote_res);
  }

  send_ring = NULL;
  confidence_iteration = 1;
  init_stat();

  /* we have a great-big while loop which controls the number of times */
  /* we run a particular test. this is for the calculation of a */
  /* confidence interval (I really should have stayed awake during */
  /* probstats :). If the user did not request confidence measurement */
  /* (no confidence is the default) then we will only go though the */
  /* loop once. the confidence stuff originates from the folks at IBM */

  while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
	 (confidence_iteration <= iteration_min)) {

    /* initialize a few counters. we have to remember that we might be */
    /* going through the loop more than once. */
    
    nummessages    =	0;
    bytes_sent     =	0.0;
    times_up       = 	0;
    timed_out	   =    0;
    
    /*set up the data socket                        */
    send_socket = create_data_socket(local_res);
    
    if (send_socket == INVALID_SOCKET){
      perror("netperf: send_sctp_stream: sctp stream data socket");
      exit(1);
    }
    
    if (debug) {
      fprintf(where,"send_sctp_stream: send_socket obtained...\n");
    }
    
    /* at this point, we have either retrieved the socket buffer sizes, */
    /* or have tried to set them, so now, we may want to set the send */
    /* size based on that (because the user either did not use a -m */
    /* option, or used one with an argument of 0). If the socket buffer */
    /* size is not available, we will set the send size to 4KB - no */
    /* particular reason, just arbitrary... */
    if (send_size == 0) {
      if (lss_size > 0) {
	send_size = lss_size;
      }
      else {
	send_size = 4096;
      }
    }
    
    /* set-up the data buffer ring with the requested alignment and offset. */
    /* note also that we have allocated a quantity */
    /* of memory that is at least one send-size greater than our socket */
    /* buffer size. We want to be sure that there are at least two */
    /* buffers allocated - this can be a bit of a problem when the */
    /* send_size is bigger than the socket size, so we must check... the */
    /* user may have wanted to explicitly set the "width" of our send */
    /* buffers, we should respect that wish... */
    if (send_width == 0) {
      send_width = (lss_size/send_size) + 1;
      if (send_width == 1) send_width++;
    }
    
    if (send_ring == NULL) {
      /* only allocate the send ring once. this is a networking test, */
      /* not a memory allocation test. this way, we do not need a */
      /* deallocate_buffer_ring() routine, and I don't feel like */
      /* writing one anyway :) raj 11/94 */
      send_ring = allocate_buffer_ring(send_width,
				       send_size,
				       local_send_align,
				       local_send_offset);
    }

    /* If the user has requested cpu utilization measurements, we must */
    /* calibrate the cpu(s). We will perform this task within the tests */
    /* themselves. If the user has specified the cpu rate, then */
    /* calibrate_local_cpu will return rather quickly as it will have */
    /* nothing to do. If local_cpu_rate is zero, then we will go through */
    /* all the "normal" calibration stuff and return the rate back. */
    
    if (local_cpu_usage) {
      local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
    }
    
    /* Tell the remote end to do a listen. The server alters the socket */
    /* paramters on the other side at this point, hence the reason for */
    /* all the values being passed in the setup message. If the user did */
    /* not specify any of the parameters, they will be passed as 0, which */
    /* will indicate to the remote that no changes beyond the system's */
    /* default should be used. Alignment is the exception, it will */
    /* default to 1, which will be no alignment alterations. */
    
    netperf_request.content.request_type	=	DO_SCTP_STREAM;
    sctp_stream_request->send_buf_size	=	rss_size_req;
    sctp_stream_request->recv_buf_size	=	rsr_size_req;
    sctp_stream_request->receive_size	=	recv_size;
    sctp_stream_request->no_delay	=	rem_nodelay;
    sctp_stream_request->recv_alignment	=	remote_recv_align;
    sctp_stream_request->recv_offset	=	remote_recv_offset;
    sctp_stream_request->measure_cpu	=	remote_cpu_usage;
    sctp_stream_request->cpu_rate	=	remote_cpu_rate;
    if (test_time) {
      sctp_stream_request->test_length	=	test_time;
    }
    else {
      if (msg_count)
	  test_bytes = send_size * msg_count;

      sctp_stream_request->test_length	=	test_bytes;
    }
    sctp_stream_request->so_rcvavoid	=	rem_rcvavoid;
    sctp_stream_request->so_sndavoid	=	rem_sndavoid;
#ifdef DIRTY
    sctp_stream_request->dirty_count    =       rem_dirty_count;
    sctp_stream_request->clean_count    =       rem_clean_count;
#endif /* DIRTY */
    sctp_stream_request->port		=	htonl(atoi(remote_data_port));
    sctp_stream_request->ipfamily	=	af_to_nf(remote_res->ai_family);
    sctp_stream_request->non_blocking   =	non_block;
    
    
    if (debug > 1) {
      fprintf(where,
	      "netperf: send_sctp_stream: requesting sctp stream test\n");
    }
    
    send_request();
    
    /* The response from the remote will contain all of the relevant 	*/
    /* socket parameters for this test type. We will put them back into */
    /* the variables here so they can be displayed if desired.  The	*/
    /* remote will have calibrated CPU if necessary, and will have done	*/
    /* all the needed set-up we will have calibrated the cpu locally	*/
    /* before sending the request, and will grab the counter value right*/
    /* after the connect returns. The remote will grab the counter right*/
    /* after the accept call. This saves the hassle of extra messages	*/
    /* being sent for the sctp tests.					*/
    
    recv_response();
    
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote listen done.\n");
      rsr_size	      =	sctp_stream_response->recv_buf_size;
      rss_size	      =	sctp_stream_response->send_buf_size;
      rem_nodelay     =	sctp_stream_response->no_delay;
      remote_cpu_usage=	sctp_stream_response->measure_cpu;
      remote_cpu_rate = sctp_stream_response->cpu_rate;

      /* we have to make sure that the server port number is in */
      /* network order */
      set_port_number(remote_res, (short)sctp_stream_response->data_port_number);

      rem_rcvavoid	= sctp_stream_response->so_rcvavoid;
      rem_sndavoid	= sctp_stream_response->so_sndavoid;
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      fprintf(where,
	      "netperf: remote error %d",
	      netperf_response.content.serv_errno);
      perror("");
      fflush(where);
      
      exit(1);
    }
    
    /*Connect up to the remote port on the data socket  */
    if (connect(send_socket, 
		remote_res->ai_addr,
		remote_res->ai_addrlen) == INVALID_SOCKET) {
      perror("netperf: send_sctp_stream: data socket connect failed");
      exit(1);
    }

    sctp_enable_events(send_socket, SCTP_ASSOC_CHANGE_EV);

    if (non_block) {
	/* now that we are connected, mark the socket as non-blocking */
	if (!set_nonblock(send_socket)) {
	  perror("netperf: fcntl");
	  exit(1);
	}
    }

    /* Data Socket set-up is finished. If there were problems, either */
    /* the connect would have failed, or the previous response would */
    /* have indicated a problem. I failed to see the value of the */
    /* extra  message after the accept on the remote. If it failed, */
    /* we'll see it here. If it didn't, we might as well start pumping */
    /* data. */ 
    
    /* Set-up the test end conditions. For a stream test, they can be */
    /* either time or byte-count based. */
    
    if (test_time) {
      /* The user wanted to end the test after a period of time. */
      times_up = 0;
      bytes_remaining = 0;
      /* in previous revisions, we had the same code repeated throught */
      /* all the test suites. this was unnecessary, and meant more */
      /* work for me when I wanted to switch to POSIX signals, so I */
      /* have abstracted this out into a routine in netlib.c. if you */
      /* are experiencing signal problems, you might want to look */
      /* there. raj 11/94 */
      start_timer(test_time);
    }
    else {
      /* The tester wanted to send a number of bytes. */
      bytes_remaining = test_bytes;
      times_up = 1;
    }
    
    /* The cpu_start routine will grab the current time and possibly */
    /* value of the idle counter for later use in measuring cpu */
    /* utilization and/or service demand and thruput. */
    
    cpu_start(local_cpu_usage);
    
#ifdef WANT_INTERVALS
    if ((interval_burst) || (demo_mode)) {
      /* zero means that we never pause, so we never should need the */
      /* interval timer, unless we are in demo_mode */
      start_itimer(interval_wate);
    }
    interval_count = interval_burst;
    /* get the signal set for the call to sigsuspend */
    if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) {
      fprintf(where,
	      "send_sctp_stream: unable to get sigmask errno %d\n",
	      errno);
      fflush(where);
      exit(1);
    }
#endif /* WANT_INTERVALS */

#ifdef DIRTY
    /* initialize the random number generator for putting dirty stuff */
    /* into the send buffer. raj */
    srand((int) getpid());
#endif
    
    /* before we start, initialize a few variables */

    /* We use an "OR" to control test execution. When the test is */
    /* controlled by time, the byte count check will always return false. */
    /* When the test is controlled by byte count, the time test will */
    /* always return false. When the test is finished, the whole */
    /* expression will go false and we will stop sending data. */
    
    while ((!times_up) || (bytes_remaining > 0)) {
      
#ifdef DIRTY
      /* we want to dirty some number of consecutive integers in the buffer */
      /* we are about to send. we may also want to bring some number of */
      /* them cleanly into the cache. The clean ones will follow any dirty */
      /* ones into the cache. at some point, we might want to replace */
      /* the rand() call with something from a table to reduce our call */
      /* overhead during the test, but it is not a high priority item. */
      message_int_ptr = (int *)(send_ring->buffer_ptr);
      for (i = 0; i < loc_dirty_count; i++) {
	*message_int_ptr = rand();
	message_int_ptr++;
      }
      for (i = 0; i < loc_clean_count; i++) {
	loc_dirty_count = *message_int_ptr;
	message_int_ptr++;
      }
#endif /* DIRTY */
      
#ifdef WANT_HISTOGRAM
      /* timestamp just before we go into send and then again just after */
      /* we come out raj 8/94 */
      HIST_timestamp(&time_one);
#endif /* WANT_HISTOGRAM */
      
      while ((len=sctp_sendmsg(send_socket,
			       send_ring->buffer_ptr, send_size,
			       NULL, 0,
			       0, 0, 0, 0, 0)) != send_size) {
	if (non_block && errno == EAGAIN)
	    continue;
	else if ((len >=0) || SOCKET_EINTR(len)) {
	  /* the test was interrupted, must be the end of test */
	  timed_out = 1;
	  break;
	}
	perror("netperf: data send error");
	printf("len was %d\n",len);
	exit(1);
      }

      if (timed_out)
	  break;	/* we timed out durint sendmsg, done with test */

#ifdef WANT_HISTOGRAM
      /* timestamp the exit from the send call and update the histogram */
      HIST_timestamp(&time_two);
      HIST_add(time_hist,delta_micro(&time_one,&time_two));
#endif /* WANT_HISTOGRAM */      

#ifdef WANT_INTERVALS      
      if (demo_mode) {
	units_this_tick += send_size;
      }
      /* in this case, the interval count is the count-down couter */
      /* to decide to sleep for a little bit */
      if ((interval_burst) && (--interval_count == 0)) {
	/* call sigsuspend and wait for the interval timer to get us */
	/* out */
	if (debug > 1) {
	  fprintf(where,"about to suspend\n");
	  fflush(where);
	}
	if (sigsuspend(&signal_set) == EFAULT) {
	  fprintf(where,
		  "send_sctp_stream: fault with sigsuspend.\n");
	  fflush(where);
	  exit(1);
	}
	interval_count = interval_burst;
      }
#endif /* WANT_INTERVALS */
      
      /* now we want to move our pointer to the next position in the */
      /* data buffer...we may also want to wrap back to the "beginning" */
      /* of the bufferspace, so we will mod the number of messages sent */
      /* by the send width, and use that to calculate the offset to add */
      /* to the base pointer. */
      nummessages++;          
      send_ring = send_ring->next;
      if (bytes_remaining) {
	bytes_remaining -= send_size;
      }
    }

    /* The test is over. Flush the buffers to the remote end. We do a */
    /* graceful release to insure that all data has been taken by the */
    /* remote. */ 

    /* but first, if the verbosity is greater than 1, find-out what */
    /* the sctp maximum segment_size was (if possible) */
    if (verbosity > 1) {
      sctp_mss = -1;
      get_sctp_info(send_socket, &sctp_mss);
    }

    shutdown(send_socket, SHUT_WR);

    /* The test server will signal to us when it wants to shutdown.
     * In blocking mode, we can call recvmsg.  In non-blocking
     * mode, we need to select on the socket for reading.
     * We'll assume that all returns are succefull
     */
    if (non_block) {
	fd_set readfds;

	FD_ZERO(&readfds);
	FD_SET(send_socket, &readfds);
	select(send_socket+1, &readfds, NULL, NULL, NULL);
    } else {
	sctp_recvmsg(send_socket, send_ring->buffer_ptr, send_size, NULL, 
		0, NULL, 0);
    }
    
    /* this call will always give us the elapsed time for the test, and */
    /* will also store-away the necessaries for cpu utilization */
    
    cpu_stop(local_cpu_usage,&elapsed_time);	/* was cpu being */
						/* measured and how */
						/* long did we really */
						/* run? */
    
    /* we are finished with the socket, so close it to prevent hitting */
    /* the limit on maximum open files. */
    close(send_socket);

    /* Get the statistics from the remote end. The remote will have */
    /* calculated service demand and all those interesting things. If it */
    /* wasn't supposed to care, it will return obvious values. */
    
    recv_response();
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote results obtained\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      fprintf(where,
	      "netperf: remote error %d",
	      netperf_response.content.serv_errno);
      perror("");
      fflush(where);
      
      exit(1);
    }
    
    /* We now calculate what our thruput was for the test. In the future, */
    /* we may want to include a calculation of the thruput measured by */
    /* the remote, but it should be the case that for a sctp stream test, */
    /* that the two numbers should be *very* close... We calculate */
    /* bytes_sent regardless of the way the test length was controlled. */
    /* If it was time, we needed to, and if it was by bytes, the user may */
    /* have specified a number of bytes that wasn't a multiple of the */
    /* send_size, so we really didn't send what he asked for ;-) */
    
    bytes_sent	= ntohd(sctp_stream_result->bytes_received);

    thruput	= (double) calc_thruput(bytes_sent);
    
    if (local_cpu_usage || remote_cpu_usage) {
      /* We must now do a little math for service demand and cpu */
      /* utilization for the system(s) */
      /* Of course, some of the information might be bogus because */
      /* there was no idle counter in the kernel(s). We need to make */
      /* a note of this for the user's benefit...*/
      if (local_cpu_usage) {
	
	local_cpu_utilization	= calc_cpu_util(0.0);
	local_service_demand	= calc_service_demand(bytes_sent,
						      0.0,
						      0.0,
						      0);
      }
      else {
	local_cpu_utilization	= (float) -1.0;
	local_service_demand	= (float) -1.0;
      }
      
      if (remote_cpu_usage) {
	
	remote_cpu_utilization	= sctp_stream_result->cpu_util;
	remote_service_demand	= calc_service_demand(bytes_sent,
						      0.0,
						      remote_cpu_utilization,
						      sctp_stream_result->num_cpus);
      }
      else {
	remote_cpu_utilization = (float) -1.0;
	remote_service_demand  = (float) -1.0;
      }
    }    
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= (float) -1.0;
      local_service_demand	= (float) -1.0;
      remote_cpu_utilization = (float) -1.0;
      remote_service_demand  = (float) -1.0;
    }

    /* at this point, we want to calculate the confidence information. */
    /* if debugging is on, calculate_confidence will print-out the */
    /* parameters we pass it */
    
    calculate_confidence(confidence_iteration,
			 elapsed_time,
			 thruput,
			 local_cpu_utilization,
			 remote_cpu_utilization,
			 local_service_demand,
			 remote_service_demand);
    
    
    confidence_iteration++;
  }

  /* at this point, we have finished making all the runs that we */
  /* will be making. so, we should extract what the calcuated values */
  /* are for all the confidence stuff. we could make the values */
  /* global, but that seemed a little messy, and it did not seem worth */
  /* all the mucking with header files. so, we create a routine much */
  /* like calcualte_confidence, which just returns the mean values. */
  /* raj 11/94 */

  retrieve_confident_values(&elapsed_time,
			    &thruput,
			    &local_cpu_utilization,
			    &remote_cpu_utilization,
			    &local_service_demand,
			    &remote_service_demand);

  /* We are now ready to print all the information. If the user */
  /* has specified zero-level verbosity, we will just print the */
  /* local service demand, or the remote service demand. If the */
  /* user has requested verbosity level 1, he will get the basic */
  /* "streamperf" numbers. If the user has specified a verbosity */
  /* of greater than 1, we will display a veritable plethora of */
  /* background information from outside of this block as it it */
  /* not cpu_measurement specific...  */

  if (confidence < 0) {
    /* we did not hit confidence, but were we asked to look for it? */
    if (iteration_max > 1) {
      display_confidence();
    }
  }

  if (local_cpu_usage || remote_cpu_usage) {
    local_cpu_method = format_cpu_method(cpu_method);
    remote_cpu_method = format_cpu_method(sctp_stream_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method);
      }
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,
		cpu_title,
		format_units(),
		local_cpu_method,
		remote_cpu_method);
      }
    
      fprintf(where,
	      cpu_fmt_1,		/* the format string */
	      rsr_size,		        /* remote recvbuf size */
	      lss_size,		        /* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time,		/* how long was the test */
	      thruput, 		        /* what was the xfer rate */
	      local_cpu_utilization,	/* local cpu */
	      remote_cpu_utilization,	/* remote cpu */
	      local_service_demand,	/* local service demand */
	      remote_service_demand);	/* remote service demand */
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      thruput);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }
      fprintf(where,
	      tput_fmt_1,		/* the format string */
	      rsr_size, 		/* remote recvbuf size */
	      lss_size, 		/* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time, 		/* how long did it take */
	      thruput);/* how fast did it go */
      break;
    }
  }
  
  /* it would be a good thing to include information about some of the */
  /* other parameters that may have been set for this test, but at the */
  /* moment, I do not wish to figure-out all the  formatting, so I will */
  /* just put this comment here to help remind me that it is something */
  /* that should be done at a later time. */
  
  if (verbosity > 1) {
    /* The user wanted to know it all, so we will give it to him. */
    /* This information will include as much as we can find about */
    /* sctp statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */
   
    /* this stuff needs to be worked-out in the presence of confidence */
    /* intervals and multiple iterations of the test... raj 11/94 */
 
    fprintf(where,
	    ksink_fmt,
	    "Bytes",
	    "Bytes",
	    "Bytes",
	    local_send_align,
	    remote_recv_align,
	    local_send_offset,
	    remote_recv_offset,
	    bytes_sent,
	    bytes_sent / (double)nummessages,
	    nummessages,
	    bytes_sent / (double)sctp_stream_result->recv_calls,
	    sctp_stream_result->recv_calls);
    fprintf(where,
	    ksink_fmt2,
	    sctp_mss);
    fflush(where);
#ifdef WANT_HISTOGRAM
    fprintf(where,"\n\nHistogram of time spent in send() call.\n");
    fflush(where);
    HIST_report(time_hist);
#endif /* WANT_HISTOGRAM */
  }
  
}




/* This is the server-side routine for the sctp stream test. It is */
/* implemented as one routine. I could break things-out somewhat, but */
/* didn't feel it was necessary. */

void
recv_sctp_stream()
{
  
  struct sockaddr_in myaddr_in; /* needed to get port number */
  struct sockaddr_storage peeraddr;	/* used in accept */
  int	s_listen,s_data;
  int 	addrlen;
  int	len;
  unsigned int	receive_calls;
  float	elapsed_time;
  double   bytes_received;
  
  struct ring_elt *recv_ring;

  struct addrinfo *local_res;
  char local_name[BUFSIZ];
  char port_buffer[PORTBUFSIZE];
  int  msg_flags = 0;

#ifdef DIRTY
  int   *message_int_ptr;
  int   dirty_count;
  int   clean_count;
  int   i;
#endif
  
#ifdef DO_SELECT
  fd_set readfds;
  struct timeval timeout;
#endif /* DO_SELECT */

  struct	sctp_stream_request_struct	*sctp_stream_request;
  struct	sctp_stream_response_struct	*sctp_stream_response;
  struct	sctp_stream_results_struct	*sctp_stream_results;
  
#ifdef DO_SELECT
  FD_ZERO(&readfds);
  timeout.tv_sec = 1;
  timeout.tv_usec = 0;
#endif /* DO_SELECT */

  sctp_stream_request	= 
    (struct sctp_stream_request_struct *)netperf_request.content.test_specific_data;
  sctp_stream_response	= 
    (struct sctp_stream_response_struct *)netperf_response.content.test_specific_data;
  sctp_stream_results	= 
    (struct sctp_stream_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_sctp_stream: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug) {
    fprintf(where,"recv_sctp_stream: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = SCTP_STREAM_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_sctp_stream: the response type is set...\n");
    fflush(where);
  }
  
  /* We now alter the message_ptr variable to be at the desired */
  /* alignment with the desired offset. */
  
  if (debug) {
    fprintf(where,"recv_sctp_stream: requested alignment of %d\n",
	    sctp_stream_request->recv_alignment);
    fflush(where);
  }

  /* create_data_socket expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lss_size_req = sctp_stream_request->send_buf_size;
  lsr_size_req = sctp_stream_request->recv_buf_size;
  loc_nodelay = sctp_stream_request->no_delay;
  loc_rcvavoid = sctp_stream_request->so_rcvavoid;
  loc_sndavoid = sctp_stream_request->so_sndavoid;
  non_block = sctp_stream_request->non_blocking;

  set_hostname_and_port(local_name,
			port_buffer,
			nf_to_af(sctp_stream_request->ipfamily),
			sctp_stream_request->port);

  local_res = complete_addrinfo(local_name,
				local_name,
				port_buffer,
				nf_to_af(sctp_stream_request->ipfamily),
				SOCK_STREAM,
				IPPROTO_SCTP,
				0);

  s_listen = create_data_socket(local_res);
  
  if (s_listen < 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }
  
  /* what sort of sizes did we end-up with? */
  if (sctp_stream_request->receive_size == 0) {
    if (lsr_size > 0) {
      recv_size = lsr_size;
    }
    else {
      recv_size = 4096;
    }
  }
  else {
    recv_size = sctp_stream_request->receive_size;
  }
  
  /* we want to set-up our recv_ring in a manner analagous to what we */
  /* do on the sending side. this is more for the sake of symmetry */
  /* than for the needs of say copy avoidance, but it might also be */
  /* more realistic - this way one could conceivably go with a */
  /* double-buffering scheme when taking the data an putting it into */
  /* the filesystem or something like that. raj 7/94 */

  if (recv_width == 0) {
    recv_width = (lsr_size/recv_size) + 1;
    if (recv_width == 1) recv_width++;
  }

  recv_ring = allocate_buffer_ring(recv_width,
				   recv_size,
				   sctp_stream_request->recv_alignment,
				   sctp_stream_request->recv_offset);

  if (debug) {
    fprintf(where,"recv_sctp_stream: set recv_size = %d, align = %d, offset = %d.\n",
		   recv_size, sctp_stream_request->recv_alignment,
		   sctp_stream_request->recv_offset);
    fflush(where);
  }
  
  /* now get the port number assigned by the system  */
  addrlen = sizeof(myaddr_in);
  if (getsockname(s_listen, 
		  (struct sockaddr *)&myaddr_in,
		  &addrlen) == -1){
    netperf_response.content.serv_errno = errno;
    close(s_listen);
    send_response();
    
    exit(1);
  }
  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  sctp_stream_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
  netperf_response.content.serv_errno   = 0;
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a -1 to */
  /* the initiator. */
  
  sctp_stream_response->cpu_rate = (float)0.0; 	/* assume no cpu */
  if (sctp_stream_request->measure_cpu) {
    sctp_stream_response->measure_cpu = 1;
    sctp_stream_response->cpu_rate = 
      calibrate_local_cpu(sctp_stream_request->cpu_rate);
  }
  else {
    sctp_stream_response->measure_cpu = 0;
  }
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  sctp_stream_response->send_buf_size = lss_size;
  sctp_stream_response->recv_buf_size = lsr_size;
  sctp_stream_response->no_delay = loc_nodelay;
  sctp_stream_response->so_rcvavoid = loc_rcvavoid;
  sctp_stream_response->so_sndavoid = loc_sndavoid;
  sctp_stream_response->receive_size = recv_size;

  /* Now, let's set-up the socket to listen for connections */
  if (listen(s_listen, 5) == -1) {
    netperf_response.content.serv_errno = errno;
    close(s_listen);
    send_response();
    
    exit(1);
  }
  
  send_response();
  
  addrlen = sizeof(peeraddr);
  
  if ((s_data = accept(s_listen,
		      (struct sockaddr *)&peeraddr,
		      &addrlen)) == INVALID_SOCKET) {
    /* Let's just punt. The remote will be given some information */
    close(s_listen);
    exit(1);
  }

  sctp_enable_events(s_data, SCTP_ASSOC_CHANGE_EV | SCTP_SHUTDOWN_EV);

  /* now that we are connected, mark the socket as non-blocking */
  if (non_block) {
      fprintf(where, "setting socket as nonblocking\n");
      fflush(where);
      if (!set_nonblock(s_data)) {
	close(s_data);
	exit(1);
      }
  }

#ifdef KLUDGE_SOCKET_OPTIONS
  /* this is for those systems which *INCORRECTLY* fail to pass */
  /* attributes across an accept() call. Including this goes against */
  /* my better judgement :( raj 11/95 */

  kludge_socket_options(s_data);

#endif /* KLUDGE_SOCKET_OPTIONS */
  
  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(sctp_stream_request->measure_cpu);

  /* The loop will exit when the sender does a shutdown, which will */
  /* return a length of zero   */
  
#ifdef DIRTY
    /* we want to dirty some number of consecutive integers in the buffer */
    /* we are about to recv. we may also want to bring some number of */
    /* them cleanly into the cache. The clean ones will follow any dirty */
    /* ones into the cache. */

  dirty_count = sctp_stream_request->dirty_count;
  clean_count = sctp_stream_request->clean_count;
  message_int_ptr = (int *)recv_ring->buffer_ptr;
  for (i = 0; i < dirty_count; i++) {
    *message_int_ptr = rand();
    message_int_ptr++;
  }
  for (i = 0; i < clean_count; i++) {
    dirty_count = *message_int_ptr;
    message_int_ptr++;
  }
#endif /* DIRTY */

  bytes_received = 0;
  receive_calls  = 0;
  
  while ((len = sctp_recvmsg(s_data,
			    recv_ring->buffer_ptr, recv_size,
			    NULL, 0, NULL, &msg_flags)) != 0) {
    if (len == SOCKET_ERROR) {
	if (non_block && errno == EAGAIN) {
	   if (debug){
	     fprintf(where,
		    "recv_sctp_stream: sctp_recvmsg timed out, trying again\n");
	     fflush(where);
	   }
	   Set_errno(0);
	   continue;
	}
	if (debug) {
	    fprintf(where,
		    "recv_sctp_stream: sctp_recvmsg error %d, exiting",
		    errno);
	    fflush(where);
        }
	netperf_response.content.serv_errno = errno;
	send_response();
	close(s_data);
	exit(1);
    }

    if (msg_flags & MSG_NOTIFICATION) {
	 msg_flags = 0;
	 if (debug) {
	   fprintf(where,
		    "recv_sctp_stream: Got notification... processing\n");
	     fflush(where);
	}
	if (sctp_process_event(s_data, recv_ring->buffer_ptr) == SCTP_CLOSE)
	    break;	/* break out of the recvmsg loop */

	continue;
    }

    bytes_received += len;
    receive_calls++;

    /* more to the next buffer in the recv_ring */
    recv_ring = recv_ring->next;

#ifdef PAUSE
    sleep(1);
#endif /* PAUSE */

#ifdef DIRTY
    message_int_ptr = (int *)(recv_ring->buffer_ptr);
    for (i = 0; i < dirty_count; i++) {
      *message_int_ptr = rand();
      message_int_ptr++;
    }
    for (i = 0; i < clean_count; i++) {
      dirty_count = *message_int_ptr;
      message_int_ptr++;
    }
#endif /* DIRTY */

#ifdef DO_SELECT
	FD_SET(s_data,&readfds);
	select(s_data+1,&readfds,NULL,NULL,&timeout);
#endif /* DO_SELECT */

  }
  
  /* perform a shutdown to signal the sender that */
  /* we have received all the data sent. raj 4/93 */

  if (close(s_data) == -1) {
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }
  
  cpu_stop(sctp_stream_request->measure_cpu,&elapsed_time);
  
  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_sctp_stream: got %g bytes\n",
	    bytes_received);
    fprintf(where,
	    "recv_sctp_stream: got %d recvs\n",
	    receive_calls);
    fflush(where);
  }
  
  sctp_stream_results->bytes_received	= htond(bytes_received);
  sctp_stream_results->elapsed_time	= elapsed_time;
  sctp_stream_results->recv_calls	= receive_calls;
  
  if (sctp_stream_request->measure_cpu) {
    sctp_stream_results->cpu_util	= calc_cpu_util(0.0);
  };
  
  if (debug) {
    fprintf(where,
	    "recv_sctp_stream: test complete, sending results.\n");
    fprintf(where,
	    "                 bytes_received %g receive_calls %d\n",
	    bytes_received,
	    receive_calls);
    fprintf(where,
	    "                 len %d\n",
	    len);
    fflush(where);
  }
  
  sctp_stream_results->cpu_method = cpu_method;
  sctp_stream_results->num_cpus   = lib_num_loc_cpus;
  send_response();

  /* we are now done with the sockets */
  close(s_listen);

}


/* This routine implements the SCTP unidirectional data transfer test */
/* (a.k.a. stream) for the sockets interface. It receives its */
/* parameters via global variables from the shell and writes its */
/* output to the standard output. */


void 
send_sctp_stream_1toMany(remote_host)
char	remote_host[];
{
  
  char *tput_title = "\
Recv   Send    Send                          \n\
Socket Socket  Message  Elapsed              \n\
Size   Size    Size     Time     Throughput  \n\
bytes  bytes   bytes    secs.    %s/sec  \n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1 =
    "%6d %6d %6d    %-6.2f   %7.2f   \n";
  
  char *cpu_title = "\
Recv   Send    Send                          Utilization       Service Demand\n\
Socket Socket  Message  Elapsed              Send     Recv     Send    Recv\n\
Size   Size    Size     Time     Throughput  local    remote   local   remote\n\
bytes  bytes   bytes    secs.    %-8.8s/s  %% %c      %% %c      us/KB   us/KB\n\n";
  
  char *cpu_fmt_0 =
    "%6.3f %c\n";

  char *cpu_fmt_1 =
    "%6d %6d %6d    %-6.2f     %7.2f   %-6.2f   %-6.2f   %-6.3f  %-6.3f\n";
  
  char *ksink_fmt = "\n\
Alignment      Offset         %-8.8s %-8.8s    Sends   %-8.8s Recvs\n\
Local  Remote  Local  Remote  Xfered   Per                 Per\n\
Send   Recv    Send   Recv             Send (avg)          Recv (avg)\n\
%5d   %5d  %5d   %5d %6.4g  %6.2f    %6d   %6.2f %6d\n";

  char *ksink_fmt2 = "\n\
Maximum\n\
Segment\n\
Size (bytes)\n\
%6d\n";
  
  
  float			elapsed_time;
  
#ifdef WANT_INTERVALS
  int interval_count;
  sigset_t signal_set;
#endif
  
  /* what we want is to have a buffer space that is at least one */
  /* send-size greater than our send window. this will insure that we */
  /* are never trying to re-use a buffer that may still be in the hands */
  /* of the transport. This buffer will be malloc'd after we have found */
  /* the size of the local senc socket buffer. We will want to deal */
  /* with alignment and offset concerns as well. */
  
#ifdef DIRTY
  int	*message_int_ptr;
#endif

  struct ring_elt *send_ring;
  
  int len;
  unsigned int nummessages = 0;
  int *send_socket;
  int bytes_remaining;
  int sctp_mss;

  /* with links like fddi, one can send > 32 bits worth of bytes */
  /* during a test... ;-) at some point, this should probably become a */
  /* 64bit integral type, but those are not entirely common yet */
  double	bytes_sent = 0.0;
  
#ifdef DIRTY
  int	i;
#endif /* DIRTY */
  int j;

  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;

  double	thruput;
  
  struct addrinfo *remote_res;
  struct addrinfo *local_res;
  struct addrinfo *last_remote_res;
  struct addrinfo *last_local_res;

  struct	sctp_stream_request_struct	*sctp_stream_request;
  struct	sctp_stream_response_struct	*sctp_stream_response;
  struct	sctp_stream_results_struct	*sctp_stream_result;
  
  sctp_stream_request  = 
    (struct sctp_stream_request_struct *)netperf_request.content.test_specific_data;
  sctp_stream_response =
    (struct sctp_stream_response_struct *)netperf_response.content.test_specific_data;
  sctp_stream_result   = 
    (struct sctp_stream_results_struct *)netperf_response.content.test_specific_data;

#ifdef WANT_HISTOGRAM
  time_hist = HIST_new();
#endif /* WANT_HISTOGRAM */
  
  complete_addrinfos(&remote_res,
		     &local_res,
		     remote_host,
		     SOCK_SEQPACKET,
		     IPPROTO_SCTP,
		     0);
  
  if ( print_headers ) {
    print_top_test_header("SCTP 1-TO-MANY STREAM TEST",local_res,remote_res);
  }

  send_ring = NULL;
  confidence_iteration = 1;
  init_stat();

  send_socket = malloc(sizeof (int) * num_associations);
  if (send_socket == NULL) {
      fprintf(where, "send_sctp_stream_1toMany: failed to allocation sockets!\n");
      exit(1);
  }

  /* we have a great-big while loop which controls the number of times */
  /* we run a particular test. this is for the calculation of a */
  /* confidence interval (I really should have stayed awake during */
  /* probstats :). If the user did not request confidence measurement */
  /* (no confidence is the default) then we will only go though the */
  /* loop once. the confidence stuff originates from the folks at IBM */

  while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
	 (confidence_iteration <= iteration_min)) {

    int		j=0;
    int		timed_out = 0;
    

    /* initialize a few counters. we have to remember that we might be */
    /* going through the loop more than once. */
    
    nummessages    =	0;
    bytes_sent     =	0.0;
    times_up       = 	0;
    
    /* at this point, we have either retrieved the socket buffer sizes, */
    /* or have tried to set them, so now, we may want to set the send */
    /* size based on that (because the user either did not use a -m */
    /* option, or used one with an argument of 0). If the socket buffer */
    /* size is not available, we will set the send size to 4KB - no */
    /* particular reason, just arbitrary... */
    if (send_size == 0) {
      if (lss_size > 0) {
	send_size = lss_size;
      }
      else {
	send_size = 4096;
      }
    }
    
    /* set-up the data buffer ring with the requested alignment and offset. */
    /* note also that we have allocated a quantity */
    /* of memory that is at least one send-size greater than our socket */
    /* buffer size. We want to be sure that there are at least two */
    /* buffers allocated - this can be a bit of a problem when the */
    /* send_size is bigger than the socket size, so we must check... the */
    /* user may have wanted to explicitly set the "width" of our send */
    /* buffers, we should respect that wish... */
    if (send_width == 0) {
      send_width = (lss_size/send_size) + 1;
      if (send_width == 1) send_width++;
    }
    
    if (send_ring == NULL) {
      /* only allocate the send ring once. this is a networking test, */
      /* not a memory allocation test. this way, we do not need a */
      /* deallocate_buffer_ring() routine, and I don't feel like */
      /* writing one anyway :) raj 11/94 */
      send_ring = allocate_buffer_ring(send_width,
				       send_size,
				       local_send_align,
				       local_send_offset);
    }

    /* If the user has requested cpu utilization measurements, we must */
    /* calibrate the cpu(s). We will perform this task within the tests */
    /* themselves. If the user has specified the cpu rate, then */
    /* calibrate_local_cpu will return rather quickly as it will have */
    /* nothing to do. If local_cpu_rate is zero, then we will go through */
    /* all the "normal" calibration stuff and return the rate back. */
    
    if (local_cpu_usage) {
      local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
    }
    
    /* Tell the remote end to do a listen. The server alters the socket */
    /* paramters on the other side at this point, hence the reason for */
    /* all the values being passed in the setup message. If the user did */
    /* not specify any of the parameters, they will be passed as 0, which */
    /* will indicate to the remote that no changes beyond the system's */
    /* default should be used. Alignment is the exception, it will */
    /* default to 1, which will be no alignment alterations. */
    
    netperf_request.content.request_type	=	DO_SCTP_STREAM_MANY;
    sctp_stream_request->send_buf_size	=	rss_size_req;
    sctp_stream_request->recv_buf_size	=	rsr_size_req;
    sctp_stream_request->receive_size	=	recv_size;
    sctp_stream_request->no_delay	=	rem_nodelay;
    sctp_stream_request->recv_alignment	=	remote_recv_align;
    sctp_stream_request->recv_offset	=	remote_recv_offset;
    sctp_stream_request->measure_cpu	=	remote_cpu_usage;
    sctp_stream_request->cpu_rate	=	remote_cpu_rate;
    if (test_time) {
      sctp_stream_request->test_length	=	test_time;
    }
    else {
      if (msg_count)
	  test_bytes = send_size * msg_count;

      sctp_stream_request->test_length	=	test_bytes*num_associations;
    }
    sctp_stream_request->so_rcvavoid	=	rem_rcvavoid;
    sctp_stream_request->so_sndavoid	=	rem_sndavoid;
#ifdef DIRTY
    sctp_stream_request->dirty_count    =       rem_dirty_count;
    sctp_stream_request->clean_count    =       rem_clean_count;
#endif /* DIRTY */
    sctp_stream_request->port		= 	(atoi(remote_data_port));
    sctp_stream_request->ipfamily	=	af_to_nf(remote_res->ai_family);
    sctp_stream_request->non_blocking   =	non_block;
    
    
    if (debug > 1) {
      fprintf(where,
	      "netperf: send_sctp_stream_1toMany: requesting sctp stream test\n");
    }
    
    send_request();
    
    /* The response from the remote will contain all of the relevant 	*/
    /* socket parameters for this test type. We will put them back into */
    /* the variables here so they can be displayed if desired.  The	*/
    /* remote will have calibrated CPU if necessary, and will have done	*/
    /* all the needed set-up we will have calibrated the cpu locally	*/
    /* before sending the request, and will grab the counter value right*/
    /* after the connect returns. The remote will grab the counter right*/
    /* after the accept call. This saves the hassle of extra messages	*/
    /* being sent for the sctp tests.					*/
    
    recv_response();
    
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote listen done.\n");
      rsr_size	      =	sctp_stream_response->recv_buf_size;
      rss_size	      =	sctp_stream_response->send_buf_size;
      rem_nodelay     =	sctp_stream_response->no_delay;
      remote_cpu_usage=	sctp_stream_response->measure_cpu;
      remote_cpu_rate = sctp_stream_response->cpu_rate;

      /* we have to make sure that the server port number is in */
      /* network order */
      set_port_number(remote_res, (unsigned short)sctp_stream_response->data_port_number);
      rem_rcvavoid	= sctp_stream_response->so_rcvavoid;
      rem_sndavoid	= sctp_stream_response->so_sndavoid;
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      fprintf(where,
	      "netperf: remote error %d",
	      netperf_response.content.serv_errno);
      perror("");
      fflush(where);
      
      exit(1);
    }
    
    /*set up the the array of data sockets  and connect them to the server */

    for (j = 0; j < num_associations; j++) {
	send_socket[j] = create_data_socket(local_res);
    
	if (send_socket[j] < 0){
	  perror("netperf: send_sctp_stream_1toMany: sctp stream data socket");
	  exit(1);
	}
	
	if (debug) {
	  fprintf(where,"send_sctp_stream_1toMany: send_socket obtained...\n");
	}

	/*Connect up to the remote port on the data socket  */
	if (connect(send_socket[j], 
		    remote_res->ai_addr,
		    remote_res->ai_addrlen) == INVALID_SOCKET){
	  perror("netperf: send_sctp_stream_1toMany: data socket connect failed");
	  exit(1);
	}

	/* Do it after connect is successfull, so that we don't see COMM_UP */
	sctp_enable_events(send_socket[j], SCTP_ASSOC_CHANGE_EV);

	if (non_block) {
	    /* now that we are connected, mark the socket as non-blocking */
	    if (!set_nonblock(send_socket[j])) {
	      perror("netperf: fcntl");
	      exit(1);
	    }
	}
    }

    /* Data Socket set-up is finished. If there were problems, either */
    /* the connect would have failed, or the previous response would */
    /* have indicated a problem. I failed to see the value of the */
    /* extra  message after the accept on the remote. If it failed, */
    /* we'll see it here. If it didn't, we might as well start pumping */
    /* data. */ 
    
    /* Set-up the test end conditions. For a stream test, they can be */
    /* either time or byte-count based. */
    
    if (test_time) {
      /* The user wanted to end the test after a period of time. */
      times_up = 0;
      bytes_remaining = 0;
      /* in previous revisions, we had the same code repeated throught */
      /* all the test suites. this was unnecessary, and meant more */
      /* work for me when I wanted to switch to POSIX signals, so I */
      /* have abstracted this out into a routine in netlib.c. if you */
      /* are experiencing signal problems, you might want to look */
      /* there. raj 11/94 */
      start_timer(test_time);
    }
    else {
      /* The tester wanted to send a number of bytes. */
      bytes_remaining = test_bytes * num_associations;
      times_up = 1;
    }
    
    /* The cpu_start routine will grab the current time and possibly */
    /* value of the idle counter for later use in measuring cpu */
    /* utilization and/or service demand and thruput. */
    
    cpu_start(local_cpu_usage);
    
#ifdef WANT_INTERVALS
    if ((interval_burst) || (demo_mode)) {
      /* zero means that we never pause, so we never should need the */
      /* interval timer, unless we are in demo_mode */
      start_itimer(interval_wate);
    }
    interval_count = interval_burst;
    /* get the signal set for the call to sigsuspend */
    if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) {
      fprintf(where,
	      "send_sctp_stream_1toMany: unable to get sigmask errno %d\n",
	      errno);
      fflush(where);
      exit(1);
    }
#endif /* WANT_INTERVALS */

#ifdef DIRTY
    /* initialize the random number generator for putting dirty stuff */
    /* into the send buffer. raj */
    srand((int) getpid());
#endif
    
    /* before we start, initialize a few variables */

    /* We use an "OR" to control test execution. When the test is */
    /* controlled by time, the byte count check will always return false. */
    /* When the test is controlled by byte count, the time test will */
    /* always return false. When the test is finished, the whole */
    /* expression will go false and we will stop sending data. */
    
    while ((!times_up) || (bytes_remaining > 0)) {
      
#ifdef DIRTY
      /* we want to dirty some number of consecutive integers in the buffer */
      /* we are about to send. we may also want to bring some number of */
      /* them cleanly into the cache. The clean ones will follow any dirty */
      /* ones into the cache. at some point, we might want to replace */
      /* the rand() call with something from a table to reduce our call */
      /* overhead during the test, but it is not a high priority item. */
      message_int_ptr = (int *)(send_ring->buffer_ptr);
      for (i = 0; i < loc_dirty_count; i++) {
	*message_int_ptr = rand();
	message_int_ptr++;
      }
      for (i = 0; i < loc_clean_count; i++) {
	loc_dirty_count = *message_int_ptr;
	message_int_ptr++;
      }
#endif /* DIRTY */
      
#ifdef WANT_HISTOGRAM
      /* timestamp just before we go into send and then again just after */
      /* we come out raj 8/94 */
      gettimeofday(&time_one,NULL);
#endif /* WANT_HISTOGRAM */
      
      for (j = 0; j < num_associations; j++) {

	  if((len=sctp_sendmsg(send_socket[j],
			       send_ring->buffer_ptr,
			       send_size,
			       (struct sockaddr *)remote_res->ai_addr,
			       remote_res->ai_addrlen,
			       0, 0, 0, 0, 0)) != send_size) {
	    if ((len >=0) || SOCKET_EINTR(len)) {
	      /* the test was interrupted, must be the end of test */
	      timed_out = 1;
	      break;
	    } else if (non_block && errno == EAGAIN) {
		j--;	 /* send again on the same socket */
		Set_errno(0);
		continue;
	    }
	    perror("netperf: data send error");
	    printf("len was %d\n",len);
	    exit(1);
	  }
      }

      if (timed_out)
	  break;	/* test is over, try next iteration */

#ifdef WANT_HISTOGRAM
      /* timestamp the exit from the send call and update the histogram */
      gettimeofday(&time_two,NULL);
      HIST_add(time_hist,delta_micro(&time_one,&time_two));
#endif /* WANT_HISTOGRAM */      

#ifdef WANT_INTERVALS      
      if (demo_mode) {
	units_this_tick += send_size;
      }
      /* in this case, the interval count is the count-down couter */
      /* to decide to sleep for a little bit */
      if ((interval_burst) && (--interval_count == 0)) {
	/* call sigsuspend and wait for the interval timer to get us */
	/* out */
	if (debug > 1) {
	  fprintf(where,"about to suspend\n");
	  fflush(where);
	}
	if (sigsuspend(&signal_set) == EFAULT) {
	  fprintf(where,
		  "send_sctp_stream_1toMany: fault with sigsuspend.\n");
	  fflush(where);
	  exit(1);
	}
	interval_count = interval_burst;
      }
#endif /* WANT_INTERVALS */
      
      /* now we want to move our pointer to the next position in the */
      /* data buffer...we may also want to wrap back to the "beginning" */
      /* of the bufferspace, so we will mod the number of messages sent */
      /* by the send width, and use that to calculate the offset to add */
      /* to the base pointer. */
      nummessages++;          
      send_ring = send_ring->next;
      if (bytes_remaining) {
	bytes_remaining -= send_size;
      }
    }

    /* The test is over. Flush the buffers to the remote end. We do a */
    /* graceful release to insure that all data has been taken by the */
    /* remote. */ 

    /* but first, if the verbosity is greater than 1, find-out what */
    /* the sctp maximum segment_size was (if possible) */
    if (verbosity > 1) {
      sctp_mss = -1;
      get_sctp_info(send_socket[0], &sctp_mss);
    }

    /* signal the server that we are all done writing, this will
     * initiate a shutdonw of one of the associations on the
     * server and trigger an event telling the server it's all done
     */ 
    sctp_sendmsg(send_socket[0], NULL, 0, remote_res->ai_addr,
		 remote_res->ai_addrlen, 0, MSG_EOF, 0, 0, 0);

    
    /* The test server will initiate closure of all associations
     * when it's done reading. We want a basic mechanism to catch this
     * and are using SCTP events for this.
     * In blocking mode, we can call recvmsg with the last socket we created.
     * In non-blocking  mode, we need to select on the socket for reading.
     * We'll assume that all returns are succefull and signify
     * closure.
     * It is sufficient to do this on a single socket in the client.
     * We choose to do it on a socket other then the one that send MSG_EOF.
     * This means that anything comming in on that socket will be a shutdown.
     */
    if (non_block) {
	fd_set readfds;

	FD_ZERO(&readfds);
	FD_SET(send_socket[num_associations-1], &readfds);
	select(send_socket[num_associations-1]+1, &readfds, NULL, NULL, NULL);
    } else {
	sctp_recvmsg(send_socket[num_associations], send_ring->buffer_ptr,
		     send_size, NULL, 0, NULL, 0);
    }
    
    /* this call will always give us the elapsed time for the test, and */
    /* will also store-away the necessaries for cpu utilization */
    
    cpu_stop(local_cpu_usage,&elapsed_time);	/* was cpu being */
						/* measured and how */
						/* long did we really */
						/* run? */
    
    /* we are finished with our sockets, so close them to prevent hitting */
    /* the limit on maximum open files. */
    for (j = 0; j < num_associations; j++)
	close(send_socket[j]);

    /* Get the statistics from the remote end. The remote will have */
    /* calculated service demand and all those interesting things. If it */
    /* wasn't supposed to care, it will return obvious values. */
    
    recv_response();
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote results obtained\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      fprintf(where,
	      "netperf: remote error %d",
	      netperf_response.content.serv_errno);
      perror("");
      fflush(where);
      
      exit(1);
    }
    
    /* We now calculate what our thruput was for the test. In the future, */
    /* we may want to include a calculation of the thruput measured by */
    /* the remote, but it should be the case that for a sctp stream test, */
    /* that the two numbers should be *very* close... We calculate */
    /* bytes_sent regardless of the way the test length was controlled. */
    /* If it was time, we needed to, and if it was by bytes, the user may */
    /* have specified a number of bytes that wasn't a multiple of the */
    /* send_size, so we really didn't send what he asked for ;-) */
    
    bytes_sent	= ntohd(sctp_stream_result->bytes_received);

    thruput	= (double) calc_thruput(bytes_sent);
    
    if (local_cpu_usage || remote_cpu_usage) {
      /* We must now do a little math for service demand and cpu */
      /* utilization for the system(s) */
      /* Of course, some of the information might be bogus because */
      /* there was no idle counter in the kernel(s). We need to make */
      /* a note of this for the user's benefit...*/
      if (local_cpu_usage) {
	
	local_cpu_utilization	= calc_cpu_util(0.0);
	local_service_demand	= calc_service_demand(bytes_sent,
						      0.0,
						      0.0,
						      0);
      }
      else {
	local_cpu_utilization	= (float) -1.0;
	local_service_demand	= (float) -1.0;
      }
      
      if (remote_cpu_usage) {
	
	remote_cpu_utilization	= sctp_stream_result->cpu_util;
	remote_service_demand	= calc_service_demand(bytes_sent,
						      0.0,
						      remote_cpu_utilization,
						      sctp_stream_result->num_cpus);
      }
      else {
	remote_cpu_utilization = (float) -1.0;
	remote_service_demand  = (float) -1.0;
      }
    }    
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= (float) -1.0;
      local_service_demand	= (float) -1.0;
      remote_cpu_utilization = (float) -1.0;
      remote_service_demand  = (float) -1.0;
    }

    /* at this point, we want to calculate the confidence information. */
    /* if debugging is on, calculate_confidence will print-out the */
    /* parameters we pass it */
    
    calculate_confidence(confidence_iteration,
			 elapsed_time,
			 thruput,
			 local_cpu_utilization,
			 remote_cpu_utilization,
			 local_service_demand,
			 remote_service_demand);
    
    
    confidence_iteration++;
  }

  /* at this point, we have finished making all the runs that we */
  /* will be making. so, we should extract what the calcuated values */
  /* are for all the confidence stuff. we could make the values */
  /* global, but that seemed a little messy, and it did not seem worth */
  /* all the mucking with header files. so, we create a routine much */
  /* like calcualte_confidence, which just returns the mean values. */
  /* raj 11/94 */

  retrieve_confident_values(&elapsed_time,
			    &thruput,
			    &local_cpu_utilization,
			    &remote_cpu_utilization,
			    &local_service_demand,
			    &remote_service_demand);

  /* We are now ready to print all the information. If the user */
  /* has specified zero-level verbosity, we will just print the */
  /* local service demand, or the remote service demand. If the */
  /* user has requested verbosity level 1, he will get the basic */
  /* "streamperf" numbers. If the user has specified a verbosity */
  /* of greater than 1, we will display a veritable plethora of */
  /* background information from outside of this block as it it */
  /* not cpu_measurement specific...  */

  if (confidence < 0) {
    /* we did not hit confidence, but were we asked to look for it? */
    if (iteration_max > 1) {
      display_confidence();
    }
  }

  if (local_cpu_usage || remote_cpu_usage) {
    local_cpu_method = format_cpu_method(cpu_method);
    remote_cpu_method = format_cpu_method(sctp_stream_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method);
      }
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,
		cpu_title,
		format_units(),
		local_cpu_method,
		remote_cpu_method);
      }
    
      fprintf(where,
	      cpu_fmt_1,		/* the format string */
	      rsr_size,		        /* remote recvbuf size */
	      lss_size,		        /* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time,		/* how long was the test */
	      thruput, 		        /* what was the xfer rate */
	      local_cpu_utilization,	/* local cpu */
	      remote_cpu_utilization,	/* remote cpu */
	      local_service_demand,	/* local service demand */
	      remote_service_demand);	/* remote service demand */
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      thruput);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }
      fprintf(where,
	      tput_fmt_1,		/* the format string */
	      rsr_size, 		/* remote recvbuf size */
	      lss_size, 		/* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time, 		/* how long did it take */
	      thruput);/* how fast did it go */
      break;
    }
  }
  
  /* it would be a good thing to include information about some of the */
  /* other parameters that may have been set for this test, but at the */
  /* moment, I do not wish to figure-out all the  formatting, so I will */
  /* just put this comment here to help remind me that it is something */
  /* that should be done at a later time. */
  
  if (verbosity > 1) {
    /* The user wanted to know it all, so we will give it to him. */
    /* This information will include as much as we can find about */
    /* sctp statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */
   
    /* this stuff needs to be worked-out in the presence of confidence */
    /* intervals and multiple iterations of the test... raj 11/94 */
 
    fprintf(where,
	    ksink_fmt,
	    "Bytes",
	    "Bytes",
	    "Bytes",
	    local_send_align,
	    remote_recv_align,
	    local_send_offset,
	    remote_recv_offset,
	    bytes_sent,
	    bytes_sent / (double)nummessages,
	    nummessages,
	    bytes_sent / (double)sctp_stream_result->recv_calls,
	    sctp_stream_result->recv_calls);
    fprintf(where,
	    ksink_fmt2,
	    sctp_mss);
    fflush(where);
#ifdef WANT_HISTOGRAM
    fprintf(where,"\n\nHistogram of time spent in send() call.\n");
    fflush(where);
    HIST_report(time_hist);
#endif /* WANT_HISTOGRAM */
  }
  
}



/* This is the server-side routine for the sctp stream test. It is */
/* implemented as one routine. I could break things-out somewhat, but */
/* didn't feel it was necessary. */

void
recv_sctp_stream_1toMany()
{
  
  struct sockaddr_in myaddr_in;
  int	s_recv;
  int 	addrlen;
  int	len;
  unsigned int	receive_calls;
  float	elapsed_time;
  double   bytes_received;
  int	msg_flags = 0;
  
  struct ring_elt *recv_ring;

  struct addrinfo *local_res;
  char local_name[BUFSIZ];
  char port_buffer[PORTBUFSIZE];

#ifdef DIRTY
  int   *message_int_ptr;
  int   dirty_count;
  int   clean_count;
  int   i;
#endif
  
#ifdef DO_SELECT
  fd_set readfds;
  struct timeval timeout;
#endif

  struct	sctp_stream_request_struct	*sctp_stream_request;
  struct	sctp_stream_response_struct	*sctp_stream_response;
  struct	sctp_stream_results_struct	*sctp_stream_results;
  
#ifdef DO_SELECT
  FD_ZERO(&readfds);
  timeout.tv_sec = 1;
  timeout.tv_usec = 0;
#endif

  sctp_stream_request	= 
    (struct sctp_stream_request_struct *)netperf_request.content.test_specific_data;
  sctp_stream_response	= 
    (struct sctp_stream_response_struct *)netperf_response.content.test_specific_data;
  sctp_stream_results	= 
    (struct sctp_stream_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_sctp_stream: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug) {
    fprintf(where,"recv_sctp_stream_1toMany: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = SCTP_STREAM_MANY_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_sctp_stream_1toMany: the response type is set...\n");
    fflush(where);
  }
  
  /* We now alter the message_ptr variable to be at the desired */
  /* alignment with the desired offset. */
  
  if (debug) {
    fprintf(where,"recv_sctp_stream_1toMany: requested alignment of %d\n",
	    sctp_stream_request->recv_alignment);
    fflush(where);
  }

  /* create_data_socket expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lss_size_req = sctp_stream_request->send_buf_size;
  lsr_size_req = sctp_stream_request->recv_buf_size;
  loc_nodelay = sctp_stream_request->no_delay;
  loc_rcvavoid = sctp_stream_request->so_rcvavoid;
  loc_sndavoid = sctp_stream_request->so_sndavoid;
  non_block = sctp_stream_request->non_blocking;

  set_hostname_and_port(local_name,
			port_buffer,
			nf_to_af(sctp_stream_request->ipfamily),
			sctp_stream_request->port);

  local_res = complete_addrinfo(local_name,
				local_name,
				port_buffer,
				nf_to_af(sctp_stream_request->ipfamily),
				SOCK_SEQPACKET,
				IPPROTO_SCTP,
				0);

  s_recv = create_data_socket(local_res);
  
  if (s_recv < 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }
  
  /* what sort of sizes did we end-up with? */
  if (sctp_stream_request->receive_size == 0) {
    if (lsr_size > 0) {
      recv_size = lsr_size;
    }
    else {
      recv_size = 4096;
    }
  }
  else {
    recv_size = sctp_stream_request->receive_size;
  }
  
  /* we want to set-up our recv_ring in a manner analagous to what we */
  /* do on the sending side. this is more for the sake of symmetry */
  /* than for the needs of say copy avoidance, but it might also be */
  /* more realistic - this way one could conceivably go with a */
  /* double-buffering scheme when taking the data an putting it into */
  /* the filesystem or something like that. raj 7/94 */

  if (recv_width == 0) {
    recv_width = (lsr_size/recv_size) + 1;
    if (recv_width == 1) recv_width++;
  }

  recv_ring = allocate_buffer_ring(recv_width,
				   recv_size,
				   sctp_stream_request->recv_alignment,
				   sctp_stream_request->recv_offset);

  if (debug) {
    fprintf(where,"recv_sctp_stream: receive alignment and offset set...\n");
    fflush(where);
  }
  
  /* Now, let's set-up the socket to listen for connections */
  if (listen(s_recv, 5) == -1) {
    netperf_response.content.serv_errno = errno;
    close(s_recv);
    send_response();
    
    exit(1);
  }

  /* now get the port number assigned by the system  */
  addrlen = sizeof(myaddr_in);
  if (getsockname(s_recv, 
		  (struct sockaddr *)&myaddr_in,
		  &addrlen) == -1){
    netperf_response.content.serv_errno = errno;
    close(s_recv);
    send_response();
    
    exit(1);
  }
  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  sctp_stream_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
  netperf_response.content.serv_errno   = 0;
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a -1 to */
  /* the initiator. */
  
  sctp_stream_response->cpu_rate = (float)0.0; 	/* assume no cpu */
  if (sctp_stream_request->measure_cpu) {
    sctp_stream_response->measure_cpu = 1;
    sctp_stream_response->cpu_rate = 
      calibrate_local_cpu(sctp_stream_request->cpu_rate);
  }
  else {
    sctp_stream_response->measure_cpu = 0;
  }
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  sctp_stream_response->send_buf_size = lss_size;
  sctp_stream_response->recv_buf_size = lsr_size;
  sctp_stream_response->no_delay = loc_nodelay;
  sctp_stream_response->so_rcvavoid = loc_rcvavoid;
  sctp_stream_response->so_sndavoid = loc_sndavoid;
  sctp_stream_response->receive_size = recv_size;

  send_response();
  

  sctp_enable_events(s_recv, SCTP_ASSOC_CHANGE_EV | SCTP_SHUTDOWN_EV);
  
  /* now that we are connected, mark the socket as non-blocking */
  if (non_block) {
      if (!set_nonblock(s_recv)) {
	close(s_recv);
	exit(1);
      }
  }


  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(sctp_stream_request->measure_cpu);
  
  /* The loop will exit when the sender does a shutdown, which will */
  /* return a length of zero   */
  
#ifdef DIRTY
    /* we want to dirty some number of consecutive integers in the buffer */
    /* we are about to recv. we may also want to bring some number of */
    /* them cleanly into the cache. The clean ones will follow any dirty */
    /* ones into the cache. */

  dirty_count = sctp_stream_request->dirty_count;
  clean_count = sctp_stream_request->clean_count;
  message_int_ptr = (int *)recv_ring->buffer_ptr;
  for (i = 0; i < dirty_count; i++) {
    *message_int_ptr = rand();
    message_int_ptr++;
  }
  for (i = 0; i < clean_count; i++) {
    dirty_count = *message_int_ptr;
    message_int_ptr++;
  }
#endif /* DIRTY */

  bytes_received = 0;
  receive_calls  = 0;
  
  while ((len = sctp_recvmsg(s_recv, recv_ring->buffer_ptr, recv_size,
			     NULL, 0,  /* we don't care who it's from */
			     NULL, &msg_flags)) != 0) {
    if (len < 0) {
      if (non_block && errno == EAGAIN) {
	Set_errno(0);
	continue;
      }
      netperf_response.content.serv_errno = errno;
      send_response();
      close(s_recv);
      exit(1);
    }

    if (msg_flags & MSG_NOTIFICATION) {
	if (sctp_process_event(s_recv, recv_ring->buffer_ptr) == SCTP_CLOSE)
	    break;

	continue;
    }
    
    bytes_received += len;
    receive_calls++;

    /* more to the next buffer in the recv_ring */
    recv_ring = recv_ring->next;

#ifdef PAUSE
    sleep(1);
#endif /* PAUSE */

#ifdef DIRTY
    message_int_ptr = (int *)(recv_ring->buffer_ptr);
    for (i = 0; i < dirty_count; i++) {
      *message_int_ptr = rand();
      message_int_ptr++;
    }
    for (i = 0; i < clean_count; i++) {
      dirty_count = *message_int_ptr;
      message_int_ptr++;
    }
#endif /* DIRTY */

#ifdef DO_SELECT
	FD_SET(s_recv,&readfds);
	select(s_recv+1,&readfds,NULL,NULL,&timeout);
#endif /* DO_SELECT */

  }
  
  /* perform a shutdown to signal the sender.  in this case, sctp
   * will close all associations on this socket
   */
  if (close(s_recv) == -1) {
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
  }
  
  cpu_stop(sctp_stream_request->measure_cpu,&elapsed_time);
  
  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_sctp_stream: got %g bytes\n",
	    bytes_received);
    fprintf(where,
	    "recv_sctp_stream: got %d recvs\n",
	    receive_calls);
    fflush(where);
  }
  
  sctp_stream_results->bytes_received	= htond(bytes_received);
  sctp_stream_results->elapsed_time	= elapsed_time;
  sctp_stream_results->recv_calls	= receive_calls;
  
  if (sctp_stream_request->measure_cpu) {
    sctp_stream_results->cpu_util	= calc_cpu_util(0.0);
  };
  
  if (debug) {
    fprintf(where,
	    "recv_sctp_stream: test complete, sending results.\n");
    fprintf(where,
	    "                 bytes_received %g receive_calls %d\n",
	    bytes_received,
	    receive_calls);
    fprintf(where,
	    "                 len %d\n",
	    len);
    fflush(where);
  }
  
  sctp_stream_results->cpu_method = cpu_method;
  sctp_stream_results->num_cpus   = lib_num_loc_cpus;
  send_response();
}


 /* this routine implements the sending (netperf) side of the SCTP_RR */
 /* test. */

void
send_sctp_rr(remote_host)
     char	remote_host[];
{
  
  char *tput_title = "\
Local /Remote\n\
Socket Size   Request  Resp.   Elapsed  Trans.\n\
Send   Recv   Size     Size    Time     Rate         \n\
bytes  Bytes  bytes    bytes   secs.    per sec   \n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1_line_1 = "\
%-6d %-6d %-6d   %-6d  %-6.2f   %7.2f   \n";
  char *tput_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *cpu_title = "\
Local /Remote\n\
Socket Size   Request Resp.  Elapsed Trans.   CPU    CPU    S.dem   S.dem\n\
Send   Recv   Size    Size   Time    Rate     local  remote local   remote\n\
bytes  bytes  bytes   bytes  secs.   per sec  %% %c    %% %c    us/Tr   us/Tr\n\n";
  
  char *cpu_fmt_0 =
    "%6.3f %c\n";
  
  char *cpu_fmt_1_line_1 = "\
%-6d %-6d %-6d  %-6d %-6.2f  %-6.2f  %-6.2f %-6.2f %-6.3f  %-6.3f\n";
  
  char *cpu_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *ksink_fmt = "\
Alignment      Offset\n\
Local  Remote  Local  Remote\n\
Send   Recv    Send   Recv\n\
%5d  %5d   %5d  %5d\n";
  
  
  int			timed_out = 0;
  float			elapsed_time;
  
  int	len;
  char	*temp_message_ptr;
  int	nummessages;
  int	send_socket;
  int	trans_remaining;
  int   msg_flags = 0;
  double	bytes_xferd;

  struct ring_elt *send_ring;
  struct ring_elt *recv_ring;
  
  int	rsp_bytes_left;
  int	rsp_bytes_recvd;
  
  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;
  double	thruput;

  struct sockaddr_storage peer;
  struct addrinfo *remote_res;
  struct addrinfo *local_res;

  struct	sctp_rr_request_struct	*sctp_rr_request;
  struct	sctp_rr_response_struct	*sctp_rr_response;
  struct	sctp_rr_results_struct	*sctp_rr_result;
  
#ifdef WANT_INTERVALS
  int	interval_count;
  sigset_t signal_set;
#endif /* WANT_INTERVALS */

  sctp_rr_request = 
    (struct sctp_rr_request_struct *)netperf_request.content.test_specific_data;
  sctp_rr_response =
    (struct sctp_rr_response_struct *)netperf_response.content.test_specific_data;
  sctp_rr_result =
    (struct sctp_rr_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  time_hist = HIST_new();
#endif /* WANT_HISTOGRAM */

  /* since we are now disconnected from the code that established the */
  /* control socket, and since we want to be able to use different */
  /* protocols and such, we are passed the name of the remote host and */
  /* must turn that into the test specific addressing information. */

  /* complete_addrinfos will either succede or exit the process */
  complete_addrinfos(&remote_res,
		     &local_res,
		     remote_host,
		     SOCK_STREAM,
		     IPPROTO_SCTP,
		     0);

  if ( print_headers ) {
    print_top_test_header("SCTP REQUEST/RESPONSE TEST", local_res, remote_res);
  }
  
  /* initialize a few counters */
  
  send_ring = NULL;
  recv_ring = NULL;
  confidence_iteration = 1;
  init_stat();

  /* we have a great-big while loop which controls the number of times */
  /* we run a particular test. this is for the calculation of a */
  /* confidence interval (I really should have stayed awake during */
  /* probstats :). If the user did not request confidence measurement */
  /* (no confidence is the default) then we will only go though the */
  /* loop once. the confidence stuff originates from the folks at IBM */

  while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
	 (confidence_iteration <= iteration_min)) {

    /* initialize a few counters. we have to remember that we might be */
    /* going through the loop more than once. */

    nummessages     = 0;
    bytes_xferd     = 0.0;
    times_up        = 0;
    timed_out       = 0;
    trans_remaining = 0;

    /* set-up the data buffers with the requested alignment and offset. */
    /* since this is a request/response test, default the send_width and */
    /* recv_width to 1 and not two raj 7/94 */

    if (send_width == 0) send_width = 1;
    if (recv_width == 0) recv_width = 1;
  
    if (send_ring == NULL) {
      send_ring = allocate_buffer_ring(send_width,
				       req_size,
				       local_send_align,
				       local_send_offset);
    }

    if (recv_ring == NULL) {
      recv_ring = allocate_buffer_ring(recv_width,
				       rsp_size,
				       local_recv_align,
				       local_recv_offset);
    }
    
    /*set up the data socket                        */
    send_socket = create_data_socket(local_res);
  
    if (send_socket < 0){
      perror("netperf: send_sctp_rr: sctp stream data socket");
      exit(1);
    }
    
    if (debug) {
      fprintf(where,"send_sctp_rr: send_socket obtained...\n");
    }
  
    /* If the user has requested cpu utilization measurements, we must */
    /* calibrate the cpu(s). We will perform this task within the tests */
    /* themselves. If the user has specified the cpu rate, then */
    /* calibrate_local_cpu will return rather quickly as it will have */
    /* nothing to do. If local_cpu_rate is zero, then we will go through */
    /* all the "normal" calibration stuff and return the rate back.*/
    
    if (local_cpu_usage) {
      local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
    }
    
    /* Tell the remote end to do a listen. The server alters the socket */
    /* paramters on the other side at this point, hence the reason for */
    /* all the values being passed in the setup message. If the user did */
    /* not specify any of the parameters, they will be passed as 0, which */
    /* will indicate to the remote that no changes beyond the system's */
    /* default should be used. Alignment is the exception, it will */
    /* default to 8, which will be no alignment alterations. */
    
    netperf_request.content.request_type	=	DO_SCTP_RR;
    sctp_rr_request->recv_buf_size	=	rsr_size_req;
    sctp_rr_request->send_buf_size	=	rss_size_req;
    sctp_rr_request->recv_alignment     =	remote_recv_align;
    sctp_rr_request->recv_offset        =	remote_recv_offset;
    sctp_rr_request->send_alignment     =	remote_send_align;
    sctp_rr_request->send_offset	=	remote_send_offset;
    sctp_rr_request->request_size	=	req_size;
    sctp_rr_request->response_size	=	rsp_size;
    sctp_rr_request->no_delay	        =	rem_nodelay;
    sctp_rr_request->measure_cpu	=	remote_cpu_usage;
    sctp_rr_request->cpu_rate	        =	remote_cpu_rate;
    sctp_rr_request->so_rcvavoid	        =	rem_rcvavoid;
    sctp_rr_request->so_sndavoid	        =	rem_sndavoid;
    if (test_time) {
      sctp_rr_request->test_length	=	test_time;
    }
    else {
      sctp_rr_request->test_length	=	test_trans * -1;
    }
    sctp_rr_request->non_blocking	= 	non_block;
    sctp_rr_request->ipfamily           = af_to_nf(remote_res->ai_family);

    if (debug > 1) {
      fprintf(where,"netperf: send_sctp_rr: requesting SCTP rr test\n");
    }
    
    send_request();
    
    /* The response from the remote will contain all of the relevant 	*/
    /* socket parameters for this test type. We will put them back into */
    /* the variables here so they can be displayed if desired.  The	*/
    /* remote will have calibrated CPU if necessary, and will have done	*/
    /* all the needed set-up we will have calibrated the cpu locally	*/
    /* before sending the request, and will grab the counter value right*/
    /* after the connect returns. The remote will grab the counter right*/
    /* after the accept call. This saves the hassle of extra messages	*/
    /* being sent for the sctp tests.					*/
  
    recv_response();
  
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote listen done.\n");
      rsr_size          = sctp_rr_response->recv_buf_size;
      rss_size          = sctp_rr_response->send_buf_size;
      rem_nodelay       = sctp_rr_response->no_delay;
      remote_cpu_usage  = sctp_rr_response->measure_cpu;
      remote_cpu_rate   = sctp_rr_response->cpu_rate;
      /* make sure that port numbers are in network order */
      set_port_number(remote_res, 
		      (unsigned short)sctp_rr_response->data_port_number);
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      fprintf(where,
	      "netperf: remote error %d",
	      netperf_response.content.serv_errno);
      perror("");
      fflush(where);
      
      exit(1);
    }
    
    /*Connect up to the remote port on the data socket  */
    if (connect(send_socket, 
		remote_res->ai_addr,
		remote_res->ai_addrlen) <0){
      perror("netperf: send_sctp_rr data socket connect failed");
      exit(1);
    }

    /* don't need events for 1-to-1 API with request-response tests */
    sctp_enable_events(send_socket, 0);

    /* set non-blocking if needed */
    if (non_block) {
       if (!set_nonblock(send_socket)) {
	    close(send_socket);
	    exit(1);
	}
    }
    
    /* Data Socket set-up is finished. If there were problems, either the */
    /* connect would have failed, or the previous response would have */
    /* indicated a problem. I failed to see the value of the extra */
    /* message after the accept on the remote. If it failed, we'll see it */
    /* here. If it didn't, we might as well start pumping data. */
    
    /* Set-up the test end conditions. For a request/response test, they */
    /* can be either time or transaction based. */
    
    if (test_time) {
      /* The user wanted to end the test after a period of time. */
      times_up = 0;
      trans_remaining = 0;
      start_timer(test_time);
    }
    else {
      /* The tester wanted to send a number of bytes. */
      trans_remaining = test_bytes;
      times_up = 1;
    }
    
    /* The cpu_start routine will grab the current time and possibly */
    /* value of the idle counter for later use in measuring cpu */
    /* utilization and/or service demand and thruput. */
    
    cpu_start(local_cpu_usage);

#ifdef WANT_INTERVALS
    if ((interval_burst) || (demo_mode)) {
      /* zero means that we never pause, so we never should need the */
      /* interval timer, unless we are in demo_mode */
      start_itimer(interval_wate);
    }
    interval_count = interval_burst;
    /* get the signal set for the call to sigsuspend */
    if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) {
      fprintf(where,
	      "send_sctp_rr: unable to get sigmask errno %d\n",
	      errno);
      fflush(where);
      exit(1);
    }
#endif /* WANT_INTERVALS */

    /* We use an "OR" to control test execution. When the test is */
    /* controlled by time, the byte count check will always return false. */
    /* When the test is controlled by byte count, the time test will */
    /* always return false. When the test is finished, the whole */
    /* expression will go false and we will stop sending data. I think I */
    /* just arbitrarily decrement trans_remaining for the timed test, but */
    /* will not do that just yet... One other question is whether or not */
    /* the send buffer and the receive buffer should be the same buffer. */

#ifdef WANT_FIRST_BURST
    {
      int i;
      for (i = 0; i < first_burst_size; i++) {
 	if((len=sctp_sendmsg(send_socket,
 			     send_ring->buffer_ptr, req_size,
 			     NULL, 0,	/* don't need addrs with 1-to-1 */
 			     0, 0, 0, 0, 0)) != req_size) {
	  /* we should never hit the end of the test in the first burst */
	  perror("send_sctp_rr: initial burst data send error");
	  exit(1);
	}
      }
    }
#endif /* WANT_FIRST_BURST */

    while ((!times_up) || (trans_remaining > 0)) {
      /* send the request. we assume that if we use a blocking socket, */
      /* the request will be sent at one shot. */
      
#ifdef WANT_HISTOGRAM
      /* timestamp just before our call to send, and then again just */
      /* after the receive raj 8/94 */
      HIST_timestamp(&time_one);
#endif /* WANT_HISTOGRAM */
      
      while ((len=sctp_sendmsg(send_socket,
			       send_ring->buffer_ptr, req_size,
			       NULL, 0, /* don't need addrs with 1-to-1 */
			       0, 0, 0, 0, 0)) != req_size) {
	if (non_block && errno == EAGAIN) {
	    /* try sending again */
	    continue;
	} else if (SOCKET_EINTR(len) || (errno == 0)) {
	  /* we hit the end of a */
	  /* timed test. */
	  timed_out = 1;
	  break;
	}
	perror("send_sctp_rr: data send error");
	exit(1);
      }

      if (timed_out) {
	/* we timed out while sending. break out another level */
	break;
      }
      send_ring = send_ring->next;
      
      /* receive the response */
      rsp_bytes_left = rsp_size;
      temp_message_ptr  = recv_ring->buffer_ptr;
      do {
	msg_flags = 0;
	if ((rsp_bytes_recvd=sctp_recvmsg(send_socket,
					 temp_message_ptr, rsp_bytes_left,
					 NULL, 0,
					 NULL, &msg_flags)) < 0) {
	  if (errno == EINTR) {
	    /* We hit the end of a timed test. */
	    timed_out = 1;
	    break;
	  } else if (non_block && errno == EAGAIN) {
	      continue;
	  }
	  perror("send_sctp_rr: data recv error");
	  exit(1);
	}
	rsp_bytes_left -= rsp_bytes_recvd;
	temp_message_ptr  += rsp_bytes_recvd;
      }	while (!(msg_flags & MSG_EOR));
      
      recv_ring = recv_ring->next;
      
      if (timed_out) {
	/* we may have been in a nested while loop - we need */
	/* another call to break. */
	break;
      }
      
#ifdef WANT_HISTOGRAM
      HIST_timestamp(&time_two);
      HIST_add(time_hist,delta_micro(&time_one,&time_two));
#endif /* WANT_HISTOGRAM */
#ifdef WANT_INTERVALS      
      if (demo_mode) {
	units_this_tick += 1;
      }
      /* in this case, the interval count is the count-down couter */
      /* to decide to sleep for a little bit */
      if ((interval_burst) && (--interval_count == 0)) {
	/* call sigsuspend and wait for the interval timer to get us */
	/* out */
	if (debug > 1) {
	  fprintf(where,"about to suspend\n");
	  fflush(where);
	}
	if (sigsuspend(&signal_set) == EFAULT) {
	  fprintf(where,
		  "send_sctp_rr: fault with signal set!\n");
	  fflush(where);
	  exit(1);
	}
	interval_count = interval_burst;
      }
#endif /* WANT_INTERVALS */
      
      nummessages++;          
      if (trans_remaining) {
	trans_remaining--;
      }
      
      if (debug > 3) {
	if ((nummessages % 100) == 0) {
	  fprintf(where,
		  "Transaction %d completed\n",
		  nummessages);
	  fflush(where);
	}
      }
    }

    /* At this point we used to call shutdown on the data socket to be */
    /* sure all the data was delivered, but this was not germane in a */
    /* request/response test, and it was causing the tests to "hang" when */
    /* they were being controlled by time. So, I have replaced this */
    /* shutdown call with a call to close that can be found later in the */
    /* procedure. */
    
    /* this call will always give us the elapsed time for the test, and */
    /* will also store-away the necessaries for cpu utilization */
    
    cpu_stop(local_cpu_usage,&elapsed_time);	/* was cpu being */
						/* measured? how long */
						/* did we really run? */
    
    /* Get the statistics from the remote end. The remote will have */
    /* calculated CPU utilization. If it wasn't supposed to care, it */
    /* will return obvious values. */ 
    
    recv_response();
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote results obtained\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      fprintf(where,"netperf: remote error %d",
	      netperf_response.content.serv_errno);
      perror("");
      fflush(where);
      exit(1);
    }
    
    /* We now calculate what our throughput was for the test. */
  
    bytes_xferd	= (req_size * nummessages) + (rsp_size * nummessages);
    thruput	= nummessages/elapsed_time;
  
    if (local_cpu_usage || remote_cpu_usage) {
      /* We must now do a little math for service demand and cpu */
      /* utilization for the system(s) */
      /* Of course, some of the information might be bogus because */
      /* there was no idle counter in the kernel(s). We need to make */
      /* a note of this for the user's benefit...*/
      if (local_cpu_usage) {
	local_cpu_utilization = calc_cpu_util(0.0);
	/* since calc_service demand is doing ms/Kunit we will */
	/* multiply the number of transaction by 1024 to get */
	/* "good" numbers */
	local_service_demand  = calc_service_demand((double) nummessages*1024,
						    0.0,
						    0.0,
						    0);
      }
      else {
	local_cpu_utilization	= (float) -1.0;
	local_service_demand	= (float) -1.0;
      }
      
      if (remote_cpu_usage) {
	remote_cpu_utilization = sctp_rr_result->cpu_util;
	/* since calc_service demand is doing ms/Kunit we will */
	/* multiply the number of transaction by 1024 to get */
	/* "good" numbers */
	remote_service_demand = calc_service_demand((double) nummessages*1024,
						    0.0,
						    remote_cpu_utilization,
						    sctp_rr_result->num_cpus);
      }
      else {
	remote_cpu_utilization = (float) -1.0;
	remote_service_demand  = (float) -1.0;
      }
      
    }
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= (float) -1.0;
      local_service_demand	= (float) -1.0;
      remote_cpu_utilization = (float) -1.0;
      remote_service_demand  = (float) -1.0;
    }

    /* at this point, we want to calculate the confidence information. */
    /* if debugging is on, calculate_confidence will print-out the */
    /* parameters we pass it */
    
    calculate_confidence(confidence_iteration,
			 elapsed_time,
			 thruput,
			 local_cpu_utilization,
			 remote_cpu_utilization,
			 local_service_demand,
			 remote_service_demand);
    
    
    confidence_iteration++;

    /* we are now done with the socket, so close it */
    close(send_socket);

  }

  retrieve_confident_values(&elapsed_time,
			    &thruput,
			    &local_cpu_utilization,
			    &remote_cpu_utilization,
			    &local_service_demand,
			    &remote_service_demand);

  /* We are now ready to print all the information. If the user */
  /* has specified zero-level verbosity, we will just print the */
  /* local service demand, or the remote service demand. If the */
  /* user has requested verbosity level 1, he will get the basic */
  /* "streamperf" numbers. If the user has specified a verbosity */
  /* of greater than 1, we will display a veritable plethora of */
  /* background information from outside of this block as it it */
  /* not cpu_measurement specific...  */

  if (confidence < 0) {
    /* we did not hit confidence, but were we asked to look for it? */
    if (iteration_max > 1) {
      display_confidence();
    }
  }

  if (local_cpu_usage || remote_cpu_usage) {
    local_cpu_method = format_cpu_method(cpu_method);
    remote_cpu_method = format_cpu_method(sctp_rr_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method);
      }
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,
		cpu_title,
		local_cpu_method,
		remote_cpu_method);
      }

      fprintf(where,
	      cpu_fmt_1_line_1,		/* the format string */
	      lss_size,		/* local sendbuf size */
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* guess */
	      elapsed_time,		/* how long was the test */
	      thruput,
	      local_cpu_utilization,	/* local cpu */
	      remote_cpu_utilization,	/* remote cpu */
	      local_service_demand,	/* local service demand */
	      remote_service_demand);	/* remote service demand */
      fprintf(where,
	      cpu_fmt_1_line_2,
	      rss_size,
	      rsr_size);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      thruput);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }

      fprintf(where,
	      tput_fmt_1_line_1,	/* the format string */
	      lss_size,
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* how large were the responses */
	      elapsed_time, 		/* how long did it take */
	      thruput);
      fprintf(where,
	      tput_fmt_1_line_2,
	      rss_size, 		/* remote recvbuf size */
	      rsr_size);
      
      break;
    }
  }
  
  /* it would be a good thing to include information about some of the */
  /* other parameters that may have been set for this test, but at the */
  /* moment, I do not wish to figure-out all the  formatting, so I will */
  /* just put this comment here to help remind me that it is something */
  /* that should be done at a later time. */
  
  /* how to handle the verbose information in the presence of */
  /* confidence intervals is yet to be determined... raj 11/94 */
  if (verbosity > 1) {
    /* The user wanted to know it all, so we will give it to him. */
    /* This information will include as much as we can find about */
    /* TCP statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */
    
    fprintf(where,
	    ksink_fmt,
	    local_send_align,
	    remote_recv_offset,
	    local_send_offset,
	    remote_recv_offset);

#ifdef WANT_HISTOGRAM
    fprintf(where,"\nHistogram of request/response times\n");
    fflush(where);
    HIST_report(time_hist);
#endif /* WANT_HISTOGRAM */

  }
  
}


 /* this routine implements the receive (netserver) side of a TCP_RR */
 /* test */
void
recv_sctp_rr()
{
  
  struct ring_elt *send_ring;
  struct ring_elt *recv_ring;
  
  struct addrinfo *local_res;
  char local_name[BUFSIZ];
  char port_buffer[PORTBUFSIZE];

  struct sockaddr_in        myaddr_in, peeraddr_in;
  int	s_listen, s_data;
  int 	addrlen;
  char	*temp_message_ptr;
  int	trans_received;
  int	trans_remaining;
  int	bytes_sent;
  int	request_bytes_recvd;
  int	request_bytes_remaining;
  int	timed_out = 0;
  float	elapsed_time;
  
  struct	sctp_rr_request_struct	*sctp_rr_request;
  struct	sctp_rr_response_struct	*sctp_rr_response;
  struct	sctp_rr_results_struct	*sctp_rr_results;
  
  sctp_rr_request = 
    (struct sctp_rr_request_struct *)netperf_request.content.test_specific_data;
  sctp_rr_response =
    (struct sctp_rr_response_struct *)netperf_response.content.test_specific_data;
  sctp_rr_results =
    (struct sctp_rr_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_sctp_rr: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug) {
    fprintf(where,"recv_sctp_rr: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = SCTP_RR_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_sctp_rr: the response type is set...\n");
    fflush(where);
  }
  
  /* allocate the recv and send rings with the requested alignments */
  /* and offsets. raj 7/94 */
  if (debug) {
    fprintf(where,"recv_sctp_rr: requested recv alignment of %d offset %d\n",
	    sctp_rr_request->recv_alignment,
	    sctp_rr_request->recv_offset);
    fprintf(where,"recv_sctp_rr: requested send alignment of %d offset %d\n",
	    sctp_rr_request->send_alignment,
	    sctp_rr_request->send_offset);
    fflush(where);
  }

  /* at some point, these need to come to us from the remote system */
  if (send_width == 0) send_width = 1;
  if (recv_width == 0) recv_width = 1;

  send_ring = allocate_buffer_ring(send_width,
				   sctp_rr_request->response_size,
				   sctp_rr_request->send_alignment,
				   sctp_rr_request->send_offset);

  recv_ring = allocate_buffer_ring(recv_width,
				   sctp_rr_request->request_size,
				   sctp_rr_request->recv_alignment,
				   sctp_rr_request->recv_offset);

  
  /* Grab a socket to listen on, and then listen on it. */
  
  if (debug) {
    fprintf(where,"recv_sctp_rr: grabbing a socket...\n");
    fflush(where);
  }

  /* create_data_socket expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lss_size_req = sctp_rr_request->send_buf_size;
  lsr_size_req = sctp_rr_request->recv_buf_size;
  loc_nodelay = sctp_rr_request->no_delay;
  loc_rcvavoid = sctp_rr_request->so_rcvavoid;
  loc_sndavoid = sctp_rr_request->so_sndavoid;
  non_block = sctp_rr_request->non_blocking;

  set_hostname_and_port(local_name,
			port_buffer,
			nf_to_af(sctp_rr_request->ipfamily),
			sctp_rr_request->port);

  local_res = complete_addrinfo(local_name,
				local_name,
				port_buffer,
				nf_to_af(sctp_rr_request->ipfamily),
				SOCK_STREAM,
				IPPROTO_SCTP,
				0);

  s_listen = create_data_socket(local_res);
  
  if (s_listen < 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    
    exit(1);
  }
  
  /* Now, let's set-up the socket to listen for connections */
  if (listen(s_listen, 5) == -1) {
    netperf_response.content.serv_errno = errno;
    close(s_listen);
    send_response();
    
    exit(1);
  }
  
  
  /* now get the port number assigned by the system  */
  addrlen = sizeof(myaddr_in);
  if (getsockname(s_listen,
		  (struct sockaddr *)&myaddr_in, &addrlen) == -1){
    netperf_response.content.serv_errno = errno;
    close(s_listen);
    send_response();
    
    exit(1);
  }
  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  sctp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
  netperf_response.content.serv_errno   = 0;
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a 0.0 to */
  /* the initiator. */
  
  sctp_rr_response->cpu_rate = (float)0.0; 	/* assume no cpu */
  sctp_rr_response->measure_cpu = 0;

  if (sctp_rr_request->measure_cpu) {
    sctp_rr_response->measure_cpu = 1;
    sctp_rr_response->cpu_rate = calibrate_local_cpu(sctp_rr_request->cpu_rate);
  }
  
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  sctp_rr_response->send_buf_size = lss_size;
  sctp_rr_response->recv_buf_size = lsr_size;
  sctp_rr_response->no_delay = loc_nodelay;
  sctp_rr_response->so_rcvavoid = loc_rcvavoid;
  sctp_rr_response->so_sndavoid = loc_sndavoid;
  sctp_rr_response->test_length = sctp_rr_request->test_length;
  send_response();
  
  addrlen = sizeof(peeraddr_in);

  if ((s_data = accept(s_listen,
		       (struct sockaddr *)&peeraddr_in,
		       &addrlen)) == -1) {
    /* Let's just punt. The remote will be given some information */
    close(s_listen);
    
    exit(1);
  }

  /* we do not need events on a 1-to-1 RR test.  The test will finish
   * once all transactions are done.
   */

  /* now that we are connected, mark the socket as non-blocking */
  if (non_block) {
    if (!set_nonblock(s_data)) {
      perror("netperf: set_nonblock");
	exit(1);
    }
  }

#ifdef KLUDGE_SOCKET_OPTIONS
  /* this is for those systems which *INCORRECTLY* fail to pass */
  /* attributes across an accept() call. Including this goes against */
  /* my better judgement :( raj 11/95 */

  kludge_socket_options(s_data);

#endif /* KLUDGE_SOCKET_OPTIONS */

  if (debug) {
    fprintf(where,"recv_sctp_rr: accept completes on the data connection.\n");
    fflush(where);
  }
  
  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(sctp_rr_request->measure_cpu);
  
  /* The loop will exit when we hit the end of the test time, or when */
  /* we have exchanged the requested number of transactions. */
  
  if (sctp_rr_request->test_length > 0) {
    times_up = 0;
    trans_remaining = 0;
    start_timer(sctp_rr_request->test_length + PAD_TIME);
  }
  else {
    times_up = 1;
    trans_remaining = sctp_rr_request->test_length * -1;
  }

  trans_received = 0;
  
  while ((!times_up) || (trans_remaining > 0)) {
    int msg_flags = 0;

    temp_message_ptr = recv_ring->buffer_ptr;
    request_bytes_remaining = sctp_rr_request->request_size;
    while(!(msg_flags & MSG_EOR)) {
      if((request_bytes_recvd=sctp_recvmsg(s_data,
					temp_message_ptr,
					request_bytes_remaining,
					NULL, 0,
					NULL, &msg_flags)) < 0) {
	if (errno == EINTR) {
	  /* the timer popped */
	  timed_out = 1;
	  break;
	} else if (non_block && errno == EAGAIN) {
	    continue;  /* while request_bytes_remaining */
	}
	netperf_response.content.serv_errno = errno;
	send_response();
	exit(1);
      }
      request_bytes_remaining -= request_bytes_recvd;
      temp_message_ptr += request_bytes_recvd;
    }

    recv_ring = recv_ring->next;

    if (timed_out) {
      /* we hit the end of the test based on time - lets */
      /* bail out of here now... */
      if (debug) {
	fprintf(where,"yo55\n");
	fflush(where);
      }						
      break;
    }
    

    /* Now, send the response to the remote
     * In 1-to-1 API destination addr is not needed.
     */
    while ((bytes_sent=sctp_sendmsg(s_data,
				    send_ring->buffer_ptr,
				    sctp_rr_request->response_size,
				    NULL, 0,
				    0, 0, 0, 0, 0)) == -1) {
      if (errno == EINTR) {
	/* the test timer has popped */
	timed_out = 1;
	break;
      } else if (non_block && errno == EAGAIN) {
	 continue;
      }

      netperf_response.content.serv_errno = 982;
      send_response();
      exit(1);
    }

    if (timed_out) {
      /* we hit the end of the test based on time - lets */
      /* bail out of here now... */
      if (debug) {
	fprintf(where,"yo6\n");
	fflush(where);
      }						
      break;
    }
    
    send_ring = send_ring->next;

    trans_received++;
    if (trans_remaining) {
      trans_remaining--;
    }
  }
  
  
  /* The loop now exits due to timeout or transaction count being */
  /* reached */
  
  cpu_stop(sctp_rr_request->measure_cpu,&elapsed_time);
  
  stop_timer();

  if (timed_out) {
    /* we ended the test by time, which was at least 2 seconds */
    /* longer than we wanted to run. so, we want to subtract */
    /* PAD_TIME from the elapsed_time. */
    elapsed_time -= PAD_TIME;
  }

  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_sctp_rr: got %d transactions\n",
	    trans_received);
    fflush(where);
  }
  
  sctp_rr_results->bytes_received = (trans_received * 
				    (sctp_rr_request->request_size + 
				     sctp_rr_request->response_size));
  sctp_rr_results->trans_received = trans_received;
  sctp_rr_results->elapsed_time   = elapsed_time;
  sctp_rr_results->cpu_method     = cpu_method;
  sctp_rr_results->num_cpus       = lib_num_loc_cpus;
  if (sctp_rr_request->measure_cpu) {
    sctp_rr_results->cpu_util	= calc_cpu_util(elapsed_time);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_sctp_rr: test complete, sending results.\n");
    fflush(where);
  }
  
  /* we are now done with the sockets */
  send_response();

  close(s_data);
  close(s_listen);
  
}



/* this routine implements the sending (netperf) side of the
   SCTP_RR_1TOMANY test */

void
send_sctp_rr_1toMany(remote_host)
     char	remote_host[];
{
  
  char *tput_title = "\
Local /Remote\n\
Socket Size   Request  Resp.   Elapsed  Trans.\n\
Send   Recv   Size     Size    Time     Rate         \n\
bytes  Bytes  bytes    bytes   secs.    per sec   \n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1_line_1 = "\
%-6d %-6d %-6d   %-6d  %-6.2f   %7.2f   \n";
  char *tput_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *cpu_title = "\
Local /Remote\n\
Socket Size   Request Resp.  Elapsed Trans.   CPU    CPU    S.dem   S.dem\n\
Send   Recv   Size    Size   Time    Rate     local  remote local   remote\n\
bytes  bytes  bytes   bytes  secs.   per sec  %% %c    %% %c    us/Tr   us/Tr\n\n";
  
  char *cpu_fmt_0 =
    "%6.3f %c\n";
  
  char *cpu_fmt_1_line_1 = "\
%-6d %-6d %-6d  %-6d %-6.2f  %-6.2f  %-6.2f %-6.2f %-6.3f  %-6.3f\n";
  
  char *cpu_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *ksink_fmt = "\
Alignment      Offset\n\
Local  Remote  Local  Remote\n\
Send   Recv    Send   Recv\n\
%5d  %5d   %5d  %5d\n";
  
  
  int			timed_out = 0;
  float			elapsed_time;
  
  int	len, j = 0;
  char	*temp_message_ptr;
  int	nummessages;
  int	*send_socket;
  int	trans_remaining;
  double	bytes_xferd;
  int   msg_flags = 0;

  struct ring_elt *send_ring;
  struct ring_elt *recv_ring;
  
  int	rsp_bytes_left;
  int	rsp_bytes_recvd;
  
  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;
  double	thruput;

  struct sockaddr_storage peer;
  struct addrinfo *local_res;
  struct addrinfo *remote_res;

  struct	sctp_rr_request_struct	*sctp_rr_request;
  struct	sctp_rr_response_struct	*sctp_rr_response;
  struct	sctp_rr_results_struct	*sctp_rr_result;
  
#ifdef WANT_INTERVALS
  int	interval_count;
  sigset_t signal_set;
#endif /* WANT_INTERVALS */

  sctp_rr_request = 
    (struct sctp_rr_request_struct *)netperf_request.content.test_specific_data;
  sctp_rr_response =
    (struct sctp_rr_response_struct *)netperf_response.content.test_specific_data;
  sctp_rr_result =
    (struct sctp_rr_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  time_hist = HIST_new();
#endif /* WANT_HISTOGRAM */

  /* since we are now disconnected from the code that established the */
  /* control socket, and since we want to be able to use different */
  /* protocols and such, we are passed the name of the remote host and */
  /* must turn that into the test specific addressing information. */

  complete_addrinfos(&remote_res,
		     &local_res,
		     remote_host,
		     SOCK_SEQPACKET,
		     IPPROTO_SCTP,
		     0);

  if ( print_headers ) {
    print_top_test_header("SCTP 1-TO-MANY REQUEST/RESPONSE TEST",local_res,remote_res);
  }

  /* initialize a few counters */
  
  send_ring = NULL;
  recv_ring = NULL;
  confidence_iteration = 1;
  init_stat();

  send_socket = malloc(sizeof(int) * num_associations);
  if (send_socket == NULL) {
      fprintf(where,
	      "Could not create the socket array for %d associations",
	      num_associations);
      fflush(where);
      exit(1);
  }

  /* we have a great-big while loop which controls the number of times */
  /* we run a particular test. this is for the calculation of a */
  /* confidence interval (I really should have stayed awake during */
  /* probstats :). If the user did not request confidence measurement */
  /* (no confidence is the default) then we will only go though the */
  /* loop once. the confidence stuff originates from the folks at IBM */

  while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
	 (confidence_iteration <= iteration_min)) {

    /* initialize a few counters. we have to remember that we might be */
    /* going through the loop more than once. */

    nummessages     = 0;
    bytes_xferd     = 0.0;
    times_up        = 0;
    timed_out       = 0;
    trans_remaining = 0;

    /* set-up the data buffers with the requested alignment and offset. */
    /* since this is a request/response test, default the send_width and */
    /* recv_width to 1 and not two raj 7/94 */

    if (send_width == 0) send_width = 1;
    if (recv_width == 0) recv_width = 1;
  
    if (send_ring == NULL) {
      send_ring = allocate_buffer_ring(send_width,
				       req_size,
				       local_send_align,
				       local_send_offset);
    }

    if (recv_ring == NULL) {
      recv_ring = allocate_buffer_ring(recv_width,
				       rsp_size,
				       local_recv_align,
				       local_recv_offset);
    }
    
    /* If the user has requested cpu utilization measurements, we must */
    /* calibrate the cpu(s). We will perform this task within the tests */
    /* themselves. If the user has specified the cpu rate, then */
    /* calibrate_local_cpu will return rather quickly as it will have */
    /* nothing to do. If local_cpu_rate is zero, then we will go through */
    /* all the "normal" calibration stuff and return the rate back.*/
    
    if (local_cpu_usage) {
      local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
    }
    
    /* Tell the remote end to do a listen. The server alters the socket */
    /* paramters on the other side at this point, hence the reason for */
    /* all the values being passed in the setup message. If the user did */
    /* not specify any of the parameters, they will be passed as 0, which */
    /* will indicate to the remote that no changes beyond the system's */
    /* default should be used. Alignment is the exception, it will */
    /* default to 8, which will be no alignment alterations. */
    
    netperf_request.content.request_type =	DO_SCTP_RR_MANY;
    sctp_rr_request->recv_buf_size     =	rsr_size_req;
    sctp_rr_request->send_buf_size     =	rss_size_req;
    sctp_rr_request->recv_alignment    =	remote_recv_align;
    sctp_rr_request->recv_offset       =	remote_recv_offset;
    sctp_rr_request->send_alignment    =	remote_send_align;
    sctp_rr_request->send_offset       =	remote_send_offset;
    sctp_rr_request->request_size      =	req_size;
    sctp_rr_request->response_size     =	rsp_size;
    sctp_rr_request->no_delay	       =	rem_nodelay;
    sctp_rr_request->measure_cpu       =	remote_cpu_usage;
    sctp_rr_request->cpu_rate	       =	remote_cpu_rate;
    sctp_rr_request->so_rcvavoid       =	rem_rcvavoid;
    sctp_rr_request->so_sndavoid       =	rem_sndavoid;
    if (test_time) {
      sctp_rr_request->test_length     =	test_time;
    }
    else {
      sctp_rr_request->test_length     =	test_trans * num_associations
						* -1;
    }
    sctp_rr_request->non_blocking      = 	non_block;
    sctp_rr_request->port              =       atoi(remote_data_port);
    sctp_rr_request->ipfamily          =       af_to_nf(remote_res->ai_family);
    if (debug > 1) {
      fprintf(where,"netperf: send_sctp_rr_1toMany: requesting SCTP rr test\n");
    }
    
    send_request();
    
    /* The response from the remote will contain all of the relevant 	*/
    /* socket parameters for this test type. We will put them back into */
    /* the variables here so they can be displayed if desired.  The	*/
    /* remote will have calibrated CPU if necessary, and will have done	*/
    /* all the needed set-up we will have calibrated the cpu locally	*/
    /* before sending the request, and will grab the counter value right*/
    /* after the connect returns. The remote will grab the counter right*/
    /* after the accept call. This saves the hassle of extra messages	*/
    /* being sent for the sctp tests.					*/
  
    recv_response();
  
    if (!netperf_response.content.serv_errno) {
      rsr_size          = sctp_rr_response->recv_buf_size;
      rss_size          = sctp_rr_response->send_buf_size;
      rem_nodelay       = sctp_rr_response->no_delay;
      remote_cpu_usage  = sctp_rr_response->measure_cpu;
      remote_cpu_rate   = sctp_rr_response->cpu_rate;
      /* make sure that port numbers are in network order */
      set_port_number(remote_res,
		      (unsigned short)sctp_rr_response->data_port_number);
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      fprintf(where,
	      "netperf: remote error %d",
	      netperf_response.content.serv_errno);
      perror("");
      fflush(where);
      
      exit(1);
    }
    
    /*set up the data socket list  */
    for (j = 0; j < num_associations; j++) {
      send_socket[j] = create_data_socket(local_res);
  
      if (send_socket < 0){
	perror("netperf: send_sctp_rr_1toMany: sctp stream data socket");
	exit(1);
      }
      
      /*Connect up to the remote port on the data socket  */
      if (connect(send_socket[j], 
		  remote_res->ai_addr,
		  remote_res->ai_addrlen) < 0){
	perror("netperf: data socket connect failed");
	
	exit(1);
      }
      
      /* The client end of the 1-to-Many test uses 1-to-1 sockets.
       * it doesn't need events.
       */
      sctp_enable_events(send_socket[j], 0);
      
      if (non_block) {
        if (!set_nonblock(send_socket[j])) {
	  close(send_socket[j]);
	  exit(1);
	}
      }
    }
    
    /* Data Socket set-up is finished. If there were problems, either the */
    /* connect would have failed, or the previous response would have */
    /* indicated a problem. I failed to see the value of the extra */
    /* message after the accept on the remote. If it failed, we'll see it */
    /* here. If it didn't, we might as well start pumping data. */
    
    /* Set-up the test end conditions. For a request/response test, they */
    /* can be either time or transaction based. */
    
    if (test_time) {
      /* The user wanted to end the test after a period of time. */
      times_up = 0;
      trans_remaining = 0;
      start_timer(test_time);
    }
    else {
      /* The tester wanted to send a number of bytes. */
      trans_remaining = test_bytes * num_associations;
      times_up = 1;
    }
    
    /* The cpu_start routine will grab the current time and possibly */
    /* value of the idle counter for later use in measuring cpu */
    /* utilization and/or service demand and thruput. */
    
    cpu_start(local_cpu_usage);

#ifdef WANT_INTERVALS
    if ((interval_burst) || (demo_mode)) {
      /* zero means that we never pause, so we never should need the */
      /* interval timer, unless we are in demo_mode */
      start_itimer(interval_wate);
    }
    interval_count = interval_burst;
    /* get the signal set for the call to sigsuspend */
    if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) {
      fprintf(where,
	      "send_sctp_rr_1toMany: unable to get sigmask errno %d\n",
	      errno);
      fflush(where);
      exit(1);
    }
#endif /* WANT_INTERVALS */

    /* We use an "OR" to control test execution. When the test is */
    /* controlled by time, the byte count check will always return false. */
    /* When the test is controlled by byte count, the time test will */
    /* always return false. When the test is finished, the whole */
    /* expression will go false and we will stop sending data. I think I */
    /* just arbitrarily decrement trans_remaining for the timed test, but */
    /* will not do that just yet... One other question is whether or not */
    /* the send buffer and the receive buffer should be the same buffer. */

#ifdef WANT_FIRST_BURST
    {
      int i;
      for (j = 0; j < num_associations; j++) {
	  for (i = 0; i < first_burst_size; i++) {
	    if((len=sctp_sendmsg(send_socket[j],
			 send_ring->buffer_ptr, send_size,
			 remote_res->ai_addr,
			 remote_res->ai_addrlen,
			 0, 0, 0, 0, 0)) != req_size) {
	      /* we should never hit the end of the test in the first burst */
	      perror("send_sctp_rr_1toMany: initial burst data send error");
	      exit(1);
	    }
	  }
      }
    }
#endif /* WANT_FIRST_BURST */

    while ((!times_up) || (trans_remaining > 0)) {
      /* send the request. we assume that if we use a blocking socket, */
      /* the request will be sent at one shot. */
      
      /* this is a fairly poor way of testing 1toMany connections.
       * For each association we measure round trip time to account for
       * any delay in lookups and delivery.  To stress the server a bit
       * more we would need a distributed client test, or at least multiple
       * processes.  I want to force as much paralellism as possible, but
       * this will do for the fist take. vlad
       */
      for (j = 0; j < num_associations; j++) {
#ifdef WANT_HISTOGRAM
	/* timestamp just before our call to send, and then again just */
	/* after the receive raj 8/94 */
	gettimeofday(&time_one,NULL);
#endif /* WANT_HISTOGRAM */
	
	while ((len=sctp_sendmsg(send_socket[j],
				 send_ring->buffer_ptr, send_size,
				 remote_res->ai_addr,
				 remote_res->ai_addrlen,
				 0, 0, 0, 0, 0)) != req_size) {
	  if (non_block && errno == EAGAIN) {
	    /* try sending again */
	    continue;
	  } else if ((errno == EINTR) || (errno == 0)) {
	    /* we hit the end of a */
	    /* timed test. */
	    timed_out = 1;
	    break;
	  }
	  perror("send_sctp_rr_1toMany: data send error");
	  exit(1);
	}

	if (timed_out) {
	  /* we may have been in a nested while loop - we need */
	  /* another call to break. */
	  break;
	}
	
	/* setup for the next time */
	send_ring = send_ring->next;
	
	rsp_bytes_left = rsp_size;
	temp_message_ptr  = recv_ring->buffer_ptr;
	while (!(msg_flags & MSG_EOR)) {
	  if((rsp_bytes_recvd = sctp_recvmsg(send_socket[j],
					     temp_message_ptr,
					     rsp_bytes_left,
					     NULL, 0,
					     NULL, &msg_flags)) < 0) {
	    if (errno == EINTR) {
	      /* We hit the end of a timed test. */
	      timed_out = 1;
	      break;
	    } else if (non_block && errno == EAGAIN) {
	      continue;
	    }
	    perror("send_sctp_rr_1toMany: data recv error");
	    exit(1);
	  }
	  rsp_bytes_left -= rsp_bytes_recvd;
	  temp_message_ptr  += rsp_bytes_recvd;
	}	
	recv_ring = recv_ring->next;
	
	if (timed_out) {
	  /* we may have been in a nested while loop - we need */
	  /* another call to break. */
	  break;
	}
	
#ifdef WANT_HISTOGRAM
	gettimeofday(&time_two,NULL);
	HIST_add(time_hist,delta_micro(&time_one,&time_two));
#endif /* WANT_HISTOGRAM */
	
	nummessages++;          
	if (trans_remaining) {
	  trans_remaining--;
	}
	
	if (debug > 3) {
	  if ((nummessages % 100) == 0) {
	    fprintf(where,
		    "Transaction %d completed\n",
		    nummessages);
	    fflush(where);
	  }
	}
      }
    }
    
    /* At this point we used to call shutdown on the data socket to be */
    /* sure all the data was delivered, but this was not germane in a */
    /* request/response test, and it was causing the tests to "hang" when */
    /* they were being controlled by time. So, I have replaced this */
    /* shutdown call with a call to close that can be found later in the */
    /* procedure. */
    
    /* this call will always give us the elapsed time for the test, and */
    /* will also store-away the necessaries for cpu utilization */
    
    cpu_stop(local_cpu_usage,&elapsed_time);	/* was cpu being */
						/* measured? how long */
						/* did we really run? */
    
    /* Get the statistics from the remote end. The remote will have */
    /* calculated CPU utilization. If it wasn't supposed to care, it */
    /* will return obvious values. */ 
    
    recv_response();
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote results obtained\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      fprintf(where,"netperf: remote error %d",
	      netperf_response.content.serv_errno);
      perror("");
      fflush(where);
      exit(1);
    }
    
    /* We now calculate what our throughput was for the test. */
  
    bytes_xferd	= (req_size * nummessages) + (rsp_size * nummessages);
    thruput	= nummessages/elapsed_time;
  
    if (local_cpu_usage || remote_cpu_usage) {
      /* We must now do a little math for service demand and cpu */
      /* utilization for the system(s) */
      /* Of course, some of the information might be bogus because */
      /* there was no idle counter in the kernel(s). We need to make */
      /* a note of this for the user's benefit...*/
      if (local_cpu_usage) {
	local_cpu_utilization = calc_cpu_util(0.0);
	/* since calc_service demand is doing ms/Kunit we will */
	/* multiply the number of transaction by 1024 to get */
	/* "good" numbers */
	local_service_demand  = calc_service_demand((double) nummessages*1024,
						    0.0,
						    0.0,
						    0);
      }
      else {
	local_cpu_utilization	= (float) -1.0;
	local_service_demand	= (float) -1.0;
      }
      
      if (remote_cpu_usage) {
	remote_cpu_utilization = sctp_rr_result->cpu_util;
	/* since calc_service demand is doing ms/Kunit we will */
	/* multiply the number of transaction by 1024 to get */
	/* "good" numbers */
	remote_service_demand = calc_service_demand((double) nummessages*1024,
						    0.0,
						    remote_cpu_utilization,
						    sctp_rr_result->num_cpus);
      }
      else {
	remote_cpu_utilization = (float) -1.0;
	remote_service_demand  = (float) -1.0;
      }
      
    }
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= (float) -1.0;
      local_service_demand	= (float) -1.0;
      remote_cpu_utilization = (float) -1.0;
      remote_service_demand  = (float) -1.0;
    }

    /* at this point, we want to calculate the confidence information. */
    /* if debugging is on, calculate_confidence will print-out the */
    /* parameters we pass it */
    
    calculate_confidence(confidence_iteration,
			 elapsed_time,
			 thruput,
			 local_cpu_utilization,
			 remote_cpu_utilization,
			 local_service_demand,
			 remote_service_demand);
    
    
    confidence_iteration++;

    /* we are now done with the socket, so close it */
    for (j = 0; j < num_associations; j++)
	close(send_socket[j]);
  }

  retrieve_confident_values(&elapsed_time,
			    &thruput,
			    &local_cpu_utilization,
			    &remote_cpu_utilization,
			    &local_service_demand,
			    &remote_service_demand);

  /* We are now ready to print all the information. If the user */
  /* has specified zero-level verbosity, we will just print the */
  /* local service demand, or the remote service demand. If the */
  /* user has requested verbosity level 1, he will get the basic */
  /* "streamperf" numbers. If the user has specified a verbosity */
  /* of greater than 1, we will display a veritable plethora of */
  /* background information from outside of this block as it it */
  /* not cpu_measurement specific...  */

  if (confidence < 0) {
    /* we did not hit confidence, but were we asked to look for it? */
    if (iteration_max > 1) {
      display_confidence();
    }
  }

  if (local_cpu_usage || remote_cpu_usage) {
    local_cpu_method = format_cpu_method(cpu_method);
    remote_cpu_method = format_cpu_method(sctp_rr_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method);
      }
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,
		cpu_title,
		local_cpu_method,
		remote_cpu_method);
      }

      fprintf(where,
	      cpu_fmt_1_line_1,		/* the format string */
	      lss_size,		/* local sendbuf size */
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* guess */
	      elapsed_time,		/* how long was the test */
	      thruput,
	      local_cpu_utilization,	/* local cpu */
	      remote_cpu_utilization,	/* remote cpu */
	      local_service_demand,	/* local service demand */
	      remote_service_demand);	/* remote service demand */
      fprintf(where,
	      cpu_fmt_1_line_2,
	      rss_size,
	      rsr_size);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      thruput);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }

      fprintf(where,
	      tput_fmt_1_line_1,	/* the format string */
	      lss_size,
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* how large were the responses */
	      elapsed_time, 		/* how long did it take */
	      thruput);
      fprintf(where,
	      tput_fmt_1_line_2,
	      rss_size, 		/* remote recvbuf size */
	      rsr_size);
      
      break;
    }
  }
  
  /* it would be a good thing to include information about some of the */
  /* other parameters that may have been set for this test, but at the */
  /* moment, I do not wish to figure-out all the  formatting, so I will */
  /* just put this comment here to help remind me that it is something */
  /* that should be done at a later time. */
  
  /* how to handle the verbose information in the presence of */
  /* confidence intervals is yet to be determined... raj 11/94 */
  if (verbosity > 1) {
    /* The user wanted to know it all, so we will give it to him. */
    /* This information will include as much as we can find about */
    /* TCP statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */
    
    fprintf(where,
	    ksink_fmt,
	    local_send_align,
	    remote_recv_offset,
	    local_send_offset,
	    remote_recv_offset);

#ifdef WANT_HISTOGRAM
    fprintf(where,"\nHistogram of request/response times\n");
    fflush(where);
    HIST_report(time_hist);
#endif /* WANT_HISTOGRAM */

  }
  
}


 /* this routine implements the receive (netserver) side of a TCP_RR */
 /* test */
void
recv_sctp_rr_1toMany()
{
  
  struct ring_elt *send_ring;
  struct ring_elt *recv_ring;
  

  struct sockaddr_in        myaddr_in; 	/* needed to get the port number */
  struct sockaddr_storage   peeraddr;   /* to communicate with peer */
  struct addrinfo *local_res;
  char   local_name[BUFSIZ];
  char   port_buffer[PORTBUFSIZE];
  int    msg_flags;

  int	s_rcv;
  int 	addrlen;
  char	*temp_message_ptr;
  int	trans_received;
  int	trans_remaining;
  int	bytes_sent;
  int	bytes_recvd;
  int	recv_buf_size;
  int	timed_out = 0;
  float	elapsed_time;
  
  struct	sctp_rr_request_struct	*sctp_rr_request;
  struct	sctp_rr_response_struct	*sctp_rr_response;
  struct	sctp_rr_results_struct	*sctp_rr_results;
  
  sctp_rr_request = 
    (struct sctp_rr_request_struct *)netperf_request.content.test_specific_data;
  sctp_rr_response =
    (struct sctp_rr_response_struct *)netperf_response.content.test_specific_data;
  sctp_rr_results =
    (struct sctp_rr_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_sctp_rr_1toMany: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug) {
    fprintf(where,"recv_sctp_rr_1toMany: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = SCTP_RR_MANY_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_sctp_rr_1toMany: the response type is set...\n");
    fflush(where);
  }
  
  /* allocate the recv and send rings with the requested alignments */
  /* and offsets. raj 7/94 */
  if (debug) {
    fprintf(where,"recv_sctp_rr_1toMany: requested recv alignment of %d offset %d\n",
	    sctp_rr_request->recv_alignment,
	    sctp_rr_request->recv_offset);
    fprintf(where,"recv_sctp_rr_1toMany: requested send alignment of %d offset %d\n",
	    sctp_rr_request->send_alignment,
	    sctp_rr_request->send_offset);
    fflush(where);
  }

  /* at some point, these need to come to us from the remote system */
  if (send_width == 0) send_width = 1;
  if (recv_width == 0) recv_width = 1;

  send_ring = allocate_buffer_ring(send_width,
				   sctp_rr_request->response_size,
				   sctp_rr_request->send_alignment,
				   sctp_rr_request->send_offset);

  recv_ring = allocate_buffer_ring(recv_width,
				   sctp_rr_request->request_size,
				   sctp_rr_request->recv_alignment,
				   sctp_rr_request->recv_offset);

  
  /* create_data_socket expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lss_size_req = sctp_rr_request->send_buf_size;
  lsr_size_req = sctp_rr_request->recv_buf_size;
  loc_nodelay = sctp_rr_request->no_delay;
  loc_rcvavoid = sctp_rr_request->so_rcvavoid;
  loc_sndavoid = sctp_rr_request->so_sndavoid;
  non_block = sctp_rr_request->non_blocking;

  set_hostname_and_port(local_name,
		        port_buffer,
			nf_to_af(sctp_rr_request->ipfamily),
			sctp_rr_request->port);

  local_res = complete_addrinfo(local_name,
				local_name,
				port_buffer,
				nf_to_af(sctp_rr_request->ipfamily),
				SOCK_SEQPACKET,
				IPPROTO_SCTP,
				0);
  
  /* Grab a socket to listen on, and then listen on it. */
  if (debug) {
    fprintf(where,"recv_sctp_rr_1toMany: grabbing a socket...\n");
    fflush(where);
  }

  s_rcv = create_data_socket(local_res);
  
  if (s_rcv < 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    
    exit(1);
  }
  
  /* Now, let's set-up the socket to listen for connections */
  if (listen(s_rcv, 5) == -1) {
    netperf_response.content.serv_errno = errno;
    close(s_rcv);
    send_response();
    
    exit(1);
  }
  
  
  /* now get the port number assigned by the system  */
  addrlen = sizeof(myaddr_in);
  if (getsockname(s_rcv,
		  (struct sockaddr *)&myaddr_in, &addrlen) == -1){
    netperf_response.content.serv_errno = errno;
    close(s_rcv);
    send_response();
    
    exit(1);
  }
  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  sctp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
  netperf_response.content.serv_errno   = 0;
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a 0.0 to */
  /* the initiator. */
  
  sctp_rr_response->cpu_rate = (float)0.0; 	/* assume no cpu */
  sctp_rr_response->measure_cpu = 0;

  if (sctp_rr_request->measure_cpu) {
    sctp_rr_response->measure_cpu = 1;
    sctp_rr_response->cpu_rate = calibrate_local_cpu(sctp_rr_request->cpu_rate);
  }
  
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  sctp_rr_response->send_buf_size = lss_size;
  sctp_rr_response->recv_buf_size = lsr_size;
  sctp_rr_response->no_delay = loc_nodelay;
  sctp_rr_response->so_rcvavoid = loc_rcvavoid;
  sctp_rr_response->so_sndavoid = loc_sndavoid;
  sctp_rr_response->test_length = sctp_rr_request->test_length;
  send_response();
  
  /* Don't need events */
  sctp_enable_events(s_rcv, 0);

  /* now that we are connected, mark the socket as non-blocking */
  if (non_block) {
    if (!set_nonblock(s_rcv)) {
      perror("netperf: set_nonblock");
	exit(1);
    }
  }

  /* FIXME:  The way 1-to-Many test operates right now, we are including
   * association setup time into our measurements.  The reason for this
   * is that the client creates multiple endpoints and connects each
   * endpoint to us using the connect call.  On this end we simply call
   * recvmsg() to get data becuase there is no equivalen of accept() for
   * 1-to-Many API.  
   * I think this is OK, but if it were to be fixed, the server side
   * would need to know how many associations are being setup and
   * have a recvmsg() loop with SCTP_ASSOC_CHANGE events waiting for
   * all the associations to be be established.
   * I am punting on this for now.
   */


  addrlen = sizeof(peeraddr);

  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(sctp_rr_request->measure_cpu);
  
  /* The loop will exit when we hit the end of the test time, or when */
  /* we have exchanged the requested number of transactions. */
  
  if (sctp_rr_request->test_length > 0) {
    times_up = 0;
    trans_remaining = 0;
    start_timer(sctp_rr_request->test_length + PAD_TIME);
  }
  else {
    times_up = 1;
    trans_remaining = sctp_rr_request->test_length * -1;
  }

  trans_received = 0;
  
  while ((!times_up) || (trans_remaining > 0)) {

    recv_buf_size = sctp_rr_request->request_size;
 
    /* Receive the data.  We don't particularly care which association
     * the data came in on.  We'll simply be doing a receive untill
     * we get and MSG_EOR flag (meaning that a single transmission was
     * received) and a send to the same address, so the RR would be for
     * the same associations.
     * We can get away with this because the client will establish all
     * the associations before transmitting any data.  Any partial data
     * will not have EOR thus will we will not send a response untill
     * we get everything.
     */
    
    do {
      msg_flags = 0;
      if((bytes_recvd = sctp_recvmsg(s_rcv,
				     recv_ring->buffer_ptr,
				     recv_buf_size,
				     (struct sockaddr *)&peeraddr, &addrlen,
				     0, &msg_flags)) == SOCKET_ERROR) {
	if (SOCKET_EINTR(bytes_recvd)) {
	  /* the timer popped */
	  timed_out = 1;
	  break;
	} else if (non_block & errno == EAGAIN) {
	    /* do recvmsg again */
	    continue;
	}
	netperf_response.content.serv_errno = errno;
	send_response();
	exit(1);
      }
    } while(!(msg_flags & MSG_EOR));

    recv_ring = recv_ring->next;

    if (timed_out) {
      /* we hit the end of the test based on time - lets */
      /* bail out of here now... */
      if (debug) {
	fprintf(where,"yo5\n");
	fflush(where);
      }						
      break;
    }
    
    /* Now, send the response to the remote */
    while ((bytes_sent=sctp_sendmsg(s_rcv,
			      send_ring->buffer_ptr,
			      sctp_rr_request->response_size,
			      (struct sockaddr *)&peeraddr, addrlen,
			      0, 0, 0, 0, 0)) == SOCKET_ERROR) {
      if (SOCKET_EINTR(bytes_sent)) {
	/* the test timer has popped */
	timed_out = 1;
	break;
      } else if (non_block && errno == EAGAIN) {
	 continue;
      }

      netperf_response.content.serv_errno = 992;
      send_response();
      exit(1);
    }

    if (timed_out) {
      if (debug) {
	fprintf(where,"yo6\n");
	fflush(where);
      }						
      /* we hit the end of the test based on time - lets */
      /* bail out of here now... */
      break;
    }
    
    send_ring = send_ring->next;

    trans_received++;
    if (trans_remaining) {
      trans_remaining--;
    }
  }
  
  
  /* The loop now exits due to timeout or transaction count being */
  /* reached */
  
  cpu_stop(sctp_rr_request->measure_cpu,&elapsed_time);
  
  stop_timer();

  if (timed_out) {
    /* we ended the test by time, which was at least 2 seconds */
    /* longer than we wanted to run. so, we want to subtract */
    /* PAD_TIME from the elapsed_time. */
    elapsed_time -= PAD_TIME;
  }

  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_sctp_rr: got %d transactions\n",
	    trans_received);
    fflush(where);
  }
  
  sctp_rr_results->bytes_received = (trans_received * 
				    (sctp_rr_request->request_size + 
				     sctp_rr_request->response_size));
  sctp_rr_results->trans_received = trans_received;
  sctp_rr_results->elapsed_time   = elapsed_time;
  sctp_rr_results->cpu_method     = cpu_method;
  sctp_rr_results->num_cpus       = lib_num_loc_cpus;
  if (sctp_rr_request->measure_cpu) {
    sctp_rr_results->cpu_util	= calc_cpu_util(elapsed_time);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_sctp_rr: test complete, sending results.\n");
    fflush(where);
  }
  
  /* we are now done with the sockets */
  close(s_rcv);

  send_response();
  
}


void
print_sctp_usage()
{

  printf("%s",sctp_usage);
  exit(1);

}
void
scan_sctp_args(argc, argv)
     int	argc;
     char	*argv[];

{

#define SOCKETS_ARGS "BDhH:I:L:m:M:P:r:s:S:VN:T:46"

  extern char	*optarg;	  /* pointer to option string	*/
  
  int		c;
  
  char	
    arg1[BUFSIZ],  /* argument holders		*/
    arg2[BUFSIZ];

  if (no_control) {
    fprintf(where,
	    "The SCTP tests do not know how to deal with no control tests\n");
    exit(-1);
  }

  strncpy(local_data_port,"0",sizeof(local_data_port));
  strncpy(remote_data_port,"0",sizeof(remote_data_port));
  
  /* Go through all the command line arguments and break them */
  /* out. For those options that take two parms, specifying only */
  /* the first will set both to that value. Specifying only the */
  /* second will leave the first untouched. To change only the */
  /* first, use the form "first," (see the routine break_args.. */
  
  while ((c= getopt(argc, argv, SOCKETS_ARGS)) != EOF) {
    switch (c) {
    case '?':	
    case '4':
      remote_data_family = AF_INET;
      local_data_family = AF_INET;
      break;
    case '6':
#if defined(AF_INET6)
      remote_data_family = AF_INET6;
      local_data_family = AF_INET6;
#else
      fprintf(stderr,
	      "This netperf was not compiled on an IPv6 capable host!\n");
      fflush(stderr);
      exit(-1);
#endif
      break;
    case 'h':
      print_sctp_usage();
      exit(1);
    case 'b':
#ifdef WANT_FIRST_BURST
      first_burst_size = atoi(optarg);
#else /* WANT_FIRST_BURST */
      printf("Initial request burst functionality not compiled-in!\n");
#endif /* WANT_FIRST_BURST */
      break;
    case 'D':
      /* set the nodelay flag */
      loc_nodelay = 1;
      rem_nodelay = 1;
      break;
    case 'H':
      break_args_explicit(optarg,arg1,arg2);
      if (arg1[0]) {
	/* make sure we leave room for the NULL termination boys and
	   girls. raj 2005-02-82 */ 
	remote_data_address = malloc(strlen(arg1)+1);
	strncpy(remote_data_address,arg1,strlen(arg1));
      }
      if (arg2[0])
	remote_data_family = parse_address_family(arg2);
      break;
    case 'L':
      break_args_explicit(optarg,arg1,arg2);
      if (arg1[0]) {
	/* make sure we leave room for the NULL termination boys and
	   girls. raj 2005-02-82 */ 
	local_data_address = malloc(strlen(arg1)+1);
	strncpy(local_data_address,arg1,strlen(arg1));
      }
      if (arg2[0])
	local_data_family = parse_address_family(arg2);
      break;
    case 'P':
      /* set the local and remote data port numbers for the tests to
	 allow them to run through those blankety blank end-to-end
	 breaking firewalls. raj 2004-06-15 */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	strncpy(local_data_port,arg1,sizeof(local_data_port));
      if (arg2[0])	
	strncpy(remote_data_port,arg2,sizeof(remote_data_port));
      break;
    case 's':
      /* set local socket sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	lss_size_req = convert(arg1);
      if (arg2[0])
	lsr_size_req = convert(arg2);
      break;
    case 'S':
      /* set remote socket sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	rss_size_req = convert(arg1);
      if (arg2[0])
	rsr_size_req = convert(arg2);
      break;
    case 'r':
      /* set the request/response sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	req_size = convert(arg1);
      if (arg2[0])	
	rsp_size = convert(arg2);
      break;
    case 'm':
      /* set size of the buffer for each sent message */
      send_size = convert(optarg);
      break;
    case 'M':
      /* set the size of the buffer for each received message */
      recv_size = convert(optarg);
      break;
    case 't':
      /* set the test name */
      strcpy(test_name,optarg);
      break;
    case 'W':
	/* set the "width" of the user space data */
	/* buffer. This will be the number of */
	/* send_size buffers malloc'd in the */
	/* *_STREAM test. It may be enhanced to set */
	/* both send and receive "widths" but for now */
	/* it is just the sending *_STREAM. */
	send_width = convert(optarg);
	break;
    case 'V':
      /* we want to do copy avoidance and will set */
      /* it for everything, everywhere, if we really */
      /* can. of course, we don't know anything */
      /* about the remote... */
#ifdef SO_SND_COPYAVOID
      loc_sndavoid = 1;
#else
      loc_sndavoid = 0;
      printf("Local send copy avoidance not available.\n");
#endif
#ifdef SO_RCV_COPYAVOID
      loc_rcvavoid = 1;
#else
      loc_rcvavoid = 0;
      printf("Local recv copy avoidance not available.\n");
#endif
      rem_sndavoid = 1;
      rem_rcvavoid = 1;
      break;
    case 'N':
      /* this opton allows the user to set the number of 
       * messages to send.  This in effect modifies the test
       * time.  If we know the message size, then the we can
       * express the test time as message_size * number_messages
       */
      msg_count = convert (optarg);
      if (msg_count > 0)
	  test_time = 0;
      break;
    case 'B':
      non_block = 1;
      break;
    case 'T':
      num_associations = atoi(optarg);
      if (num_associations <= 1) {
	  printf("Number of SCTP associations must be >= 1\n");
	  exit(1);
      }
      break;
    };
  }
}

#endif  /* WANT_SCTP */