/*
#  Copyright 2021 Hewlett Packard Enterprise Development LP
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
# USE OR OTHER DEALINGS IN THE SOFTWARE.
*/


/****************************************************************/
/*								*/
/*	nettest_dlpi.c						*/
/*								*/
/*	the actual test routines...				*/
/*								*/
/*	send_dlpi_co_stream()	perform a CO DLPI stream test	*/
/*	recv_dlpi_co_stream()					*/
/*	send_dlpi_co_rr()	perform a CO DLPI req/res	*/
/*	recv_dlpi_co_rr()					*/
/*	send_dlpi_cl_stream()	perform a CL DLPI stream test	*/
/*	recv_dlpi_cl_stream()					*/
/*	send_dlpi_cl_rr()	perform a CL DLPI req/res	*/
/*	recv_dlpi_cl_rr()					*/
/*								*/
/****************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef WANT_DLPI
char	nettest_dlpi_id[]="\
@(#)nettest_dlpi.c (c) Copyright 1993-2012 Hewlett-Packard Co. Version 2.6.0";

#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <malloc.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/poll.h>
#ifdef __osf__
#include <sys/dlpihdr.h>
#else /* __osf__ */
#include <sys/dlpi.h>
#ifdef __hpux__
#include <sys/dlpi_ext.h>
#endif /* __hpux__ */
#endif /* __osf__ */

#include "netlib.h"
#include "netsh.h"
#include "nettest_dlpi.h"

/* some stuff for DLPI control messages */
#define DLPI_DATA_SIZE 2048

unsigned long control_data[DLPI_DATA_SIZE];
struct strbuf control_message = {DLPI_DATA_SIZE, 0, (char *)control_data};

/* these are some variables global to all the DLPI tests. declare */
/* them static to make them global only to this file */

static int
  rsw_size,		/* remote send window size	*/
  rrw_size,		/* remote recv window size	*/
  lsw_size,		/* local  send window size 	*/
  lrw_size,		/* local  recv window size 	*/
  req_size = 100,	/* request size                   	*/
  rsp_size = 200,	/* response size			*/
  send_size,		/* how big are individual sends		*/
  recv_size;		/* how big are individual receives	*/

int
  loc_ppa = 4,          /* the ppa for the local interface, */
  /* as shown as the NM Id in lanscan */
  rem_ppa = 4,          /* the ppa for the remote interface */
  dlpi_sap = 84;        /* which 802.2 SAP should we use?   */

char loc_dlpi_device[32] = "/dev/dlpi";
char rem_dlpi_device[32] = "/dev/dlpi";

char dlpi_usage[] = "\n\
Usage: netperf [global options] -- [test options] \n\
\n\
CO/CL DLPI Test Options:\n\
    -D dev[,dev]      Set the local/remote DLPI device file name\n\
    -h                Display this text\n\
    -M bytes          Set the recv size (DLCO_STREAM, DLCL_STREAM)\n\
    -m bytes          Set the send size (DLCO_STREAM, DLCL_STREAM)\n\
    -p loc[,rem]      Set the local/remote PPA for the test\n\
    -R bytes          Set response size (DLCO_RR, DLCL_RR)\n\
    -r bytes          Set request size (DLCO_RR, DLCL_RR)\n\
    -s sap            Set the 802.2 sap for the test\n\
    -W send[,recv]    Set remote send/recv window sizes\n\
    -w send[,recv]    Set local send/recv window sizes\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";



/* routines that used to be in src/netlib.c but this code is the only
   code that uses them. raj 20110111 */


int
put_control(fd, len, pri, ack)
     int fd, len, pri, ack;
{
  int error;
  int flags = 0;
  dl_error_ack_t *err_ack = (dl_error_ack_t *)control_data;

  control_message.len = len;

  if ((error = putmsg(fd, &control_message, 0, pri)) < 0 ) {
    fprintf(where,"put_control: putmsg error %d\n",error);
    fflush(where);
    return(-1);
  }
  if ((error = getmsg(fd, &control_message, 0, &flags)) < 0) {
    fprintf(where,"put_control: getsmg error %d\n",error);
    fflush(where);
    return(-1);
  }
  if (err_ack->dl_primitive != ack) {
    fprintf(where,"put_control: acknowledgement error wanted %u got %u \n",
            ack,err_ack->dl_primitive);
    if (err_ack->dl_primitive == DL_ERROR_ACK) {
      fprintf(where,"             dl_error_primitive: %u\n",
              err_ack->dl_error_primitive);
      fprintf(where,"             dl_errno:           %u\n",
              err_ack->dl_errno);
      fprintf(where,"             dl_unix_errno       %u\n",
              err_ack->dl_unix_errno);
    }
    fflush(where);
    return(-1);
  }

  return(0);
}

int
dl_open(char devfile[], int ppa)
{
  int fd;
  dl_attach_req_t *attach_req = (dl_attach_req_t *)control_data;

  if ((fd = open(devfile, O_RDWR)) == -1) {
    fprintf(where,"netperf: dl_open: open of %s failed, errno = %d\n",
            devfile,
            errno);
    return(-1);
  }

  attach_req->dl_primitive = DL_ATTACH_REQ;
  attach_req->dl_ppa = ppa;

  if (put_control(fd, sizeof(dl_attach_req_t), 0, DL_OK_ACK) < 0) {
    fprintf(where,
            "netperf: dl_open: could not send control message, errno = %d\n",
            errno);
    return(-1);
  }
  return(fd);
}

int
dl_bind(int fd, int sap, int mode, char *dlsap_ptr, int *dlsap_len)
{
  dl_bind_req_t *bind_req = (dl_bind_req_t *)control_data;
  dl_bind_ack_t *bind_ack = (dl_bind_ack_t *)control_data;

  bind_req->dl_primitive = DL_BIND_REQ;
  bind_req->dl_sap = sap;
  bind_req->dl_max_conind = 1;
  bind_req->dl_service_mode = mode;
  bind_req->dl_conn_mgmt = 0;
  bind_req->dl_xidtest_flg = 0;

  if (put_control(fd, sizeof(dl_bind_req_t), 0, DL_BIND_ACK) < 0) {
    fprintf(where,
            "netperf: dl_bind: could not send control message, errno = %d\n",
            errno);
    return(-1);
  }

  /* at this point, the control_data portion of the control message */
  /* structure should contain a DL_BIND_ACK, which will have a full */
  /* DLSAP in it. we want to extract this and pass it up so that    */
  /* it can be passed around. */
  if (*dlsap_len >= bind_ack->dl_addr_length) {
    bcopy((char *)bind_ack+bind_ack->dl_addr_offset,
          dlsap_ptr,
          bind_ack->dl_addr_length);
    *dlsap_len = bind_ack->dl_addr_length;
    return(0);
  }
  else {
    return (-1);
  }
}

int
dl_connect(int fd, unsigned char *remote_addr, int remote_addr_len)
{
  dl_connect_req_t *connection_req = (dl_connect_req_t *)control_data;
  dl_connect_con_t *connection_con = (dl_connect_con_t *)control_data;
  struct pollfd pinfo;

  int flags = 0;

  /* this is here on the off chance that we really want some data */
  u_long data_area[512];
  struct strbuf data_message;

  int error;

  data_message.maxlen = 2048;
  data_message.len = 0;
  data_message.buf = (char *)data_area;

  connection_req->dl_primitive = DL_CONNECT_REQ;
  connection_req->dl_dest_addr_length = remote_addr_len;
  connection_req->dl_dest_addr_offset = sizeof(dl_connect_req_t);
  connection_req->dl_qos_length = 0;
  connection_req->dl_qos_offset = 0;
  bcopy (remote_addr,
         (unsigned char *)control_data + sizeof(dl_connect_req_t),
         remote_addr_len);

  /* well, I would call the put_control routine here, but the sequence */
  /* of connection stuff with DLPI is a bit screwey with all this */
  /* message passing - Toto, I don't think were in Berkeley anymore. */

  control_message.len = sizeof(dl_connect_req_t) + remote_addr_len;
  if ((error = putmsg(fd,&control_message,0,0)) !=0) {
    fprintf(where,"dl_connect: putmsg failure, errno = %d, error 0x%x \n",
            errno,error);
    fflush(where);
    return(-1);
  };

  pinfo.fd = fd;
  pinfo.events = POLLIN | POLLPRI;
  pinfo.revents = 0;

  if ((error = getmsg(fd,&control_message,&data_message,&flags)) != 0) {
    fprintf(where,"dl_connect: getmsg failure, errno = %d, error 0x%x \n",
            errno,error);
    fflush(where);
    return(-1);
  }
  while (control_data[0] == DL_TEST_CON) {
    /* i suppose we spin until we get an error, or a connection */
    /* indication */
    if((error = getmsg(fd,&control_message,&data_message,&flags)) !=0) {
       fprintf(where,"dl_connect: getmsg failure, errno = %d, error = 0x%x\n",
               errno,error);
       fflush(where);
       return(-1);
    }
  }

  /* we are out - it either worked or it didn't - which was it? */
  if (control_data[0] == DL_CONNECT_CON) {
    return(0);
  }
  else {
    return(-1);
  }
}

int
dl_accept(fd, remote_addr, remote_addr_len)
     int fd;
     unsigned char *remote_addr;
     int remote_addr_len;
{
  dl_connect_ind_t *connect_ind = (dl_connect_ind_t *)control_data;
  dl_connect_res_t *connect_res = (dl_connect_res_t *)control_data;
  int tmp_cor;
  int flags = 0;

  /* hang around and wait for a connection request */
  getmsg(fd,&control_message,0,&flags);
  while (control_data[0] != DL_CONNECT_IND) {
    getmsg(fd,&control_message,0,&flags);
  }

  /* now respond to the request. at some point, we may want to be sure */
  /* that the connection came from the correct station address, but */
  /* will assume that we do not have to worry about it just now. */

  tmp_cor = connect_ind->dl_correlation;

  connect_res->dl_primitive = DL_CONNECT_RES;
  connect_res->dl_correlation = tmp_cor;
  connect_res->dl_resp_token = 0;
  connect_res->dl_qos_length = 0;
  connect_res->dl_qos_offset = 0;
  connect_res->dl_growth = 0;

  return(put_control(fd, sizeof(dl_connect_res_t), 0, DL_OK_ACK));

}

int
dl_set_window(fd, window)
     int fd, window;
{
  return(0);
}

void
dl_stats(fd)
     int fd;
{
}

int
dl_send_disc(fd)
     int fd;
{
}

int
dl_recv_disc(fd)
     int fd;
{
}


/* This routine implements the CO 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_dlpi_co_stream()
{

  char *tput_title = "\
Recv   Send    Send                          \n\
Window Window  Message  Elapsed              \n\
Size   Size    Size     Time     Throughput  \n\
frames frames  bytes    secs.    %s/sec  \n\n";

  char *tput_fmt_0 =
    "%7.2f\n";

  char *tput_fmt_1 =
    "%5d  %5d  %6d    %-6.2f   %7.2f   \n";

  char *cpu_title = "\
Recv   Send    Send                          Utilization    Service Demand\n\
Window Window  Message  Elapsed              Send   Recv    Send    Recv\n\
Size   Size    Size     Time     Throughput  local  remote  local   remote\n\
frames frames  bytes    secs.    %-8.8s/s  %%      %%       us/KB   us/KB\n\n";

  char *cpu_fmt_0 =
    "%6.3f\n";

  char *cpu_fmt_1 =
    "%5d  %5d  %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";


  float			elapsed_time;

#ifdef WANT_INTERVALS
  int interval_count;
#endif /* WANT_INTERVALS */

  /* 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. */

  struct ring_elt *send_ring;
  char	*message;
  char	*message_ptr;
  struct strbuf send_message;
  char  dlsap[BUFSIZ];
  int   dlsap_len;
  int	*message_int_ptr;
  int	message_offset;
  int	malloc_size;

  int	len;
  int	nummessages;
  int	send_descriptor;
  int	bytes_remaining;
  /* with links like fddi, one can send > 32 bits worth of bytes */
  /* during a test... ;-) */
  double	bytes_sent;

