next up previous contents
Next: About this document Up: Implementing a High Performance Previous: A Terminology

B The Test Setup

 

This appendix describes the setup we used to perform our tests.

B.1 The Hardware

We used two PCs with Intel Pentium running at 133 MHz. They were connected with a 10 Mbit/s ethernet via a hub, but disconnected from all other hosts. Connected to the parallel port was an oscilloscope to measure the time spent in the stack.

The first test measuring the round-trip time was made with a PC running version 2.0.29 of the Linux kernel. The second test measuring the time spent in the stack was made on a PC running version 2.1.29 of the Linux kernel. The different kernel versions should however not affect the results in any major way.

B.2 Required Kernel Modification

The following two diffs have to be applied to the Linux kernel (v2.1.29) to make it raise and lower the signal on the parallel port when a packet arrives and leaves the stack. Both files are located in the net/ipv4/ subdirectory of the Linux kernel source tree.

B.2.1 net/ipv4/ip_input.c

diff -c ip_input.org.c ip_input.c
*** ip_input.org.c      Mon May 19 16:38:17 1997
--- ip_input.c  Mon May 19 16:38:29 1997
***************
*** 341,346 ****
--- 341,348 ----
        struct ip_options * opt = NULL;
        int err;
  
+   outb(240, 0x378);
+ 
  #ifdef CONFIG_NET_IPV6
        /* 
         *      Intercept IPv6 frames. We dump ST-II and invalid types just below..

B.2.2 net/ipv4/ip_output.c

diff -c ip_output.org.c ip_output.c 
*** ip_output.org.c     Mon May 19 16:39:34 1997
--- ip_output.c Tue Mar 18 15:38:23 1997
***************
*** 266,271 ****
--- 266,273 ----
            !(dev->flags&IFF_LOOPBACK))
                dev_loopback_xmit(skb);
  
+   outb(0, 0x378);
+ 
        if (dev->flags & IFF_UP) {
                dev_queue_xmit(skb);
                return 0;
***************
*** 294,299 ****
--- 296,303 ----
        if (rt->rt_flags&RTCF_NAT)
                ip_do_nat(skb);
  
+   outb(0, 0x378);
+ 
        if (dev->flags & IFF_UP) {
                dev_queue_xmit(skb);
                return 0;

B.3 The Software

B.3.1 UDPMirror

/* UDPMirror.c
 *
 * This program takes any incoming UDP packets,
 * and returns them to the sender.
 */

#include <getopt.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>

#define MAXBLKSIZE (64*1024)

struct        sockaddr_in sock_in;
char buffer[MAXBLKSIZE];
int f;
int port = 12345;

/* Long options.  */
static const struct option long_options[] =
{
  { "help", no_argument, NULL, 'h' },
  { NULL, 0, NULL, 0 }
};

void usage(void)
{
  fprintf(stderr, "Usage: udpmirror [options]\n");
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "-h\t\tthis information\n");
  fprintf(stderr, "-p nn\t\tuse port #nn instead of default %d\n", port);
  exit(1);
}

int main(int argc, char *argv[])
{
  int opt;
  char optchar;

  bzero(buffer, MAXBLKSIZE);

  while ((optchar = getopt_long(argc, argv, "hp:", long_options, NULL)) != EOF)
    switch (optchar) {
    case '\0':                     break;
    case 'p': port = atoi(optarg); break;
    case 'h':
    default: usage();
    }

  if((f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("udpmirror: socket");
    exit(1);
  }

  bzero((char *)&sock_in, sizeof(sock_in));
  sock_in.sin_family = AF_INET;
  sock_in.sin_port = htons(port);
  sock_in.sin_addr.s_addr = htonl(INADDR_ANY);
  if (bind(f, (struct sockaddr*)&sock_in, sizeof (sock_in)) < 0) {
    perror("udpmirror: bind");
    exit(1);
  }

  while(1) {
    int len, sock_len = sizeof(sock_in);
    if((len = recvfrom(f, buffer, MAXBLKSIZE, 0, (struct sockaddr *)&sock_in, &sock_len) > 0)) {
      sendto(f, buffer, strlen(buffer)+1, 0, (struct sockaddr *)&sock_in, sock_len);
    }
  }

  return(0);
}

B.3.2 UDPSend

/* UDPSend.c
 *
 * This program sends a number of UDP packets to a host,
 * waits for each packet to return, and measures the
 * time it takes.
 */

#include <getopt.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/time.h>

#define MAXBLKSIZE (64*1024)

struct  sockaddr_in sock_in, sock_out;
struct  hostent *host;
char buffer[MAXBLKSIZE];
char outbuffer[MAXBLKSIZE] = "Hej alla småkottar";
int f;
int port = 12345;

unsigned long starts, startms, stops, stopms, expms;
struct timeval ti, tis; 
struct timezone tiz;

/* Long options. */
static const struct option long_options[] =
{
  { "help", no_argument, NULL, 'h' },
  { NULL, 0, NULL, 0 }
};

void usage(void)
{
  fprintf(stderr, "Usage: udpsend [options] destination\n");
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "-b nnn\t\tbuffer size, default %d bytes\n", strlen(outbuffer));
  fprintf(stderr, "-h\t\tthis information\n");
  fprintf(stderr, "-n nn\t\tnumber of blocks to send (default is infinite)\n");
  fprintf(stderr, "-p nn\t\tuse port #nn instead of default %d\n", port);
  fprintf(stderr, "destination\thost name or address\n");
  exit(1);
}