#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	dlpi_co_stream_request_struct	*dlpi_co_stream_request;
  struct	dlpi_co_stream_response_struct	*dlpi_co_stream_response;
  struct	dlpi_co_stream_results_struct	*dlpi_co_stream_result;

  dlpi_co_stream_request	=
    (struct dlpi_co_stream_request_struct *)netperf_request.content.test_specific_data;
  dlpi_co_stream_response	=
    (struct dlpi_co_stream_response_struct *)netperf_response.content.test_specific_data;
  dlpi_co_stream_result	        =
    (struct dlpi_co_stream_results_struct *)netperf_response.content.test_specific_data;

  if ( print_headers ) {
    fprintf(where,"DLPI CO STREAM TEST\n");
    if (local_cpu_usage || remote_cpu_usage)
      fprintf(where,cpu_title,format_units());
    else
      fprintf(where,tput_title,format_units());
  }

  /* initialize a few counters */

  nummessages	=	0;
  bytes_sent	=	0.0;
  times_up 	= 	0;

  /*set up the data descriptor                        */
  send_descriptor = dl_open(loc_dlpi_device,loc_ppa);
  if (send_descriptor < 0){
    perror("netperf: send_dlpi_co_stream: dlpi stream data descriptor");
    exit(1);
  }

  /* bind the puppy and get the assigned dlsap */
  dlsap_len = BUFSIZ;
  if (dl_bind(send_descriptor,
              dlpi_sap, DL_CODLS, dlsap, &dlsap_len) != 0) {
    fprintf(where,"send_dlpi_co_rr: bind failure\n");
    fflush(where);
    exit(1);
  }

  if (debug) {
    fprintf(where,"send_dlpi_co_stream: send_descriptor obtained...\n");
  }

#ifdef DL_HP_SET_LOCAL_WIN_REQ
  if (lsw_size > 0) {
    if (debug > 1) {
      fprintf(where,"netperf: send_dlpi_co_stream: window send size altered from system default...\n");
      fprintf(where,"                          send: %d\n",lsw_size);
    }
  }
  if (lrw_size > 0) {
    if (debug > 1) {
      fprintf(where,
	      "netperf: send_dlpi_co_stream: window recv size altered from system default...\n");
      fprintf(where,"                          recv: %d\n",lrw_size);
    }
  }


  /* Now, we will find-out what the size actually became, and report */
  /* that back to the user. If the call fails, we will just report a -1 */
  /* back to the initiator for the recv buffer size. */


  if (debug) {
    fprintf(where,
	    "netperf: send_dlpi_co_stream: window sizes determined...\n");
    fprintf(where,"         send: %d recv: %d\n",lsw_size,lrw_size);
    fflush(where);
  }

#else /* DL_HP_SET_LOCAL_WIN_REQ */

  lsw_size = -1;
  lrw_size = -1;

#endif /* DL_HP_SET_LOCAL_WIN_REQ */

  /* we should pick a default send_size, it should not be larger than */
  /* the min of the two interface MTU's, and should perhaps default to */
  /* the Interface MTU, but for now, we will default it to 1024... if */
  /* someone wants to change this, the should change the corresponding */
  /* lines in the recv_dlpi_co_stream routine */

  if (send_size == 0) {
    send_size = 1024;
  }

  /* set-up the data buffer with the requested alignment and offset. */
  /* After we have calculated the proper starting address, we want to */
  /* put that back into the message variable so we go back to the */
  /* proper place. note that this means that only the first send is */
  /* guaranteed to be at the alignment specified by the -a parameter. I */
  /* think that this is a little more "real-world" than what was found */
  /* in previous versions. 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 = (lsw_size/send_size) + 1;
    if (send_width == 1) send_width++;
  }

  send_ring = allocate_buffer_ring(send_width,
				   send_size,
				   local_send_align,
				   local_send_offset);

  send_message.maxlen = send_size;
  send_message.len = send_size;
  send_message.buf = send_ring->buffer_ptr;

  /* 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. */

  netperf_request.content.request_type	 =	DO_DLPI_CO_STREAM;
  dlpi_co_stream_request->send_win_size =	rsw_size;
  dlpi_co_stream_request->recv_win_size =	rrw_size;
  dlpi_co_stream_request->receive_size	 =	recv_size;
  dlpi_co_stream_request->recv_alignment=	remote_recv_align;
  dlpi_co_stream_request->recv_offset	 =	remote_recv_offset;
  dlpi_co_stream_request->measure_cpu	 =	remote_cpu_usage;
  dlpi_co_stream_request->cpu_rate	 =	remote_cpu_rate;
  dlpi_co_stream_request->ppa           =      rem_ppa;
  dlpi_co_stream_request->sap           =      dlpi_sap;
  dlpi_co_stream_request->dev_name_len  =      strlen(rem_dlpi_device);
  strcpy(dlpi_co_stream_request->dlpi_device,
	 rem_dlpi_device);

#ifdef __alpha

  /* ok - even on a DEC box, strings are strings. I didn't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;

    initword = (int *) dlpi_co_stream_request->dlpi_device;
    lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4);

    for (charword = initword;
	 charword < lastword;
	 charword++) {

      *charword = ntohl(*charword);
    }
  }
#endif /* __alpha */

  if (test_time) {
    dlpi_co_stream_request->test_length	=	test_time;
  }
  else {
    dlpi_co_stream_request->test_length	=	test_bytes;
  }
#ifdef DIRTY
  dlpi_co_stream_request->dirty_count       =       rem_dirty_count;
  dlpi_co_stream_request->clean_count       =       rem_clean_count;
#endif /* DIRTY */


  if (debug > 1) {
    fprintf(where,
	    "netperf: send_dlpi_co_stream: requesting DLPI CO stream test\n");
  }

  send_request();

  /* The response from the remote will contain all of the relevant 	*/
  /* 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 TCP tests.					*/

  recv_response();

  if (!netperf_response.content.serv_errno) {
    if (debug)
      fprintf(where,"remote listen done.\n");
    rrw_size	=	dlpi_co_stream_response->recv_win_size;
    rsw_size	=	dlpi_co_stream_response->send_win_size;
    remote_cpu_usage=	dlpi_co_stream_response->measure_cpu;
    remote_cpu_rate = 	dlpi_co_stream_response->cpu_rate;
  }
  else {
    Set_errno(netperf_response.content.serv_errno);
    perror("netperf: remote error");
    exit(1);
  }

  /* Connect up to the remote port on the data descriptor */
  if(dl_connect(send_descriptor,
		dlpi_co_stream_response->station_addr,
		dlpi_co_stream_response->station_addr_len) != 0) {
    fprintf(where,"recv_dlpi_co_stream: connect failure\n");
    fflush(where);
    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;
    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);

  /* 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. */

#ifdef DIRTY
  /* initialize the random number generator for putting dirty stuff */
  /* into the send buffer. raj */
  srand((int) getpid());
#endif /* DIRTY */

  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. */
    message_int_ptr = (int *)message_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 */

    if((putmsg(send_descriptor,
	       0,
	       &send_message,
	       0)) != 0) {
      if (errno == EINTR)
	break;
      perror("netperf: data send error");
      exit(1);
    }
    send_ring = send_ring->next;
    send_message.buf = send_ring->buffer_ptr;
#ifdef WANT_INTERVALS
    for (interval_count = 0;
	 interval_count < interval_wate;
	 interval_count++);
#endif /* WANT_INTERVALS */

    if (debug > 4) {
      fprintf(where,"netperf: send_clpi_co_stream: putmsg called ");
      fprintf(where,"len is %d\n",send_message.len);
      fflush(where);
    }

    nummessages++;
    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. this needs a little work - there is no three-way */
  /* handshake with type two as there is with TCP, so there really */
  /* should be a message exchange here. however, we will finesse it by */
  /* saying that the tests shoudl run for a while. */

  if (debug) {
    fprintf(where,"sending test end signal \n");
    fflush(where);
  }

  send_message.len = (send_size - 1);
  if (send_message.len == 0) send_message.len = 2;

  if((putmsg(send_descriptor,
	     0,
	     &send_message,
	     0)) != 0) {
    perror("netperf: data send error");
    exit(1);
  }

  /* 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 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);
    perror("netperf: remote error");

    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 TCP 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	= ((double) send_size * (double) nummessages) + (double) len;
  thruput		= 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) {
      if (local_cpu_rate == 0.0) {
	fprintf(where,
		"WARNING WARNING WARNING  WARNING WARNING WARNING  WARNING!\n");
	fprintf(where,
		"Local CPU usage numbers based on process information only!\n");
	fflush(where);
      }
      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	= -1.0;
      local_service_demand	= -1.0;
    }

    if (remote_cpu_usage) {
      if (remote_cpu_rate == 0.0) {
	fprintf(where,
		"DANGER   DANGER  DANGER   DANGER   DANGER  DANGER   DANGER!\n");
	fprintf(where,
		"Remote CPU usage numbers based on process information only!\n");
	fflush(where);
      }
      remote_cpu_utilization	= dlpi_co_stream_result->cpu_util;
      remote_service_demand	= calc_service_demand(bytes_sent,
						      0.0,
						      remote_cpu_utilization,
						      dlpi_co_stream_result->num_cpus);
    }
    else {
      remote_cpu_utilization = -1.0;
      remote_service_demand  = -1.0;
    }

    /* 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...  */

    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand);
      }
      break;
    case 1:
    case 2:
      fprintf(where,
	      cpu_fmt_1,		/* the format string */
	      rrw_size,		/* remote recvbuf size */
	      lsw_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:
      fprintf(where,
	      tput_fmt_1,		/* the format string */
	      rrw_size, 		/* remote recvbuf size */
	      lsw_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 */
    /* TCP statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */

    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)dlpi_co_stream_result->recv_calls,
	    dlpi_co_stream_result->recv_calls);
  }

}


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

int
  recv_dlpi_co_stream()
{

  int	data_descriptor;
  int	flags = 0;
  int	measure_cpu;
  int	bytes_received;
  int	receive_calls;
  float	elapsed_time;

  struct ring_elt *recv_ring;
  char	*message_ptr;
  char	*message;
  int   *message_int_ptr;
  struct strbuf recv_message;
  int   dirty_count;
  int   clean_count;
  int   i;

  struct	dlpi_co_stream_request_struct	*dlpi_co_stream_request;
  struct	dlpi_co_stream_response_struct	*dlpi_co_stream_response;
  struct	dlpi_co_stream_results_struct	*dlpi_co_stream_results;

  dlpi_co_stream_request	= (struct dlpi_co_stream_request_struct *)netperf_request.content.test_specific_data;
  dlpi_co_stream_response	= (struct dlpi_co_stream_response_struct *)netperf_response.content.test_specific_data;
  dlpi_co_stream_results	= (struct dlpi_co_stream_results_struct *)netperf_response.content.test_specific_data;

  if (debug) {
    fprintf(where,"netserver: recv_dlpi_co_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. */

  netperf_response.content.response_type = DLPI_CO_STREAM_RESPONSE;

  /* We now alter the message_ptr variable to be at the desired */
  /* alignment with the desired offset. */

  if (debug > 1) {
    fprintf(where,"recv_dlpi_co_stream: requested alignment of %d\n",
	    dlpi_co_stream_request->recv_alignment);
    fflush(where);
  }


  /* Grab a descriptor to listen on, and then listen on it. */

  if (debug > 1) {
    fprintf(where,"recv_dlpi_co_stream: grabbing a descriptor...\n");
    fflush(where);
  }



#ifdef __alpha

  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;

    initword = (int *) dlpi_co_stream_request->dlpi_device;
    lastword = initword + ((dlpi_co_stream_request->dev_name_len + 3) / 4);

    for (charword = initword;
	 charword < lastword;
	 charword++) {

      *charword = htonl(*charword);
    }
  }
#endif /* __alpha */

  data_descriptor = dl_open(dlpi_co_stream_request->dlpi_device,
			    dlpi_co_stream_request->ppa);
  if (data_descriptor < 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }

  /* Let's get an address assigned to this descriptor so we can tell the */
  /* initiator how to reach the data descriptor. There may be a desire to */
  /* nail this descriptor to a specific address in a multi-homed, */
  /* multi-connection situation, but for now, we'll ignore the issue */
  /* and concentrate on single connection testing. */

  /* bind the sap and retrieve the dlsap assigned by the system  */
  dlpi_co_stream_response->station_addr_len = 14; /* arbitrary */
  if (dl_bind(data_descriptor,
	      dlpi_co_stream_request->sap,
	      DL_CODLS,
	      (char *)dlpi_co_stream_response->station_addr,
	      &dlpi_co_stream_response->station_addr_len) != 0) {
    fprintf(where,"recv_dlpi_co_stream: bind failure\n");
    fflush(where);
    exit(1);
  }

  /* The initiator may have wished-us to modify the socket buffer */
  /* sizes. We should give it a shot. If he didn't ask us to change the */
  /* sizes, we should let him know what sizes were in use at this end. */
  /* If none of this code is compiled-in, then we will tell the */
  /* initiator that we were unable to play with the socket buffer by */
  /* setting the size in the response to -1. */

#ifdef DL_HP_SET_LOCAL_WIN_REQ

  if (dlpi_co_stream_request->recv_win_size) {
  }
  /* Now, we will find-out what the size actually became, and report */
  /* that back to the user. If the call fails, we will just report a -1 */
  /* back to the initiator for the recv buffer size. */

#else /* the system won't let us play with the buffers */

  dlpi_co_stream_response->recv_win_size	= -1;

#endif /* DL_HP_SET_LOCAL_WIN_REQ */

  /* what sort of sizes did we end-up with? */
  /* this bit of code whould default to the Interface MTU */
  if (dlpi_co_stream_request->receive_size == 0) {
    recv_size = 1024;
  }
  else {
    recv_size = dlpi_co_stream_request->receive_size;
  }

  /* tell the other fellow what our receive size became */
  dlpi_co_stream_response->receive_size = recv_size;

  /* just a little prep work for when we may have to behave like the */
  /* sending side... */
  message = (char *)malloc(recv_size * 2);
  if (message == NULL) {
    printf("malloc(%d) failed!\n", recv_size * 2);
    exit(1);
  }

  message_ptr = ALIGN_BUFFER(message, dlpi_co_stream_request->recv_alignment, dlpi_co_stream_request->recv_offset);
  recv_message.maxlen = recv_size;
  recv_message.len = 0;
  recv_message.buf = message_ptr;

  if (debug > 1) {
    fprintf(where,
	    "recv_dlpi_co_stream: receive alignment and offset set...\n");
    fflush(where);
  }

  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. */

  dlpi_co_stream_response->cpu_rate = 0.0; 	/* assume no cpu */
  if (dlpi_co_stream_request->measure_cpu) {
    dlpi_co_stream_response->measure_cpu = 1;
    dlpi_co_stream_response->cpu_rate =
      calibrate_local_cpu(dlpi_co_stream_request->cpu_rate);
  }

  send_response();

  /* accept a connection on this file descriptor. at some point, */
  /* dl_accept will "do the right thing" with the last two parms, but */
  /* for now it ignores them, so we will pass zeros. */

  if(dl_accept(data_descriptor, 0, 0) != 0) {
    fprintf(where,
	    "recv_dlpi_co_stream: error in accept, errno %d\n",
	    errno);
    fflush(where);
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }

  if (debug) {
    fprintf(where,"netserver:recv_dlpi_co_stream: connection accepted\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(dlpi_co_stream_request->measure_cpu);

#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 = dlpi_co_stream_request->dirty_count;
  clean_count = dlpi_co_stream_request->clean_count;
  message_int_ptr = (int *)message_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 */

  recv_message.len = recv_size;
  while (recv_message.len == recv_size) {
    if (getmsg(data_descriptor,
	       0,
	       &recv_message,
	       &flags) != 0) {
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }
    bytes_received += recv_message.len;
    receive_calls++;

    if (debug) {
      fprintf(where,
	      "netserver:recv_dlpi_co_stream: getmsg accepted %d bytes\n",
	      recv_message.len);
      fflush(where);
    }


#ifdef DIRTY
    message_int_ptr = (int *)message_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 */

  }

  /* The loop now exits due to zero bytes received. */
  /* should perform a disconnect to signal the sender that */
  /* we have received all the data sent. */

  if (close(data_descriptor) == -1) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }

  cpu_stop(dlpi_co_stream_request->measure_cpu,&elapsed_time);

  /* send the results to the sender			*/

  if (debug) {
    fprintf(where,
	    "recv_dlpi_co_stream: got %d bytes\n",
	    bytes_received);
    fprintf(where,
	    "recv_dlpi_co_stream: got %d recvs\n",
	    receive_calls);
    fflush(where);
  }

  dlpi_co_stream_results->bytes_received	= bytes_received;
  dlpi_co_stream_results->elapsed_time	= elapsed_time;
  dlpi_co_stream_results->recv_calls		= receive_calls;

  if (dlpi_co_stream_request->measure_cpu) {
    dlpi_co_stream_results->cpu_util	= calc_cpu_util(0.0);
  };

  if (debug > 1) {
    fprintf(where,
	    "recv_dlpi_co_stream: test complete, sending results.\n");
    fflush(where);
  }

  send_response();
}

/*********************************/

int send_dlpi_co_rr(char remote_host[])
{

  char *tput_title = "\
 Local /Remote\n\
 Window Size   Request  Resp.   Elapsed  Trans.\n\
 Send   Recv   Size     Size    Time     Rate         \n\
 frames frames 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\
 Window Size   Request Resp.  Elapsed Trans.   CPU    CPU    S.dem   S.dem\n\
 Send   Recv   Size    Size   Time    Rate     local  remote local   remote\n\
 frames frames bytes   bytes  secs.   per sec  %%      %%      us/Tr   us/Tr\n\n";

  char *cpu_fmt_0 =
    "%6.3f\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	    dlsap_len;
  char      dlsap[BUFSIZ];

  int   flags = 0;
  char	*send_message_ptr;
  char	*recv_message_ptr;
  char	*temp_message_ptr;
  struct strbuf send_message;
  struct strbuf recv_message;

  int	nummessages;
  int	send_descriptor;
  int	trans_remaining;
  double	bytes_xferd;

  int	rsp_bytes_left;

  /* we assume that station adresses fit within two ints */
  unsigned int   remote_address[1];

  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;
  double	thruput;

  struct	dlpi_co_rr_request_struct	*dlpi_co_rr_request;
  struct	dlpi_co_rr_response_struct	*dlpi_co_rr_response;
  struct	dlpi_co_rr_results_struct	*dlpi_co_rr_result;

  dlpi_co_rr_request	=
    (struct dlpi_co_rr_request_struct *)netperf_request.content.test_specific_data;
  dlpi_co_rr_response	=
    (struct dlpi_co_rr_response_struct *)netperf_response.content.test_specific_data;
  dlpi_co_rr_result	=
    (struct dlpi_co_rr_results_struct *)netperf_response.content.test_specific_data;

  /* 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. */

  if ( print_headers ) {
    fprintf(where,"DLPI CO REQUEST/RESPONSE TEST\n");
    if (local_cpu_usage || remote_cpu_usage)
      fprintf(where,cpu_title,format_units());
    else
      fprintf(where,tput_title,format_units());
  }

  /* initialize a few counters */

  nummessages	=	0;
  bytes_xferd	=	0.0;
  times_up 	= 	0;

  /* set-up the data buffers with the requested alignment and offset */
  temp_message_ptr = (char *)malloc(req_size+MAXALIGNMENT+MAXOFFSET);
  if (temp_message_ptr == NULL) {
    printf("malloc(%d) failed!\n", req_size+MAXALIGNMENT+MAXOFFSET);
    exit(1);
  }
  send_message_ptr = (char *)(( (long) temp_message_ptr +
			       (long) local_send_align - 1) &
			      ~((long) local_send_align - 1));
  send_message_ptr = send_message_ptr + local_send_offset;
  send_message.maxlen = req_size+MAXALIGNMENT+MAXOFFSET;
  send_message.len    = req_size;
  send_message.buf    = send_message_ptr;

  temp_message_ptr = (char *)malloc(rsp_size+MAXALIGNMENT+MAXOFFSET);
  if (temp_message_ptr == NULL) {
    printf("malloc(%d) failed!\n", rsp_size+MAXALIGNMENT+MAXOFFSET);
    exit(1);
  }
  recv_message_ptr = (char *)(( (long) temp_message_ptr +
			       (long) local_recv_align - 1) &
			      ~((long) local_recv_align - 1));
  recv_message_ptr = recv_message_ptr + local_recv_offset;
  recv_message.maxlen = rsp_size+MAXALIGNMENT+MAXOFFSET;
  recv_message.len    = 0;
  recv_message.buf    = send_message_ptr;

  /*set up the data socket                        */

  send_descriptor = dl_open(loc_dlpi_device,loc_ppa);
  if (send_descriptor < 0){
    perror("netperf: send_dlpi_co_rr: tcp stream data descriptor");
    exit(1);
  }

  if (debug) {
    fprintf(where,"send_dlpi_co_rr: send_descriptor obtained...\n");
  }

  /* bind the puppy and get the assigned dlsap */

  dlsap_len = BUFSIZ;
  if (dl_bind(send_descriptor,
	      dlpi_sap, DL_CODLS, dlsap, &dlsap_len) != 0) {
    fprintf(where,"send_dlpi_co_rr: bind failure\n");
    fflush(where);
    exit(1);
  }

  /* Modify the local socket size. The reason we alter the send buffer */
  /* size here rather than when the connection is made is to take care */
  /* of decreases in buffer size. Decreasing the window size after */
  /* connection establishment is a TCP no-no. Also, by setting the */
  /* buffer (window) size before the connection is established, we can */
  /* control the TCP MSS (segment size). The MSS is never more that 1/2 */
  /* the minimum receive buffer size at each half of the connection. */
  /* This is why we are altering the receive buffer size on the sending */
  /* size of a unidirectional transfer. If the user has not requested */
  /* that the socket buffers be altered, we will try to find-out what */
  /* their values are. If we cannot touch the socket buffer in any way, */
  /* we will set the values to -1 to indicate that.  */

#ifdef DL_HP_SET_LOCAL_WIN_REQ
  if (lsw_size > 0) {
    if (debug > 1) {
      fprintf(where,"netperf: send_dlpi_co_rr: socket send size altered from system default...\n");
      fprintf(where,"                          send: %d\n",lsw_size);
    }
  }
  if (lrw_size > 0) {
    if (debug > 1) {
      fprintf(where,"netperf: send_dlpi_co_rr: socket recv size altered from system default...\n");
      fprintf(where,"                          recv: %d\n",lrw_size);
    }
  }


  /* Now, we will find-out what the size actually became, and report */
  /* that back to the user. If the call fails, we will just report a -1 */
  /* back to the initiator for the recv buffer size. */


  if (debug) {
    fprintf(where,"netperf: send_dlpi_co_rr: socket sizes determined...\n");
    fprintf(where,"         send: %d recv: %d\n",lsw_size,lrw_size);
  }

#else /* DL_HP_SET_LOCAL_WIN_REQ */

  lsw_size = -1;
  lrw_size = -1;

#endif /* DL_HP_SET_LOCAL_WIN_REQ */

  /* 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_DLPI_CO_RR;
  dlpi_co_rr_request->recv_win_size	=	rrw_size;
  dlpi_co_rr_request->send_win_size	=	rsw_size;
  dlpi_co_rr_request->recv_alignment	=	remote_recv_align;
  dlpi_co_rr_request->recv_offset	=	remote_recv_offset;
  dlpi_co_rr_request->send_alignment	=	remote_send_align;
  dlpi_co_rr_request->send_offset	=	remote_send_offset;
  dlpi_co_rr_request->request_size	=	req_size;
  dlpi_co_rr_request->response_size	=	rsp_size;
  dlpi_co_rr_request->measure_cpu	=	remote_cpu_usage;
  dlpi_co_rr_request->cpu_rate	        =	remote_cpu_rate;
  dlpi_co_rr_request->ppa               =       rem_ppa;
  dlpi_co_rr_request->sap               =       dlpi_sap;
  dlpi_co_rr_request->dev_name_len      =       strlen(rem_dlpi_device);
  strcpy(dlpi_co_rr_request->dlpi_device,
	 rem_dlpi_device);
#ifdef __alpha

  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;

    initword = (int *) dlpi_co_rr_request->dlpi_device;
    lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4);

    for (charword = initword;
	 charword < lastword;
	 charword++) {

      *charword = ntohl(*charword);
    }
  }
#endif /* __alpha */

  if (test_time) {
    dlpi_co_rr_request->test_length	=	test_time;
  }
  else {
    dlpi_co_rr_request->test_length	=	test_trans * -1;
  }

  if (debug > 1) {
    fprintf(where,"netperf: send_dlpi_co_rr: requesting TCP 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 TCP tests.					*/

  recv_response();

  if (!netperf_response.content.serv_errno) {
    if (debug)
      fprintf(where,"remote listen done.\n");
    rrw_size	=	dlpi_co_rr_response->recv_win_size;
    rsw_size	=	dlpi_co_rr_response->send_win_size;
    remote_cpu_usage=	dlpi_co_rr_response->measure_cpu;
    remote_cpu_rate = 	dlpi_co_rr_response->cpu_rate;

  }
  else {
    Set_errno(netperf_response.content.serv_errno);
    perror("netperf: remote error");

    exit(1);
  }

  /*Connect up to the remote port on the data descriptor  */

  if(dl_connect(send_descriptor,
		dlpi_co_rr_response->station_addr,
		dlpi_co_rr_response->station_addr_len) != 0) {
    fprintf(where,"send_dlpi_co_rr: connect failure\n");
    fflush(where);
    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);

  /* 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. */

  while ((!times_up) || (trans_remaining > 0)) {
    /* send the request */
    if((putmsg(send_descriptor,
	       0,
	       &send_message,
	       0)) != 0) {
      if (errno == EINTR) {
	/* we hit the end of a */
	/* timed test. */
	timed_out = 1;
	break;
      }
      perror("send_dlpi_co_rr: putmsg error");
      exit(1);
    }

    if (debug) {
      fprintf(where,"recv_message.len %d\n",recv_message.len);
      fprintf(where,"send_message.len %d\n",send_message.len);
      fflush(where);
    }

    /* receive the response */
    /* this needs some work with streams buffers if we are going to */
    /* support requests and responses larger than the MTU of the */
    /* network, but this can wait until later */
    rsp_bytes_left = rsp_size;
    recv_message.len = rsp_size;
    while(rsp_bytes_left > 0) {
      if((getmsg(send_descriptor,
		 0,
		 &recv_message,
		 &flags)) < 0) {
	if (errno == EINTR) {
	  /* We hit the end of a timed test. */
	  timed_out = 1;
	  break;
	}
	perror("send_dlpi_co_rr: data recv error");
	exit(1);
      }
      rsp_bytes_left -= recv_message.len;
    }

    if (timed_out) {
      /* we may have been in a nested while loop - we need */
      /* another call to break. */
      break;
    }

    nummessages++;
    if (trans_remaining) {
      trans_remaining--;
    }

    if (debug > 3) {
      fprintf(where,
	      "Transaction %d completed\n",
	      nummessages);
      fflush(where);
    }
  }

  /* At this point we used to call shutdown onthe 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 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);
    perror("netperf: remote error");

    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 TCP 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 ;-) We use */
  /* Kbytes/s as the units of thruput for a TCP stream test, where K = */
  /* 1024. A future enhancement *might* be to choose from a couple of */
  /* unit selections. */

  bytes_xferd	= (req_size * nummessages) + (rsp_size * nummessages);
  thruput		= calc_thruput(bytes_xferd);

  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) {
      if (local_cpu_rate == 0.0) {
	fprintf(where,"WARNING WARNING WARNING  WARNING WARNING WARNING  WARNING!\n");
	fprintf(where,"Local CPU usage numbers based on process information only!\n");
	fflush(where);
      }
      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	= -1.0;
      local_service_demand	= -1.0;
    }

    if (remote_cpu_usage) {
      if (remote_cpu_rate == 0.0) {
	fprintf(where,"DANGER  DANGER  DANGER    DANGER  DANGER  DANGER    DANGER!\n");
	fprintf(where,"Remote CPU usage numbers based on process information only!\n");
	fflush(where);
      }
      remote_cpu_utilization = dlpi_co_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,
						  dlpi_co_rr_result->num_cpus);
    }
    else {
      remote_cpu_utilization = -1.0;
      remote_service_demand  = -1.0;
    }

    /* 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...  */

    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand);
      }
      break;
    case 1:
      fprintf(where,
	      cpu_fmt_1_line_1,		/* the format string */
	      lsw_size,		/* local sendbuf size */
	      lrw_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* guess */
	      elapsed_time,		/* how long was the test */
	      nummessages/elapsed_time,
	      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,
	      rsw_size,
	      rrw_size);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      nummessages/elapsed_time);
      break;
    case 1:
      fprintf(where,
	      tput_fmt_1_line_1,	/* the format string */
	      lsw_size,
	      lrw_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* how large were the responses */
	      elapsed_time, 		/* how long did it take */
	      nummessages/elapsed_time);
      fprintf(where,
	      tput_fmt_1_line_2,
	      rsw_size, 		/* remote recvbuf size */
	      rrw_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. */

  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);
  }
  /* The test is over. Kill the data descriptor */

  if (close(send_descriptor) == -1) {
    perror("send_dlpi_co_rr: cannot shutdown tcp stream descriptor");
  }

}