int main(int argc, char *argv[])
{
  int opt;
  char optchar;
  int nblocks = -1, bufsize = strlen(outbuffer), len, sock_len = sizeof(sock_in);
  int i, c;

  bzero(buffer, MAXBLKSIZE);

  while ((optchar = getopt_long(argc, argv, "b:hn:p:", long_options, NULL)) != EOF)
    switch (optchar) {
    case '\0':                                  break;
    case 'b':
      bufsize = atoi(optarg);
      bufsize = (bufsize > 18 ? bufsize : 18);
      bufsize = (bufsize < 65536 ? bufsize : 65536);
      break;
    case 'n': nblocks = atoi(optarg);           break;
    case 'p': port = atoi(optarg);              break;
    case 'h':
    default: usage();
    }

  /* after options processing we need two args left */
  if (argc - optind != 1) {
    printf("Need a hostname.\n");
    usage();
  }

  if((f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("udpsend: socket");
    exit(1);
  }

  bzero((char *)&sock_in, sizeof(sock_in));
  sock_in.sin_family = AF_INET;
  if (bind(f, (struct sockaddr*)&sock_in, sizeof (sock_in)) < 0) {
    perror("udpsend: bind");
    exit(1);
  }

  host = gethostbyname(argv[optind]);
  if (host) {
    sock_out.sin_family = host->h_addrtype;
    bcopy(host->h_addr, &sock_out.sin_addr, host->h_length);
  }
  else {
    sock_out.sin_family = AF_INET;
    sock_out.sin_addr.s_addr = inet_addr(argv[optind]);
    if (sock_out.sin_addr.s_addr == -1) {
      fprintf(stderr, "udpsend: %s unknown host\n", argv[optind]);
      exit(1);
    }
  }
  sock_out.sin_port = htons(port);

  for(c = '0', i = 0; i < bufsize-1; i++) {
    outbuffer[i] = c++;
    if(c > 'z')
      c = '0';
  }
  outbuffer[i] = '\0';

  if (gettimeofday(&ti, &tiz) < 0) {
    perror("udpsend: time");
    exit(1);
  }

  while(nblocks--) {
    sendto(f, outbuffer, bufsize, 0, (struct sockaddr *)&sock_out, sock_len);
    recvfrom(f, buffer, MAXBLKSIZE, 0, (struct sockaddr *)&sock_in, &sock_len);
  }

  if (gettimeofday(&tis, &tiz) < 0) {
    perror("udpsend: time");
    exit(1);
  }

  starts  = ti.tv_sec;
  startms = ti.tv_usec / 1000L;
  stops  = tis.tv_sec;
  stopms = tis.tv_usec / 1000L;

  expms = (stops-starts)*1000 + (stopms-startms);

  printf("Time: %lds, %ldms\n", (int)(expms/1000), (int)(expms%1000));

  return(0);
}

B.4 Running the Tests

These are the instruction on how to run the two different tests we performed.

Start by stopping as many processes running on the computer which protocol is to be tested, especially X and other CPU hungry processes. Then start the UDPMirror program on it. Then run the UDPSend program on the other computer.

When measuring the round-trip time, use the -n option to the UDPSend program to specify the number of blocks to send. When measuring the time spent in the stack, no options should be required as this will make UDPSend run forever.

When running our stack the UDPMirror does not (and should not) need to be started since our stack does the mirroring internally.


next up previous contents
Next: About this document Up: Implementing a High Performance Previous: A Terminology

Peter Kjellerstedt
Thu Jun 5 00:52:23 MET DST 1997