void
  send_dlpi_cl_stream(char remote_host[])
{
  /************************************************************************/
  /*									*/
  /*               	UDP Unidirectional Send Test                    */
  /*									*/
  /************************************************************************/
  char *tput_title =
    "Window  Message  Elapsed      Messages                \n\
Size    Size     Time         Okay Errors   Throughput\n\
frames  bytes    secs            #      #   %s/sec\n\n";

  char *tput_fmt_0 =
    "%7.2f\n";

  char *tput_fmt_1 =
    "%5d   %5d    %-7.2f   %7d %6d    %7.2f\n\
%5d            %-7.2f   %7d           %7.2f\n\n";


  char *cpu_title =
    "Window  Message  Elapsed      Messages                   CPU     Service\n\
Size    Size     Time         Okay Errors   Throughput   Util    Demand\n\
frames  bytes    secs            #      #   %s/sec   %%       us/KB\n\n";

  char *cpu_fmt_0 =
    "%6.2f\n";

  char *cpu_fmt_1 =
    "%5d   %5d    %-7.2f   %7d %6d    %7.1f      %-6.2f  %-6.3f\n\
%5d            %-7.2f   %7d           %7.1f      %-6.2f  %-6.3f\n\n";

  int	messages_recvd;
  float	elapsed_time,
  local_cpu_utilization,
  remote_cpu_utilization;

  float	local_service_demand, remote_service_demand;
  double	local_thruput, remote_thruput;
  double	bytes_sent;
  double	bytes_recvd;


  int	*message_int_ptr;
  char	*message_ptr;
  char	*message;
  char  sctl_data[BUFSIZ];
  struct strbuf send_message;
  struct strbuf sctl_message;
  dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data;

  char dlsap[BUFSIZ];
  int  dlsap_len;
  int	message_offset;
  int	message_max_offset;
  int	failed_sends;
  int	failed_cows;
  int 	messages_sent;
  int 	data_descriptor;


#ifdef WANT_INTERVALS
  int	interval_count;
#endif /* WANT_INTERVALS */
#ifdef DIRTY
  int	i;
#endif /* DIRTY */

  struct	dlpi_cl_stream_request_struct	*dlpi_cl_stream_request;
  struct	dlpi_cl_stream_response_struct	*dlpi_cl_stream_response;
  struct	dlpi_cl_stream_results_struct	*dlpi_cl_stream_results;

  dlpi_cl_stream_request	= (struct dlpi_cl_stream_request_struct *)netperf_request.content.test_specific_data;
  dlpi_cl_stream_response	= (struct dlpi_cl_stream_response_struct *)netperf_response.content.test_specific_data;
  dlpi_cl_stream_results	= (struct dlpi_cl_stream_results_struct *)netperf_response.content.test_specific_data;

  if ( print_headers ) {
    printf("DLPI CL UNIDIRECTIONAL SEND TEST\n");
    if (local_cpu_usage || remote_cpu_usage)
      printf(cpu_title,format_units());
    else
      printf(tput_title,format_units());
  }

  failed_sends	= 0;
  messages_sent	= 0;
  times_up	= 0;

  /*set up the data descriptor			*/

  data_descriptor = dl_open(loc_dlpi_device,loc_ppa);
  if (data_descriptor < 0){
    perror("send_dlpi_cl_stream: data descriptor");
    exit(1);
  }

  /* bind the puppy and get the assigned dlsap */
  dlsap_len = BUFSIZ;
  if (dl_bind(data_descriptor,
              dlpi_sap, DL_CLDLS, dlsap, &dlsap_len) != 0) {
    fprintf(where,"send_dlpi_cl_stream: bind failure\n");
    fflush(where);
    exit(1);
  }

  /* Modify the local socket size (SNDBUF size)    */

#ifdef DL_HP_SET_LOCAL_WIN_REQ
  if (lsw_size > 0) {
    if (debug > 1) {
      fprintf(where,"netperf: send_dlpi_cl_stream: descriptor send size altered from system default...\n");
      fprintf(where,"                          send: %d\n",lsw_size);
    }
  }
  if (lrw_size > 0) {
    if (debug > 1) {
      fprintf(where,"netperf: send_dlpi_cl_stream: descriptor recv size altered from system default...\n");
      fprintf(where,"                          recv: %d\n",lrw_size);
    }
  }


  /* Now, we will find-out what the size actually became, and report */
  /* that back to the user. If the call fails, we will just report a -1 */
  /* back to the initiator for the recv buffer size. */

#else /* DL_HP_SET_LOCAL_WIN_REQ */

  lsw_size = -1;
  lrw_size = -1;

#endif /* DL_HP_SET_LOCAL_WIN_REQ */

  /* now, we want to see if we need to set the send_size */
  if (send_size == 0) {
    send_size = 1024;
  }


  /* set-up the data buffer with the requested alignment and offset, */
  /* most of the numbers here are just a hack to pick something nice */
  /* and big in an attempt to never try to send a buffer a second time */
  /* before it leaves the node...unless the user set the width */
  /* explicitly. */
  if (send_width == 0) send_width = 32;
  message = (char *)malloc(send_size * (send_width + 1) + local_send_align + local_send_offset);
  if (message == NULL) {
    printf("malloc(%d) failed!\n", send_size * (send_width + 1) + local_send_align + local_send_offset);
    exit(1);
  }
  message_ptr = (char *)(( (long) message +
			  (long) local_send_align - 1) &
			 ~((long) local_send_align - 1));
  message_ptr = message_ptr + local_send_offset;
  message = message_ptr;
  send_message.maxlen = send_size;
  send_message.len = send_size;
  send_message.buf = message;

  sctl_message.maxlen = BUFSIZ;
  sctl_message.len    = 0;
  sctl_message.buf    = sctl_data;

  /* if the user supplied a cpu rate, this call will complete rather */
  /* quickly, otherwise, the cpu rate will be retured to us for */
  /* possible display. The Library will keep it's own copy of this data */
  /* for use elsewhere. We will only display it. (Does that make it */
  /* "opaque" to us?) */

  if (local_cpu_usage)
    local_cpu_rate = calibrate_local_cpu(local_cpu_rate);

  /* Tell the remote end to set up the data connection. The server */
  /* sends back the port number and alters the socket parameters there. */
  /* Of course this is a datagram service so no connection is actually */
  /* set up, the server just sets up the socket and binds it. */

  netperf_request.content.request_type                 = DO_DLPI_CL_STREAM;
  dlpi_cl_stream_request->recv_win_size	        = rrw_size;
  dlpi_cl_stream_request->message_size	        = send_size;
  dlpi_cl_stream_request->recv_alignment	= remote_recv_align;
  dlpi_cl_stream_request->recv_offset		= remote_recv_offset;
  dlpi_cl_stream_request->measure_cpu		= remote_cpu_usage;
  dlpi_cl_stream_request->cpu_rate		= remote_cpu_rate;
  dlpi_cl_stream_request->ppa                   = rem_ppa;
  dlpi_cl_stream_request->sap                   = dlpi_sap;
  dlpi_cl_stream_request->dev_name_len          = strlen(rem_dlpi_device);
  strcpy(dlpi_cl_stream_request->dlpi_device,
	 rem_dlpi_device);

#ifdef __alpha

  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;

    initword = (int *) dlpi_cl_stream_request->dlpi_device;
    lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4);

    for (charword = initword;
	 charword < lastword;
	 charword++) {

      *charword = ntohl(*charword);
    }
  }
#endif /* __alpha */

  if (test_time) {
    dlpi_cl_stream_request->test_length	=	test_time;
  }
  else {
    dlpi_cl_stream_request->test_length	=	test_bytes * -1;
  }


  send_request();

  recv_response();

  if (!netperf_response.content.serv_errno) {
    if (debug)
      fprintf(where,"send_dlpi_cl_stream: remote data connection done.\n");
  }
  else {
    Set_errno(netperf_response.content.serv_errno);
    perror("send_dlpi_cl_stream: error on remote");
    exit(1);
  }

  /* place some of the remote's addressing information into the send */
  /* structure so our sends can be sent to the correct place. Also get */
  /* some of the returned socket buffer information for user display. */

  /* set-up the destination addressing control info */
  data_req->dl_primitive = DL_UNITDATA_REQ;
  bcopy((char *)(dlpi_cl_stream_response->station_addr),
	((char *)data_req + sizeof(dl_unitdata_req_t)),
	dlpi_cl_stream_response->station_addr_len);
  data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t);
  data_req->dl_dest_addr_length = dlpi_cl_stream_response->station_addr_len;
  /* there is a dl_priority structure too, but I am ignoring it for */
  /* the time being. */
  /* however... it is best to put some value in there lest some code
     get grumpy about it - fix from Nicolas Thomas */
  data_req->dl_priority.dl_min = DL_QOS_DONT_CARE;
  data_req->dl_priority.dl_max = DL_QOS_DONT_CARE;

  sctl_message.len = sizeof(dl_unitdata_req_t) +
    data_req->dl_dest_addr_length;

  rrw_size	        = dlpi_cl_stream_response->recv_win_size;
  rsw_size	        = dlpi_cl_stream_response->send_win_size;
  remote_cpu_rate	= dlpi_cl_stream_response->cpu_rate;


  /* set up the timer to call us after test_time	*/
  start_timer(test_time);

  /* Get the start count for the idle counter and the start time */

  cpu_start(local_cpu_usage);

#ifdef WANT_INTERVALS
  interval_count = interval_burst;
#endif /* WANT_INTERVALS */

  /* Send datagrams like there was no tomorrow */
  while (!times_up) {
#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. */
    message_int_ptr = (int *)message_ptr;
    for (i = 0; i < loc_dirty_count; i++) {
      *message_int_ptr = 4;
      message_int_ptr++;
    }
    for (i = 0; i < loc_clean_count; i++) {
      loc_dirty_count = *message_int_ptr;
      message_int_ptr++;
    }
#endif /* DIRTY */
    if (putmsg(data_descriptor,
	       &sctl_message,
	       &send_message,
	       0)  != 0) {
      if (errno == EINTR) {
	break;
      }
      if (errno == ENOBUFS) {
	/* we might not ever hit this with STREAMS, it would probably */
	/* be better to do a getinfo request at the end of the test to */
	/* get all sorts of gory statistics. in the meantime, we will */
	/* keep this code in place. */
	failed_sends++;
	continue;
      }
      perror("send_dlpi_cl_stream: data send error");
      if (debug) {
	fprintf(where,"messages_sent %u\n",messages_sent);
	fflush(where);
      }
      exit(1);
    }
    messages_sent++;

    /* now we want to move our pointer to the next position in the */
    /* data buffer...since there was a successful send */


#ifdef WANT_INTERVALS
    /* 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 the sleep routine for some milliseconds, if our */
      /* timer popped while we were in there, we want to */
      /* break out of the loop. */
      if (msec_sleep(interval_wate)) {
	break;
      }
      interval_count = interval_burst;
    }

#endif /* WANT_INTERVALS */

  }

  /* This is a timed test, so the remote will be returning to us after */
  /* a time. We should not need to send any "strange" messages to tell */
  /* the remote that the test is completed, unless we decide to add a */
  /* number of messages to the test. */

  /* the test is over, so get stats and stuff */
  cpu_stop(local_cpu_usage,
	   &elapsed_time);

  /* Get the statistics from the remote end	*/
  recv_response();
  if (!netperf_response.content.serv_errno) {
    if (debug)
      fprintf(where,"send_dlpi_cl_stream: remote results obtained\n");
  }
  else {
    Set_errno(netperf_response.content.serv_errno);
    perror("send_dlpi_cl_stream: error on remote");
    exit(1);
  }

  bytes_sent	= send_size * messages_sent;
  local_thruput	= calc_thruput(bytes_sent);

  messages_recvd	= dlpi_cl_stream_results->messages_recvd;
  bytes_recvd	= send_size * messages_recvd;

  /* we asume that the remote ran for as long as we did */

  remote_thruput	= calc_thruput(bytes_recvd);

  /* print the results for this descriptor and message size */

  if (local_cpu_usage || remote_cpu_usage) {
    /* We must now do a little math for service demand and cpu */
    /* utilization for the system(s) We pass zeros for the local */
    /* cpu utilization and elapsed time to tell the routine to use */
    /* the libraries own values for those. */
    if (local_cpu_usage) {
      if (local_cpu_rate == 0.0) {
	fprintf(where,"WARNING WARNING WARNING  WARNING WARNING WARNING  WARNING!\n");
	fprintf(where,"Local CPU usage numbers based on process information only!\n");
	fflush(where);
      }

      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	= -1.0;
      local_service_demand	= -1.0;
    }

    /* The local calculations could use variables being kept by */
    /* the local netlib routines. The remote calcuations need to */
    /* have a few things passed to them. */
    if (remote_cpu_usage) {
      if (remote_cpu_rate == 0.0) {
	fprintf(where,"DANGER   DANGER  DANGER   DANGER  DANGER   DANGER   DANGER!\n");
	fprintf(where,"REMOTE CPU usage numbers based on process information only!\n");
	fflush(where);
      }

      remote_cpu_utilization	= dlpi_cl_stream_results->cpu_util;
      remote_service_demand	= calc_service_demand(bytes_recvd,
						      0.0,
						      remote_cpu_utilization,
						      dlpi_cl_stream_results->num_cpus);
    }
    else {
      remote_cpu_utilization	= -1.0;
      remote_service_demand	= -1.0;
    }

    /* 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...  */

    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand);
      }
      break;
    case 1:
      fprintf(where,
	      cpu_fmt_1,		/* the format string */
	      lsw_size,		/* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time,		/* how long was the test */
	      messages_sent,
	      failed_sends,
	      local_thruput, 		/* what was the xfer rate */
	      local_cpu_utilization,	/* local cpu */
	      local_service_demand,	/* local service demand */
	      rrw_size,
	      elapsed_time,
	      messages_recvd,
	      remote_thruput,
	      remote_cpu_utilization,	/* remote cpu */
	      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,
	      local_thruput);
      break;
    case 1:
      fprintf(where,
	      tput_fmt_1,		/* the format string */
	      lsw_size, 		/* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time, 		/* how long did it take */
	      messages_sent,
	      failed_sends,
	      local_thruput,
	      rrw_size, 		/* remote recvbuf size */
	      elapsed_time,
	      messages_recvd,
	      remote_thruput
	      );
      break;
    }
  }
}

int
  recv_dlpi_cl_stream()
{

  char  *message;
  int	data_descriptor;
  int	len;
  char	*message_ptr;
  char  rctl_data[BUFSIZ];
  struct strbuf recv_message;
  struct strbuf rctl_message;
  int flags = 0;
  /* these are to make reading some of the DLPI control messages easier */
  dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data;
  dl_uderror_ind_t  *uder_ind = (dl_uderror_ind_t *)rctl_data;

  int	bytes_received = 0;
  float	elapsed_time;

  int	message_size;
  int	messages_recvd = 0;
  int	measure_cpu;

  struct	dlpi_cl_stream_request_struct	*dlpi_cl_stream_request;
  struct	dlpi_cl_stream_response_struct	*dlpi_cl_stream_response;
  struct	dlpi_cl_stream_results_struct	*dlpi_cl_stream_results;

  dlpi_cl_stream_request	= (struct dlpi_cl_stream_request_struct *)netperf_request.content.test_specific_data;
  dlpi_cl_stream_response	= (struct dlpi_cl_stream_response_struct *)netperf_response.content.test_specific_data;
  dlpi_cl_stream_results	= (struct dlpi_cl_stream_results_struct *)netperf_response.content.test_specific_data;

  if (debug) {
    fprintf(where,"netserver: recv_dlpi_cl_stream: entered...\n");
    fflush(where);
  }

  /* We want to set-up the listen descriptor 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 > 1) {
    fprintf(where,"recv_dlpi_cl_stream: setting the response type...\n");
    fflush(where);
  }

  netperf_response.content.response_type = DLPI_CL_STREAM_RESPONSE;

  if (debug > 2) {
    fprintf(where,"recv_dlpi_cl_stream: the response type is set...\n");
    fflush(where);
  }

  /* set-up the data buffer with the requested alignment and offset */
  message = (char *)malloc(DATABUFFERLEN);
  if (message == NULL) {
    printf("malloc(%d) failed!\n", DATABUFFERLEN);
    exit(1);
  }

  /* We now alter the message_ptr variable to be at the desired */
  /* alignment with the desired offset. */

  if (debug > 1) {
    fprintf(where,"recv_dlpi_cl_stream: requested alignment of %d\n",
	    dlpi_cl_stream_request->recv_alignment);
    fflush(where);
  }

  message_ptr = ALIGN_BUFFER(message, dlpi_cl_stream_request->recv_alignment, dlpi_cl_stream_request->recv_offset);

  if (dlpi_cl_stream_request->message_size > 0) {
    recv_message.maxlen = dlpi_cl_stream_request->message_size;
  }
  else {
    recv_message.maxlen = 4096;
  }
  recv_message.len    = 0;
  recv_message.buf    = message_ptr;

  rctl_message.maxlen = BUFSIZ;
  rctl_message.len    = 0;
  rctl_message.buf    = rctl_data;

  if (debug > 1) {
    fprintf(where,
	    "recv_dlpi_cl_stream: receive alignment and offset set...\n");
    fflush(where);
  }

  if (debug > 1) {
    fprintf(where,"recv_dlpi_cl_stream: grabbing a descriptor...\n");
    fflush(where);
  }

#ifdef __alpha

  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;

    initword = (int *) dlpi_cl_stream_request->dlpi_device;
    lastword = initword + ((dlpi_cl_stream_request->dev_name_len + 3) / 4);

    for (charword = initword;
	 charword < lastword;
	 charword++) {

      *charword = htonl(*charword);
    }
  }
#endif /* __alpha */

  data_descriptor = dl_open(dlpi_cl_stream_request->dlpi_device,
			    dlpi_cl_stream_request->ppa);
  if (data_descriptor < 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }

  /* The initiator may have wished-us to modify the window */
  /* sizes. We should give it a shot. If he didn't ask us to change the */
  /* sizes, we should let him know what sizes were in use at this end. */
  /* If none of this code is compiled-in, then we will tell the */
  /* initiator that we were unable to play with the sizes by */
  /* setting the size in the response to -1. */

#ifdef DL_HP_SET_LOCAL_WIN_REQ

  if (dlpi_cl_stream_request->recv_win_size) {
    dlpi_cl_stream_response->recv_win_size	= -1;
  }

#else /* the system won't let us play with the buffers */

  dlpi_cl_stream_response->recv_win_size	= -1;

#endif /* DL_HP_SET_LOCAL_WIN_REQ */

  dlpi_cl_stream_response->test_length = dlpi_cl_stream_request->test_length;

  /* bind the sap and retrieve the dlsap assigned by the system  */
  dlpi_cl_stream_response->station_addr_len = 14; /* arbitrary */
  if (dl_bind(data_descriptor,
	      dlpi_cl_stream_request->sap,
	      DL_CLDLS,
	      (char *)dlpi_cl_stream_response->station_addr,
	      &dlpi_cl_stream_response->station_addr_len) != 0) {
    fprintf(where,"send_dlpi_cl_stream: bind failure\n");
    fflush(where);
    exit(1);
  }

  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. */

  dlpi_cl_stream_response->cpu_rate = 0.0; 	/* assume no cpu */
  if (dlpi_cl_stream_request->measure_cpu) {
    /* We will pass the rate into the calibration routine. If the */
    /* user did not specify one, it will be 0.0, and we will do a */
    /* "real" calibration. Otherwise, all it will really do is */
    /* store it away... */
    dlpi_cl_stream_response->measure_cpu = 1;
    dlpi_cl_stream_response->cpu_rate = calibrate_local_cpu(dlpi_cl_stream_request->cpu_rate);
  }

  message_size	= dlpi_cl_stream_request->message_size;
  test_time	= dlpi_cl_stream_request->test_length;

  send_response();

  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */

  cpu_start(dlpi_cl_stream_request->measure_cpu);

  /* The loop will exit when the timer pops, or if we happen to recv a */
  /* message of less than send_size bytes... */

  times_up = 0;
  start_timer(test_time + PAD_TIME);

  if (debug) {
    fprintf(where,"recv_dlpi_cl_stream: about to enter inner sanctum.\n");
    fflush(where);
  }

  while (!times_up) {
    if((getmsg(data_descriptor,
	       &rctl_message,
	       &recv_message,
	       &flags) != 0) ||
       (data_ind->dl_primitive != DL_UNITDATA_IND)) {
      if (errno == EINTR) {
	/* Again, we have likely hit test-end time */
	break;
      }
      fprintf(where,
	      "dlpi_recv_cl_stream: getmsg failure: errno %d primitive 0x%x\n",
	      errno,
	      data_ind->dl_primitive);
      fflush(where);
      netperf_response.content.serv_errno = 996;
      send_response();
      exit(1);
    }
    messages_recvd++;
  }

  if (debug) {
    fprintf(where,"recv_dlpi_cl_stream: got %d messages.\n",messages_recvd);
    fflush(where);
  }


  /* The loop now exits due timer or < send_size bytes received. */

  cpu_stop(dlpi_cl_stream_request->measure_cpu,&elapsed_time);

  if (times_up) {
    /* we ended on a timer, subtract the PAD_TIME */
    elapsed_time -= (float)PAD_TIME;
  }
  else {
    stop_timer();
  }

  if (debug) {
    fprintf(where,"recv_dlpi_cl_stream: test ended in %f seconds.\n",elapsed_time);
    fflush(where);
  }


  /* We will count the "off" message */
  bytes_received = (messages_recvd * message_size) + len;

  /* send the results to the sender			*/

  if (debug) {
    fprintf(where,
	    "recv_dlpi_cl_stream: got %d bytes\n",
	    bytes_received);
    fflush(where);
  }

  netperf_response.content.response_type		= DLPI_CL_STREAM_RESULTS;
  dlpi_cl_stream_results->bytes_received	= bytes_received;
  dlpi_cl_stream_results->messages_recvd	= messages_recvd;
  dlpi_cl_stream_results->elapsed_time	= elapsed_time;
  if (dlpi_cl_stream_request->measure_cpu) {
    dlpi_cl_stream_results->cpu_util	= calc_cpu_util(elapsed_time);
  }
  else {
    dlpi_cl_stream_results->cpu_util	= -1.0;
  }

  if (debug > 1) {
    fprintf(where,
	    "recv_dlpi_cl_stream: test complete, sending results.\n");
    fflush(where);
  }

  send_response();

}

int send_dlpi_cl_rr(char remote_host[])
{

  char *tput_title = "\
Local /Remote\n\
Window Size   Request  Resp.   Elapsed  Trans.\n\
Send   Recv   Size     Size    Time     Rate         \n\
frames frames 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\
Window Size   Request Resp.  Elapsed Trans.   CPU    CPU    S.dem   S.dem\n\
Send   Recv   Size    Size   Time    Rate     local  remote local   remote\n\
frames frames bytes   bytes  secs.   per sec  %%      %%      us/Tr   us/Tr\n\n";

  char *cpu_fmt_0 =
    "%6.3f\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";


  float			elapsed_time;

  int   dlsap_len;
  int   flags = 0;
  char	*send_message_ptr;
  char	*recv_message_ptr;
  char	*temp_message_ptr;
  char  sctl_data[BUFSIZ];
  char  rctl_data[BUFSIZ];
  char  dlsap[BUFSIZ];
  struct strbuf send_message;
  struct strbuf recv_message;
  struct strbuf sctl_message;
  struct strbuf rctl_message;

  /* these are to make reading some of the DLPI control messages easier */
  dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data;
  dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data;
  dl_uderror_ind_t  *uder_ind = (dl_uderror_ind_t *)rctl_data;

  int	nummessages;
  int	send_descriptor;
  int	trans_remaining;
  int	bytes_xferd;

  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;
  double thruput;

#ifdef WANT_INTERVALS
  /* timing stuff */
#define	MAX_KEPT_TIMES	1024
  int	time_index = 0;
  int	unused_buckets;
  int	kept_times[MAX_KEPT_TIMES];
  int	sleep_usecs;
  unsigned	int	total_times=0;
  struct	timezone	dummy_zone;
  struct	timeval		send_time;
  struct	timeval		recv_time;
  struct	timeval		sleep_timeval;
#endif /* WANT_INTERVALS */

  struct	dlpi_cl_rr_request_struct	*dlpi_cl_rr_request;
  struct	dlpi_cl_rr_response_struct	*dlpi_cl_rr_response;
  struct	dlpi_cl_rr_results_struct	*dlpi_cl_rr_result;

  dlpi_cl_rr_request	=
    (struct dlpi_cl_rr_request_struct *)netperf_request.content.test_specific_data;
  dlpi_cl_rr_response	=
    (struct dlpi_cl_rr_response_struct *)netperf_response.content.test_specific_data;
  dlpi_cl_rr_result	=
    (struct dlpi_cl_rr_results_struct *)netperf_response.content.test_specific_data;

  /* we want to zero out the times, so we can detect unused entries. */
#ifdef WANT_INTERVALS
  time_index = 0;
  while (time_index < MAX_KEPT_TIMES) {
    kept_times[time_index] = 0;
    time_index += 1;
  }
  time_index = 0;
#endif /* WANT_INTERVALS */

  if (print_headers) {
    fprintf(where,"DLPI CL REQUEST/RESPONSE TEST\n");
    if (local_cpu_usage || remote_cpu_usage)
      fprintf(where,cpu_title,format_units());
    else
      fprintf(where,tput_title,format_units());
  }

  /* initialize a few counters */

  nummessages	=	0;
  bytes_xferd	=	0;
  times_up 	= 	0;

  /* set-up the data buffer with the requested alignment and offset */
  temp_message_ptr = (char *)malloc(req_size+MAXALIGNMENT+MAXOFFSET);
  if (temp_message_ptr == NULL) {
    printf("malloc(%d) failed!\n", req_size+MAXALIGNMENT+MAXOFFSET);
    exit(1);
  }
  send_message_ptr = (char *)(( (long)temp_message_ptr +
			       (long) local_send_align - 1) &
			      ~((long) local_send_align - 1));
  send_message_ptr = send_message_ptr + local_send_offset;
  send_message.maxlen = req_size;
  send_message.len    = req_size;
  send_message.buf    = send_message_ptr;

  temp_message_ptr = (char *)malloc(rsp_size+MAXALIGNMENT+MAXOFFSET);
  if (temp_message_ptr == NULL) {
    printf("malloc(%d) failed!\n", rsp_size+MAXALIGNMENT+MAXOFFSET);
    exit(1);
  }
  recv_message_ptr = (char *)(( (long)temp_message_ptr +
			       (long) local_recv_align - 1) &
			      ~((long) local_recv_align - 1));
  recv_message_ptr = recv_message_ptr + local_recv_offset;
  recv_message.maxlen = rsp_size;
  recv_message.len    = 0;
  recv_message.buf    = recv_message_ptr;

  sctl_message.maxlen = BUFSIZ;
  sctl_message.len    = 0;
  sctl_message.buf    = sctl_data;

  rctl_message.maxlen = BUFSIZ;
  rctl_message.len    = 0;
  rctl_message.buf    = rctl_data;

  /* lets get ourselves a file descriptor */

  send_descriptor = dl_open(loc_dlpi_device,loc_ppa);
  if (send_descriptor < 0){
    perror("netperf: send_dlpi_cl_rr: dlpi cl rr send descriptor");
    exit(1);
  }

  if (debug) {
    fprintf(where,"send_dlpi_cl_rr: send_descriptor obtained...\n");
  }

  /* bind the sap to the descriptor and get the dlsap */
  dlsap_len = BUFSIZ;
  if (dl_bind(send_descriptor,
	      dlpi_sap,
	      DL_CLDLS,
	      dlsap,
	      &dlsap_len) != 0) {
    fprintf(where,"send_dlpi_cl_rr: bind failure\n");
    fflush(where);
    exit(1);
  }

  /* Modify the local socket size. If the user has not requested that */
  /* the socket buffers be altered, we will try to find-out what their */
  /* values are. If we cannot touch the socket buffer in any way, we */
  /* will set the values to -1 to indicate that.  The receive socket */
  /* must have enough space to hold addressing information so += a */
  /* sizeof struct sockaddr_in to it. */

  /* this is actually nothing code, and should be replaced with the */
  /* alalagous calls in the STREAM test where the window size is set */
  /* with the HP DLPI Extension. raj 8/94 */
#ifdef SO_SNDBUF
  if (lsw_size > 0) {
    if (debug > 1) {
      fprintf(where,"netperf: send_dlpi_cl_rr: local window size altered from system default...\n");
      fprintf(where,"                          window: %d\n",lsw_size);
    }
  }
  if (lrw_size > 0) {
    if (debug > 1) {
      fprintf(where,"netperf: send_dlpi_cl_rr: remote window size altered from system default...\n");
      fprintf(where,"                          remote: %d\n",lrw_size);
    }
  }


  /* Now, we will find-out what the size actually became, and report */
  /* that back to the user. If the call fails, we will just report a -1 */
  /* back to the initiator for the recv buffer size. */

  if (debug) {
    fprintf(where,"netperf: send_dlpi_cl_rr: socket sizes determined...\n");
    fprintf(where,"         send: %d recv: %d\n",lsw_size,lrw_size);
  }

#else /* SO_SNDBUF */

  lsw_size = -1;
  lrw_size = -1;

#endif /* SO_SNDBUF */

  /* 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 */
  /* there is no idle counter in the kernel idle loop, the */
  /* local_cpu_rate will be set to -1. */

  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_DLPI_CL_RR;
  dlpi_cl_rr_request->recv_win_size	=	rrw_size;
  dlpi_cl_rr_request->send_win_size	=	rsw_size;
  dlpi_cl_rr_request->recv_alignment	=	remote_recv_align;
  dlpi_cl_rr_request->recv_offset	=	remote_recv_offset;
  dlpi_cl_rr_request->send_alignment	=	remote_send_align;
  dlpi_cl_rr_request->send_offset	=	remote_send_offset;
  dlpi_cl_rr_request->request_size	=	req_size;
  dlpi_cl_rr_request->response_size	=	rsp_size;
  dlpi_cl_rr_request->measure_cpu	=	remote_cpu_usage;
  dlpi_cl_rr_request->cpu_rate	        =	remote_cpu_rate;
  dlpi_cl_rr_request->ppa               =       rem_ppa;
  dlpi_cl_rr_request->sap               =       dlpi_sap;
  dlpi_cl_rr_request->dev_name_len      =       strlen(rem_dlpi_device);
  strcpy(dlpi_cl_rr_request->dlpi_device,
	 rem_dlpi_device);

#ifdef __alpha

  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;

    initword = (int *) dlpi_cl_rr_request->dlpi_device;
    lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4);

    for (charword = initword;
	 charword < lastword;
	 charword++) {

      *charword = ntohl(*charword);
    }
  }
#endif /* __alpha */

  if (test_time) {
    dlpi_cl_rr_request->test_length	=	test_time;
  }
  else {
    dlpi_cl_rr_request->test_length	=	test_trans * -1;
  }

  if (debug > 1) {
    fprintf(where,"netperf: send_dlpi_cl_rr: requesting DLPI CL request/response 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 tests.                                          */

  recv_response();

  if (!netperf_response.content.serv_errno) {
    if (debug)
      fprintf(where,"remote listen done.\n");
    rrw_size	=	dlpi_cl_rr_response->recv_win_size;
    rsw_size	=	dlpi_cl_rr_response->send_win_size;
    remote_cpu_usage=	dlpi_cl_rr_response->measure_cpu;
    remote_cpu_rate = 	dlpi_cl_rr_response->cpu_rate;

    /* set-up the destination addressing control info */
    data_req->dl_primitive = DL_UNITDATA_REQ;
    bcopy((char *)(dlpi_cl_rr_response->station_addr),
	  ((char *)data_req + sizeof(dl_unitdata_req_t)),
	  dlpi_cl_rr_response->station_addr_len);
    data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t);
    data_req->dl_dest_addr_length = dlpi_cl_rr_response->station_addr_len;
    /* there is a dl_priority structure too, but I am ignoring it for */
    /* the time being. */
    sctl_message.len = sizeof(dl_unitdata_req_t) +
      data_req->dl_dest_addr_length;
    /* famous last words - some DLPI providers get unhappy if the
       priority stuff is not initialized.  fix from Nicolas Thomas. */
    data_req->dl_priority.dl_min = DL_QOS_DONT_CARE;
    data_req->dl_priority.dl_max = DL_QOS_DONT_CARE;

  }
  else {
    Set_errno(netperf_response.content.serv_errno);
    perror("netperf: remote error");
    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);

  /* 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. */
  while ((!times_up) || (trans_remaining > 0)) {
    /* send the request */
#ifdef WANT_INTERVALS
    gettimeofday(&send_time,&dummy_zone);
#endif /* WANT_INTERVALS */
    if(putmsg(send_descriptor,
	      &sctl_message,
	      &send_message,
	      0) != 0) {
      if (errno == EINTR) {
	/* We likely hit */
	/* test-end time. */
	break;
      }
      /* there is more we could do here, but it can wait */
      perror("send_dlpi_cl_rr: data send error");
      exit(1);
    }

    /* receive the response. at some point, we will need to handle */
    /* sending responses which are greater than the datalink MTU. we */
    /* may also want to add some DLPI error checking, but for now we */
    /* will ignore that and just let errors stop the test with little */
    /* indication of what might actually be wrong. */

    if((getmsg(send_descriptor,
	       &rctl_message,
	       &recv_message,
	       &flags) != 0) ||
       (data_ind->dl_primitive != DL_UNITDATA_IND)) {
      if (errno == EINTR) {
	/* Again, we have likely hit test-end time */
	break;
      }
      fprintf(where,
	      "send_dlpi_cl_rr: recv error: errno %d primitive 0x%x\n",
	      errno,
	      data_ind->dl_primitive);
      fflush(where);
      exit(1);
    }
#ifdef WANT_INTERVALS
    gettimeofday(&recv_time,&dummy_zone);

    /* now we do some arithmatic on the two timevals */
    if (recv_time.tv_usec < send_time.tv_usec) {
      /* we wrapped around a second */
      recv_time.tv_usec += 1000000;
      recv_time.tv_sec  -= 1;
    }

    /* and store it away */
    kept_times[time_index] = (recv_time.tv_sec - send_time.tv_sec) * 1000000;
    kept_times[time_index] += (recv_time.tv_usec - send_time.tv_usec);

    /* at this point, we may wish to sleep for some period of */
    /* time, so we see how long that last transaction just took, */
    /* and sleep for the difference of that and the interval. We */
    /* will not sleep if the time would be less than a */
    /* millisecond.  */
    if (interval_usecs > 0) {
      sleep_usecs = interval_usecs - kept_times[time_index];
      if (sleep_usecs > 1000) {
	/* we sleep */
	sleep_timeval.tv_sec = sleep_usecs / 1000000;
	sleep_timeval.tv_usec = sleep_usecs % 1000000;
	select(0,
	       0,
	       0,
	       0,
	       &sleep_timeval);
      }
    }

    /* now up the time index */
    time_index = (time_index +1)%MAX_KEPT_TIMES;
#endif /* WANT_INTERVALS */
    nummessages++;
    if (trans_remaining) {
      trans_remaining--;
    }

    if (debug > 3) {
      fprintf(where,"Transaction %d completed\n",nummessages);
      fflush(where);
    }

  }

  /* 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 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);
    perror("netperf: remote error");

    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 UDP 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 ;-) We use */

  bytes_xferd	= (req_size * nummessages) + (rsp_size * nummessages);
  thruput		= calc_thruput(bytes_xferd);

  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) {
      if (local_cpu_rate == 0.0) {
	fprintf(where,"WARNING WARNING WARNING  WARNING WARNING WARNING  WARNING!\n");
	fprintf(where,"Local CPU usage numbers based on process information only!\n");
	fflush(where);
      }
      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	= -1.0;
      local_service_demand	= -1.0;
    }

    if (remote_cpu_usage) {
      if (remote_cpu_rate == 0.0) {
	fprintf(where,"DANGER  DANGER  DANGER    DANGER  DANGER  DANGER    DANGER!\n");
	fprintf(where,"Remote CPU usage numbers based on process information only!\n");
	fflush(where);
      }
      remote_cpu_utilization = dlpi_cl_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,
						   dlpi_cl_rr_result->num_cpus);
    }
    else {
      remote_cpu_utilization = -1.0;
      remote_service_demand  = -1.0;
    }

    /* 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...  */

    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand);
      }
      break;
    case 1:
    case 2:
      fprintf(where,
	      cpu_fmt_1_line_1,		/* the format string */
	      lsw_size,		/* local sendbuf size */
	      lrw_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* guess */
	      elapsed_time,		/* how long was the test */
	      nummessages/elapsed_time,
	      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,
	      rsw_size,
	      rrw_size);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      nummessages/elapsed_time);
      break;
    case 1:
    case 2:
      fprintf(where,
	      tput_fmt_1_line_1,	/* the format string */
	      lsw_size,
	      lrw_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* how large were the responses */
	      elapsed_time, 		/* how long did it take */
	      nummessages/elapsed_time);
      fprintf(where,
	      tput_fmt_1_line_2,
	      rsw_size, 		/* remote recvbuf size */
	      rrw_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. */

  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 */
    /* UDP statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */

#ifdef WANT_INTERVALS
    kept_times[MAX_KEPT_TIMES] = 0;
    time_index = 0;
    while (time_index < MAX_KEPT_TIMES) {
      if (kept_times[time_index] > 0) {
	total_times += kept_times[time_index];
      }
      else
	unused_buckets++;
      time_index += 1;
    }
    total_times /= (MAX_KEPT_TIMES-unused_buckets);
    fprintf(where,
	    "Average response time %d usecs\n",
	    total_times);
#endif
  }
}

int
  recv_dlpi_cl_rr()
{

  char  *message;
  int	data_descriptor;
  int   flags = 0;
  int	measure_cpu;

  char	*recv_message_ptr;
  char	*send_message_ptr;
  char  sctl_data[BUFSIZ];
  char  rctl_data[BUFSIZ];
  char  dlsap[BUFSIZ];
  struct strbuf send_message;
  struct strbuf recv_message;
  struct strbuf sctl_message;
  struct strbuf rctl_message;

  /* these are to make reading some of the DLPI control messages easier */
  dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data;
  dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data;
  dl_uderror_ind_t  *uder_ind = (dl_uderror_ind_t *)rctl_data;

  int	trans_received;
  int	trans_remaining;
  float	elapsed_time;

  struct	dlpi_cl_rr_request_struct	*dlpi_cl_rr_request;
  struct	dlpi_cl_rr_response_struct	*dlpi_cl_rr_response;
  struct	dlpi_cl_rr_results_struct	*dlpi_cl_rr_results;

  dlpi_cl_rr_request  =
    (struct dlpi_cl_rr_request_struct *)netperf_request.content.test_specific_data;
  dlpi_cl_rr_response =
    (struct dlpi_cl_rr_response_struct *)netperf_response.content.test_specific_data;
  dlpi_cl_rr_results  =
    (struct dlpi_cl_rr_results_struct *)netperf_response.content.test_specific_data;

  if (debug) {
    fprintf(where,"netserver: recv_dlpi_cl_rr: entered...\n");
    fflush(where);
  }

  /* We want to set-up the listen descriptor 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 descriptor 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_dlpi_cl_rr: setting the response type...\n");
    fflush(where);
  }

  netperf_response.content.response_type = DLPI_CL_RR_RESPONSE;

  if (debug) {
    fprintf(where,"recv_dlpi_cl_rr: the response type is set...\n");
    fflush(where);
  }

  /* set-up the data buffer with the requested alignment and offset */
  message = (char *)malloc(DATABUFFERLEN);
  if (message == NULL) {
    printf("malloc(%d) failed!\n", DATABUFFERLEN);
    exit(1);
  }

  /* We now alter the message_ptr variables to be at the desired */
  /* alignments with the desired offsets. */

  if (debug) {
    fprintf(where,
	    "recv_dlpi_cl_rr: requested recv alignment of %d offset %d\n",
	    dlpi_cl_rr_request->recv_alignment,
	    dlpi_cl_rr_request->recv_offset);
    fprintf(where,
	    "recv_dlpi_cl_rr: requested send alignment of %d offset %d\n",
	    dlpi_cl_rr_request->send_alignment,
	    dlpi_cl_rr_request->send_offset);
    fflush(where);
  }

  recv_message_ptr = ALIGN_BUFFER(message, dlpi_cl_rr_request->recv_alignment, dlpi_cl_rr_request->recv_offset);
  recv_message.maxlen = dlpi_cl_rr_request->request_size;
  recv_message.len    = 0;
  recv_message.buf    = recv_message_ptr;

  send_message_ptr = ALIGN_BUFFER(message, dlpi_cl_rr_request->send_alignment, dlpi_cl_rr_request->send_offset);
  send_message.maxlen = dlpi_cl_rr_request->response_size;
  send_message.len    = dlpi_cl_rr_request->response_size;
  send_message.buf    = send_message_ptr;

  sctl_message.maxlen = BUFSIZ;
  sctl_message.len    = 0;
  sctl_message.buf    = sctl_data;

  rctl_message.maxlen = BUFSIZ;
  rctl_message.len    = 0;
  rctl_message.buf    = rctl_data;

  if (debug) {
    fprintf(where,"recv_dlpi_cl_rr: receive alignment and offset set...\n");
    fprintf(where,"recv_dlpi_cl_rr: grabbing a socket...\n");
    fflush(where);
  }


#ifdef __alpha

  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;

    initword = (int *) dlpi_cl_rr_request->dlpi_device;
    lastword = initword + ((dlpi_cl_rr_request->dev_name_len + 3) / 4);

    for (charword = initword;
	 charword < lastword;
	 charword++) {

      *charword = htonl(*charword);
    }
  }
#endif /* __alpha */

  data_descriptor = dl_open(dlpi_cl_rr_request->dlpi_device,
			    dlpi_cl_rr_request->ppa);
  if (data_descriptor < 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }


  /* The initiator may have wished-us to modify the window */
  /* sizes. We should give it a shot. If he didn't ask us to change the */
  /* sizes, we should let him know what sizes were in use at this end. */
  /* If none of this code is compiled-in, then we will tell the */
  /* initiator that we were unable to play with the sizes by */
  /* setting the size in the response to -1. */

#ifdef DL_HP_SET_LOCAL_WIN_REQ

  if (dlpi_cl_rr_request->recv_win_size) {
  }

  if (dlpi_cl_rr_request->send_win_size) {
  }

  /* Now, we will find-out what the sizes actually became, and report */
  /* them back to the user. If the calls fail, we will just report a -1 */
  /* back to the initiator for the buffer size. */

#else /* the system won't let us play with the buffers */

  dlpi_cl_rr_response->recv_win_size	= -1;
  dlpi_cl_rr_response->send_win_size	= -1;

#endif /* DL_HP_SET_LOCAL_WIN_REQ */

  /* bind the sap and retrieve the dlsap assigned by the system  */
  dlpi_cl_rr_response->station_addr_len = 14; /* arbitrary */
  if (dl_bind(data_descriptor,
	      dlpi_cl_rr_request->sap,
	      DL_CLDLS,
	      (char *)dlpi_cl_rr_response->station_addr,
	      &dlpi_cl_rr_response->station_addr_len) != 0) {
    fprintf(where,"send_dlpi_cl_rr: bind failure\n");
    fflush(where);
    exit(1);
  }

  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. */

  dlpi_cl_rr_response->cpu_rate = 0.0; 	/* assume no cpu */
  if (dlpi_cl_rr_request->measure_cpu) {
    dlpi_cl_rr_response->measure_cpu = 1;
    dlpi_cl_rr_response->cpu_rate = calibrate_local_cpu(dlpi_cl_rr_request->cpu_rate);
  }

  send_response();

  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start receiving. */

  cpu_start(dlpi_cl_rr_request->measure_cpu);

  if (dlpi_cl_rr_request->test_length > 0) {
    times_up = 0;
    trans_remaining = 0;
    start_timer(dlpi_cl_rr_request->test_length + PAD_TIME);
  }
else {
  times_up = 1;
  trans_remaining = dlpi_cl_rr_request->test_length * -1;
}

  while ((!times_up) || (trans_remaining > 0)) {

    /* receive the request from the other side. at some point we need */
    /* to handle "logical" requests and responses which are larger */
    /* than the data link MTU */

    if((getmsg(data_descriptor,
	       &rctl_message,
	       &recv_message,
	       &flags) != 0) ||
       (data_ind->dl_primitive != DL_UNITDATA_IND)) {
      if (errno == EINTR) {
	/* Again, we have likely hit test-end time */
	break;
      }
      fprintf(where,
	      "dlpi_recv_cl_rr: getmsg failure: errno %d primitive 0x%x\n",
	      errno,
	      data_ind->dl_primitive);
      fprintf(where,
	      "                 recevied %u transactions\n",
	      trans_received);
      fflush(where);
      netperf_response.content.serv_errno = 995;
      send_response();
      exit(1);
    }

    /* Now, send the response to the remote. first copy the dlsap */
    /* information from the receive to the sending control message */

    data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t);
    bcopy((char *)data_ind + data_ind->dl_src_addr_offset,
	  (char *)data_req + data_req->dl_dest_addr_offset,
	  data_ind->dl_src_addr_length);
    data_req->dl_dest_addr_length = data_ind->dl_src_addr_length;
    data_req->dl_primitive = DL_UNITDATA_REQ;
    /* be sure to initialize the priority fields. fix from Nicholas
       Thomas */
    data_req->dl_priority.dl_min = DL_QOS_DONT_CARE;
    data_req->dl_priority.dl_max = DL_QOS_DONT_CARE;

    sctl_message.len = sizeof(dl_unitdata_req_t) +
      data_ind->dl_src_addr_length;
    if(putmsg(data_descriptor,
	      &sctl_message,
	      &send_message,
	      0) != 0) {
      if (errno == EINTR) {
	/* We likely hit */
	/* test-end time. */
	break;
      }
      /* there is more we could do here, but it can wait */
      fprintf(where,
	      "dlpi_recv_cl_rr: putmsg failure: errno %d\n",
	      errno);
      fflush(where);
      netperf_response.content.serv_errno = 993;
      send_response();
      exit(1);
    }

    trans_received++;
    if (trans_remaining) {
      trans_remaining--;
    }

    if (debug) {
      fprintf(where,
	      "recv_dlpi_cl_rr: Transaction %d complete.\n",
	      trans_received);
      fflush(where);
    }

  }


  /* The loop now exits due to timeout or transaction count being */
  /* reached */

  cpu_stop(dlpi_cl_rr_request->measure_cpu,&elapsed_time);

  if (times_up) {
    /* 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_dlpi_cl_rr: got %d transactions\n",
	    trans_received);
    fflush(where);
  }

  dlpi_cl_rr_results->bytes_received	= (trans_received *
					   (dlpi_cl_rr_request->request_size +
					    dlpi_cl_rr_request->response_size));
  dlpi_cl_rr_results->trans_received	= trans_received;
  dlpi_cl_rr_results->elapsed_time	= elapsed_time;
  if (dlpi_cl_rr_request->measure_cpu) {
    dlpi_cl_rr_results->cpu_util	= calc_cpu_util(elapsed_time);
  }

  if (debug) {
    fprintf(where,
	    "recv_dlpi_cl_rr: test complete, sending results.\n");
    fflush(where);
  }

  send_response();

}

int
  recv_dlpi_co_rr()
{

  char  *message;
  SOCKET	s_listen,data_descriptor;

  int	measure_cpu;

  int   flags = 0;
  char	*recv_message_ptr;
  char	*send_message_ptr;
  struct strbuf send_message;
  struct strbuf recv_message;

  int	trans_received;
  int	trans_remaining;
  int	request_bytes_remaining;
  int	timed_out = 0;
  float	elapsed_time;

  struct	dlpi_co_rr_request_struct	*dlpi_co_rr_request;
  struct	dlpi_co_rr_response_struct	*dlpi_co_rr_response;
  struct	dlpi_co_rr_results_struct	*dlpi_co_rr_results;

  dlpi_co_rr_request	= (struct dlpi_co_rr_request_struct *)netperf_request.content.test_specific_data;
  dlpi_co_rr_response	= (struct dlpi_co_rr_response_struct *)netperf_response.content.test_specific_data;
  dlpi_co_rr_results	= (struct dlpi_co_rr_results_struct *)netperf_response.content.test_specific_data;

  if (debug) {
    fprintf(where,"netserver: recv_dlpi_co_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_dlpi_co_rr: setting the response type...\n");
    fflush(where);
  }

  netperf_response.content.response_type = DLPI_CO_RR_RESPONSE;

  if (debug) {
    fprintf(where,"recv_dlpi_co_rr: the response type is set...\n");
    fflush(where);
  }

  /* set-up the data buffer with the requested alignment and offset */
  message = (char *)malloc(DATABUFFERLEN);
  if (message == NULL) {
    printf("malloc(%d) failed!\n", DATABUFFERLEN);
    exit(1);
  }

  /* We now alter the message_ptr variables to be at the desired */
  /* alignments with the desired offsets. */

  if (debug) {
    fprintf(where,
	    "recv_dlpi_co_rr: requested recv alignment of %d offset %d\n",
	    dlpi_co_rr_request->recv_alignment,
	    dlpi_co_rr_request->recv_offset);
    fprintf(where,
	    "recv_dlpi_co_rr: requested send alignment of %d offset %d\n",
	    dlpi_co_rr_request->send_alignment,
	    dlpi_co_rr_request->send_offset);
    fflush(where);
  }

  recv_message_ptr = ALIGN_BUFFER(message, dlpi_co_rr_request->recv_alignment, dlpi_co_rr_request->recv_offset);
  recv_message.maxlen = dlpi_co_rr_request->request_size;
  recv_message.len    = 0;
  recv_message.buf    = recv_message_ptr;

  send_message_ptr = ALIGN_BUFFER(message, dlpi_co_rr_request->send_alignment, dlpi_co_rr_request->send_offset);
  send_message.maxlen = dlpi_co_rr_request->response_size;
  send_message.len    = dlpi_co_rr_request->response_size;
  send_message.buf    = send_message_ptr;

  if (debug) {
    fprintf(where,"recv_dlpi_co_rr: receive alignment and offset set...\n");
    fprintf(where,"recv_dlpi_co_rr: send_message.buf %x .len %d .maxlen %d\n",
	    send_message.buf,send_message.len,send_message.maxlen);
    fprintf(where,"recv_dlpi_co_rr: recv_message.buf %x .len %d .maxlen %d\n",
	    recv_message.buf,recv_message.len,recv_message.maxlen);
    fflush(where);
  }

  /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */
  /* can put in OUR values !-) At some point, we may want to nail this */
  /* socket to a particular network-level address, but for now, */
  /* INADDR_ANY should be just fine. */

  /* Grab a socket to listen on, and then listen on it. */

  if (debug) {
    fprintf(where,"recv_dlpi_co_rr: grabbing a socket...\n");
    fflush(where);
  }

  /* lets grab a file descriptor for a particular link */

#ifdef __alpha

  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;

    initword = (int *) dlpi_co_rr_request->dlpi_device;
    lastword = initword + ((dlpi_co_rr_request->dev_name_len + 3) / 4);

    for (charword = initword;
	 charword < lastword;
	 charword++) {

      *charword = htonl(*charword);
    }
  }
#endif /* __alpha */

  if ((data_descriptor = dl_open(dlpi_co_rr_request->dlpi_device,
				 dlpi_co_rr_request->ppa)) < 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }

  /* bind the file descriptor to a sap and get the resultant dlsap */
  dlpi_co_rr_response->station_addr_len = 14; /*arbitrary needs fixing */
  if (dl_bind(data_descriptor,
              dlpi_co_rr_request->sap,
              DL_CODLS,
              (char *)dlpi_co_rr_response->station_addr,
              &dlpi_co_rr_response->station_addr_len) != 0) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }

  /* The initiator may have wished-us to modify the socket buffer */
  /* sizes. We should give it a shot. If he didn't ask us to change the */
  /* sizes, we should let him know what sizes were in use at this end. */
  /* If none of this code is compiled-in, then we will tell the */
  /* initiator that we were unable to play with the socket buffer by */
  /* setting the size in the response to -1. */

#ifdef DL_HP_SET_LOCAL_WIN_REQ

  if (dlpi_co_rr_request->recv_win_size) {
    /* SMOP */
  }

  if (dlpi_co_rr_request->send_win_size) {
    /* SMOP */
  }

  /* Now, we will find-out what the sizes actually became, and report */
  /* them back to the user. If the calls fail, we will just report a -1 */
  /* back to the initiator for the buffer size. */

#else /* the system won't let us play with the buffers */

  dlpi_co_rr_response->recv_win_size	= -1;
  dlpi_co_rr_response->send_win_size	= -1;

#endif /* DL_HP_SET_LOCAL_WIN_REQ */

  /* we may have been requested to enable the copy avoidance features. */
  /* can we actually do this with DLPI, the world wonders */

  if (dlpi_co_rr_request->so_rcvavoid) {
#ifdef SO_RCV_COPYAVOID
    dlpi_co_rr_response->so_rcvavoid = 0;
#else
    /* it wasn't compiled in... */
    dlpi_co_rr_response->so_rcvavoid = 0;
#endif
  }

  if (dlpi_co_rr_request->so_sndavoid) {
#ifdef SO_SND_COPYAVOID
    dlpi_co_rr_response->so_sndavoid = 0;
#else
    /* it wasn't compiled in... */
    dlpi_co_rr_response->so_sndavoid = 0;
#endif
  }

  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. */

  dlpi_co_rr_response->cpu_rate = 0.0; 	/* assume no cpu */
  if (dlpi_co_rr_request->measure_cpu) {
    dlpi_co_rr_response->measure_cpu = 1;
    dlpi_co_rr_response->cpu_rate = calibrate_local_cpu(dlpi_co_rr_request->cpu_rate);
  }

  send_response();

  /* accept a connection on this file descriptor. at some point, */
  /* dl_accept will "do the right thing" with the last two parms, but */
  /* for now it ignores them, so we will pass zeros. */

  if(dl_accept(data_descriptor, 0, 0) != 0) {
    fprintf(where,
	    "recv_dlpi_co_rr: error in accept, errno %d\n",
	    errno);
    fflush(where);
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }

  if (debug) {
    fprintf(where,
	    "recv_dlpi_co_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(dlpi_co_rr_request->measure_cpu);

  /* The loop will exit when the sender does a shutdown, which will */
  /* return a length of zero   */

  if (dlpi_co_rr_request->test_length > 0) {
    times_up = 0;
    trans_remaining = 0;
    start_timer(dlpi_co_rr_request->test_length + PAD_TIME);
  }
else {
  times_up = 1;
  trans_remaining = dlpi_co_rr_request->test_length * -1;
}

  while ((!times_up) || (trans_remaining > 0)) {
    request_bytes_remaining	= dlpi_co_rr_request->request_size;

    /* receive the request from the other side. there needs to be some */
    /* more login in place for handling messages larger than link mtu, */
    /* but that can wait for later */
    while(request_bytes_remaining > 0) {
      if((getmsg(data_descriptor,
		 0,
		 &recv_message,
		 &flags)) < 0) {
	if (errno == EINTR) {
	  /* the timer popped */
	  timed_out = 1;
	  break;
	}

        if (debug) {
	  fprintf(where,"failed getmsg call errno %d\n",errno);
	  fprintf(where,"recv_message.len %d\n",recv_message.len);
	  fprintf(where,"send_message.len %d\n",send_message.len);
	  fflush(where);
        }

	netperf_response.content.serv_errno = errno;
	send_response();
	exit(1);
      }
      else {
	request_bytes_remaining -= recv_message.len;
      }
    }

    if (timed_out) {
      /* we hit the end of the test based on time - lets bail out of */
      /* here now... */
      break;
    }

    if (debug) {
      fprintf(where,"recv_message.len %d\n",recv_message.len);
      fprintf(where,"send_message.len %d\n",send_message.len);
      fflush(where);
    }

    /* Now, send the response to the remote */
    if((putmsg(data_descriptor,
	       0,
	       &send_message,
	       0)) != 0) {
      if (errno == EINTR) {
	/* the test timer has popped */
	timed_out = 1;
	break;
      }
      netperf_response.content.serv_errno = 994;
      send_response();
      exit(1);
    }

    trans_received++;
    if (trans_remaining) {
      trans_remaining--;
    }

    if (debug) {
      fprintf(where,
	      "recv_dlpi_co_rr: Transaction %d complete\n",
	      trans_received);
      fflush(where);
    }
  }


  /* The loop now exits due to timeout or transaction count being */
  /* reached */

  cpu_stop(dlpi_co_rr_request->measure_cpu,&elapsed_time);

  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_dlpi_co_rr: got %d transactions\n",
	    trans_received);
    fflush(where);
  }

  dlpi_co_rr_results->bytes_received	= (trans_received *
					   (dlpi_co_rr_request->request_size +
					    dlpi_co_rr_request->response_size));
  dlpi_co_rr_results->trans_received	= trans_received;
  dlpi_co_rr_results->elapsed_time	= elapsed_time;
  if (dlpi_co_rr_request->measure_cpu) {
    dlpi_co_rr_results->cpu_util	= calc_cpu_util(elapsed_time);
  }

  if (debug) {
    fprintf(where,
	    "recv_dlpi_co_rr: test complete, sending results.\n");
    fflush(where);
  }

  send_response();

}

/* this routine will display the usage string for the DLPI tests */
void
  print_dlpi_usage()

{
  fwrite(dlpi_usage, sizeof(char), strlen(dlpi_usage), stdout);
}


/* this routine will scan the command line for DLPI test arguments */
void
  scan_dlpi_args(int argc, char *argv[])
{
  extern int	optind, opterrs;  /* index of first unused arg 	*/
  extern char	*optarg;	  /* pointer to option string	*/

  int		c;

  char	arg1[BUFSIZ],  /* argument holders		*/
  arg2[BUFSIZ];

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

  /* 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.. */

#define DLPI_ARGS "D:hM:m:p:r:s:W:w:"

  while ((c= getopt(argc, argv, DLPI_ARGS)) != EOF) {
    switch (c) {
    case '?':
    case 'h':
      print_dlpi_usage();
      exit(1);
    case 'D':
      /* set the dlpi device file name(s) */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	strcpy(loc_dlpi_device,arg1);
      if (arg2[0])
	strcpy(rem_dlpi_device,arg2);
      break;
    case 'm':
      /* set the send size */
      send_size = atoi(optarg);
      break;
    case 'M':
      /* set the recv size */
      recv_size = atoi(optarg);
      break;
    case 'p':
      /* set the local/remote ppa */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	loc_ppa = atoi(arg1);
      if (arg2[0])
	rem_ppa = atoi(arg2);
      break;
    case 'r':
      /* set the request/response sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	req_size = atoi(arg1);
      if (arg2[0])
	rsp_size = atoi(arg2);
      break;
    case 's':
      /* set the 802.2 sap for the test */
      dlpi_sap = atoi(optarg);
      break;
    case 'w':
      /* set local window sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	lsw_size = atoi(arg1);
      if (arg2[0])
	lrw_size = atoi(arg2);
      break;
    case 'W':
      /* set remote window sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	rsw_size = atoi(arg1);
      if (arg2[0])
	rrw_size = atoi(arg2);
      break;
    };
  }
}


#endif /* WANT_DLPI */
