
/* Program to test OSF packetfilter as used by Symbolics OpenGenera.
 * 1995, Mark Nahabedian
 * Artificial Intelligence Laboratory
 * Massachusetts Institute of Technology
 */

#include <sys/types.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <cma.h>
/* #include <net/socket.h> */	/* header file doesn't exist */
#include <net/pfilt.h>
#include <net/if.h>
#include <sys/timers.h>

#define SSWAB(x)	(((x & 0x00ff) << 8) | ((x & 0xff00) >> 8))

#define IP_ADDRESS(a,b,c,d)	((a)|(b<<8)|(c<<16)|(d<<24))

int my_internet_address =	IP_ADDRESS(128, 52, 33, 16);
int wilson_address = 		IP_ADDRESS(128, 52, 33, 12);
int fdr_address = 		IP_ADDRESS(128, 52, 39, 16);
int psi_address =		IP_ADDRESS(128, 52, 33, 20);
int clinton_address = 		IP_ADDRESS(128, 52, 33, 14);
int gateway_address = 		IP_ADDRESS(128, 52, 33, 1);
int life_address = 		IP_ADDRESS(128, 52, 32, 80);
int etna_address = 		IP_ADDRESS(128, 52, 33, 19);

char zero_address[] = { '\0', '\0', '\0', '\0', '\0', '\0' };

#define PF_QUEUE_SIZE	(32)

int pf_fd;	/* file descriptor of opened packet filter device */

/* Interface parameters as returned by ioctl EIOCDEVP */
struct endevp interface_parameters;

#define MAX_PACKET_LENGTH	(interface_parameters.end_MTU)

short *inbuffer, *outbuffer;

int transmit_count = 0;
int receive_count = 0;

int command_count = 0;

/* Skip the first this many bytes of the output buffer, I don't know why. */
#define OBSLOP	(0)

char *progname;	/* the name of this program as appears in argv[0] */

/* Flags controling what information to show */
#define SHOW_ETHER_HEADERS	(1)
#define SHOW_ARP_PACKETS	(1<<1)
#define SHOW_IP			(1<<2)
#define SHOW_TRANSMITTED	(1<<3)

int showflags = SHOW_ETHER_HEADERS | SHOW_TRANSMITTED;

int show_count = 16;


/************************************************************
 * Routine for system call error reporting
 */

typedef enum { fatal_err, ignore_err }  error_type;

void
report_error(where, et)
char *where;
error_type et;
{
  fprintf(stderr, "%s:  %s:  %s\n",
	  progname, where, sys_errlist[errno]);
  if(et == fatal_err)
    exit (-1);
}


/************************************************************
 * Packet Structures
 */

/* Ethernet */

typedef union {			/* Ethernet address */
  u_char bytes[6];
  u_short shorts[3];
} ether_address;

typedef enum {			/* Ethernet protocol type */
  et_chaos =	0x0408,		/* SSWAB(0x0804) */
  et_arp =	0x0608,		/* SSWAP(0x0806) */
  et_symb1 =	0x1C08,		/* SSWAB(0x081C) */
  et_slap =	0x0781,		/* SSWAB(0x8107) */
  et_rarp =	0x3580,		/* SSWAB(0x8035) */
  et_AppleTalk = 0x9B80,	/* SSWAB(0x809B) */
  et_ip =	0x0008,		/* SSWAB(0x0800) */
  et_dec_lat =	0x0460
  } ether_type;

typedef struct 			/* Ethernet header */
{
  ether_address e_to_addr;
  ether_address e_from_addr;
  short e_type;
  char e_data0;		/* first data byte */
} ether_header;

#define ETHER_HEADER_SIZE	(14)

/* Internet Protocol */

typedef struct			/* Internet Packet */
{
  u_char ip_vers_hlen;
  u_char ip_service;
  u_short ip_length;
  u_short ip_id;
  u_short ip_flag_frag;
  u_char ip_ttl;
  u_char ip_protocol;
  u_short ip_hcheck;
  int ip_source_addr;
  int ip_dest_addr;
  int ip_options;
  char ip_data0;  
} ip_pkt;


/************************************************************
 * Checking packet structures
 */

/* Make sure structure offsets in Ethernet header 
 * are correct.
 */
void
check_ether_header()
{
  ether_header foo;

  printf("%d %d %d %d\n",
	 ((int) &(foo.e_to_addr)) - ((int) &foo),
	 ((int) &(foo.e_from_addr)) - ((int) &foo),
	 ((int) &(foo.e_type)) - ((int) &foo),
	 ((int) &(foo.e_data0)) - ((int) &foo) );
}


/************************************************************
 * displaying addresses
 */

show_bytes(address, count, format, delimiter)
u_char *address;
int count;
char *format;
char *delimiter;
{
  u_char *p, *end;

  p = address;
  end = address + count;

  for(; p < end; p++) {
    if(p != address)
      fputs(delimiter, stdout);
    printf(format, *p);
  }
}

/* display a hardware address */
void
print_hardware_address(ea)
ether_address *ea;
{
  show_bytes((char *) ea, interface_parameters.end_addr_len, "%02x", ":");
}


/************************************************************
 * Transmitting packets
 */

void
transmit(data, nbytes, why)
char *data;
int nbytes;
char *why;
{
  ssize_t w;

  if(showflags & SHOW_TRANSMITTED) {
    printf("XMIT %s ", why);
    show_bytes(data, 
               (nbytes<show_count) ? nbytes
	                           : show_count,
               "%02x", " ");
    putchar('\n');
  }

  w = write(pf_fd, data, nbytes);
  if(w != nbytes)
    printf("Tried to write %d but wrote %d\n", nbytes, w);
  else if(w < 0)
    report_error(why, fatal_err);
  transmit_count++;
}


/************************************************************
 * displaying packets
 */

/* show_arp() is defined above in the ARP code */

void
show_ether(eh, what)
ether_header *eh;
char *what;
{
  if(!(showflags & SHOW_ETHER_HEADERS))
    return;
  printf("%s  x%04x  ", what, eh->e_type);
  putchar('[');
  print_hardware_address(&eh->e_from_addr);
  printf("]->[");
  print_hardware_address(&eh->e_to_addr);
  putchar(']');
  putchar('\n');
}


void
show_ip(ip)
ip_pkt *ip;
{
  if(!(showflags & SHOW_IP))
    return;
  printf("IP %x %x %02x %04x %x %02x  ",
	 ip->ip_vers_hlen & 0xff,
	 (ip->ip_vers_hlen >> 4) & 0xff,	 
	 ip->ip_service,
	 ip->ip_length,
	 ip->ip_ttl,
	 ip->ip_protocol);
  show_bytes(&(ip->ip_source_addr), 4, "%d", ".");
  printf(" -> ");
  show_bytes(&(ip->ip_dest_addr), 4, "%d", ".");
  putchar('\n');
}


/************************************************************
 * Address resolution protocol
 */

typedef enum {			/* ARP packet opcodes */
  arp_req =	0x0100,		/* SSWAB(0x0001) */
  arp_rep = 	0x0200,		/* SSWAB(0x0002) */
  arp_rev_req = 0x0300,
  arp_rev_rep = 0x0400,
  arp_d_rev_req = 0x0500,
  arp_d_rev_rep = 0x0600,
  arp_d_rev_err = 0x0700,
  arp_inv_req = 0x0800,
  arp_inv_rep = 0x0900
  } arp_op;

typedef enum {			/* ARP hardware types */
  arp_ethernet = 0x0100		/* SSWAP(0x0001) */
  } arp_hardware;

typedef struct			/* ARP packet */
{
  u_short arp_hardware;
  u_short arp_protocol;
  u_char arp_hlength;
  u_char arp_plength;
  u_short arp_op;
  char arp_addrs;		/* addresses start here */
  /* sender hardware address */
  /* sender protocol address */
  /* target hardware address */
  /* target protocol address */
} arp_pkt;

#define ARP_HEADER_SIZE		(8)

/* Arguments are references to pointers.  The pointers are set to point to the 
 * starts of the corresponding address fields of the packet.
 */
void
arp_addresses(arp, sender_h_p, sender_p_p, target_h_p, target_p_p)
arp_pkt *arp;
char **sender_h_p, **sender_p_p, **target_h_p, **target_p_p;
{
  u_short hlen, plen;

  hlen = arp->arp_hlength;
  plen = arp->arp_plength;

  *sender_h_p = &(arp->arp_addrs);
  *sender_p_p = *sender_h_p + hlen;
  *target_h_p = *sender_p_p + plen;
  *target_p_p = *target_h_p + hlen;
}

void
show_arp(arp)
  arp_pkt *arp;
{
  u_short hlen, plen;
  char *sender_h, *sender_p, *target_h, *target_p;

  if(!(showflags & SHOW_ARP_PACKETS))
    return;

  hlen = arp->arp_hlength;
  plen = arp->arp_plength;
  arp_addresses(arp, &sender_h, &sender_p, &target_h, &target_p);

  printf("ARP h %d: %db;  p %d: %db;  op %d\n",
	 arp->arp_hardware, hlen,
	 arp->arp_protocol, plen,
	 arp->arp_op);

  /* sender hardware address */
  show_bytes(sender_h, hlen, "%02x", ":");
  putchar(' ');

  /* sender protocol address */
  show_bytes(sender_p, plen, "%d", ".");
  putchar(' ');

  /* target hardware address */
  show_bytes(target_h, hlen, "%02x", ":");
  putchar(' ');

  /* target protocol address */
  show_bytes(target_p, plen, "%d", ".");

  putchar('\n');
  fflush(NULL);
}


#define ARP_TABLE_SIZE (20)

typedef struct {
  ether_address arp_ea;
  int arp_ia;
  time_t arp_timestamp;
} arp_entry;

arp_entry arp_table[ARP_TABLE_SIZE];


void
show_arp_table()
{
  int i;

  for(i=0; i<ARP_TABLE_SIZE; i++) {
    if(arp_table[i].arp_timestamp != 0) {
      printf("   arp entry %d @%d  ", i,
	     (arp_table[i].arp_timestamp) % (60 * 60 * 24));
      show_bytes(&(arp_table[i].arp_ea), 6, "%02x", ":");
      printf(" <-> ");
      show_bytes(&(arp_table[i].arp_ia), 4, "%d", ".");
      putchar('\n');
    }
  }
  putchar('\n');
}


/* Searches the address resolution table for an entry matching the specified
 * internet address.  If found, a pointer to that entry is returned.  Otherwise
 * returns NULL.
 */
arp_entry *
arp_table_find_i(ip_address)
u_int ip_address;
{
  int i;

  for(i=0; i<ARP_TABLE_SIZE; i++) {
    if(ip_address == arp_table[i].arp_ia)
      return(&arp_table[i]);
  }
  return((arp_entry *) NULL);
}

arp_entry *
arp_table_find_e(ea)
ether_address *ea;
{
  int i;

  for(i=0; i<ARP_TABLE_SIZE; i++) {
    /* is == the right test? */
    if(0 == memcmp(ea, &arp_table[i].arp_ea, 6))
      return(&arp_table[i]);
  }
  return((arp_entry *) NULL);
}

arp_entry *
arp_table_find_empty()
{
  int i, oldest = 0;

  for(i=0; i<ARP_TABLE_SIZE; i++)
    if(arp_table[i].arp_timestamp == 0)
      return(&arp_table[i]);
    else if(arp_table[i].arp_timestamp < arp_table[oldest].arp_timestamp)
      oldest = i;
  return(&arp_table[oldest]);
}


/* arguments are pointers to ethernet and internet address pair for a host */
void
make_arp_entry(now, ea, ia)
time_t now;
char *ea;
char *ia;
{
  arp_entry *entry;

  entry = arp_table_find_i(* (u_int *) ia);

  if(entry == (arp_entry *)NULL) {
    entry = arp_table_find_empty();
    entry->arp_ia = * (u_int *) ia;
  }

  memcpy((char *) &(entry->arp_ea), ea, 6);
  entry->arp_timestamp = now;
}


void
arp_table_clear()
{
  int i;

  for(i=0; i<ARP_TABLE_SIZE; i++) {
    arp_table[i].arp_timestamp = 0;
    arp_table[i].arp_ia = 0;
    arp_table[i].arp_ea.shorts[0] = 0;
    arp_table[i].arp_ea.shorts[1] = 0;
    arp_table[i].arp_ea.shorts[2] = 0;
  }

  make_arp_entry(MAX_SECONDS, 
		 &(interface_parameters.end_addr[0]),
		 &my_internet_address);
}


/* Fill in an ARP packet.  Arguments are ethernet packet, 
 * ARP opcode and target hardware and protocol addresses.
 * Returns the total size of the Ethernet packet.
 */
int
arp_format(pkt, op, t_h, t_p)
ether_header *pkt;
arp_op op;
char *t_h, *t_p;
{
  arp_pkt *arp;
  char *sender_h, *sender_p, *target_h, *target_p;

  arp = (arp_pkt *) &(pkt->e_data0);
  arp->arp_hardware = arp_ethernet;
  arp->arp_hlength = 6;
  arp->arp_protocol = et_ip;
  arp->arp_plength = 4;
  arp->arp_op = op;

  arp_addresses(arp, &sender_h, &sender_p, &target_h, &target_p);
  
  /* Fill in arp packet */
  memcpy(sender_h, &(interface_parameters.end_addr[0]), arp->arp_hlength);
  memcpy(sender_p, &my_internet_address, arp->arp_plength);
  memcpy(target_h, t_h, arp->arp_hlength);
  memcpy(target_p, t_p, arp->arp_plength);
  arp->arp_op = op;

  /* Fill in Ethernet header */
  pkt->e_type = (u_short) et_arp;
  memcpy(&(pkt->e_from_addr.bytes[0]), sender_h, arp->arp_hlength);
  /* If target hardware address is unknown (zero) use the broadcast address */
  if(0 == memcmp(zero_address, target_h, arp->arp_hlength))
    memcpy(&(pkt->e_to_addr.bytes[0]),
	   &(interface_parameters.end_broadaddr[0]),
	   arp->arp_hlength);
  else
    memcpy(&(pkt->e_to_addr.bytes[0]), target_h, arp->arp_hlength);

  return(ETHER_HEADER_SIZE + ARP_HEADER_SIZE 
          + 2 * (arp->arp_hlength + arp->arp_plength));
}

void
arp_receive(pkt) 
ether_header *pkt;
{
  arp_pkt *arp;
  char *sender_h, *sender_p, *target_h, *target_p;
  timespec_t now;
  arp_entry *sender_entry;
  int i, w;

  getclock(TIMEOFDAY, &now);

  arp = (arp_pkt *) &(pkt->e_data0);

  if(! ((6 == arp->arp_hlength) &&
	(4 == arp->arp_plength) &&
	(arp_ethernet == arp->arp_hardware) &&
	(et_ip == arp->arp_protocol)) ) {
    printf("Bad ARP packet\n");
    return;
  }
  arp_addresses(arp, &sender_h, &sender_p, &target_h, &target_p);

  /* If the sender is already in our table, force update. */
  sender_entry = arp_table_find_i(* (u_int *) sender_p);
  if(sender_entry != NULL) {
    memcpy((char *) &(sender_entry->arp_ea), sender_h, 6);
    sender_entry->arp_timestamp = now.tv_sec;
  }

  /* Is it targeted at us? */
  if(0 != memcmp(target_p, (char *)&my_internet_address, 4))
    return;

  if(sender_entry == NULL)
    make_arp_entry(now.tv_sec, sender_h, sender_p);

  switch(arp->arp_op) {
    case arp_rep:	/* processed by above */
      break;
    case arp_req:
      i = arp_format(((char *)outbuffer)+OBSLOP, arp_rep, sender_h, sender_p) + OBSLOP;
      transmit((char *)outbuffer, i, "ARP reply");
      break;
    default:
      break;
  }
}

void
arp_request(ip_address)
int ip_address;
{
  int size, w;
  char *ob;

  ob = ((char *) outbuffer) + OBSLOP;

  size = arp_format(ob, arp_req, zero_address, &ip_address) + OBSLOP;

  if(showflags & SHOW_ARP_PACKETS) {
    printf("Requesting  ");
    show_ether(ob, "");
    show_arp(&(((ether_header *)ob)->e_data0));
  }
  transmit(outbuffer, size, "ARP request");
}


/************************************************************
 * Packet Filter stuff
 */

/* open packet filter */
void
openpf()
{
  pf_fd = pfopen(NULL, O_RDWR);
  if(pf_fd < 0)
    report_error("opening packetfilter", fatal_err);
}

/* get information about the network and interface */
void
get_net_info(fd)
int fd;
{
  if(-1 == ioctl(fd, EIOCDEVP, &interface_parameters)) 
    report_error("getting info", fatal_err);
  
  switch (interface_parameters.end_dev_type) {
  case ENDT_10MB:
    printf("Device is 10Mb Ethernet\n");
    break;
  case ENDT_FDDI:
    printf("Device is FDDI\n");
    break;
  default:
    printf("Unknown device type %d\n", interface_parameters.end_dev_type);
    break;
  }

  printf("MTU size = %d.\n", interface_parameters.end_MTU);
  printf("Hardware address:   ");
  print_hardware_address(&(interface_parameters.end_addr[0]));
  putchar('\n');
  printf("Broadcast address:  ");
  print_hardware_address(&(interface_parameters.end_broadaddr[0]));
  putchar('\n');
  putchar('\n');
}

/* initialize packet filter mode flags, etc. */
void
pfinitmode()
{
  u_short good_bits = ENCOPYALL | ENNONEXCL | ENHOLDSIG;
  u_short bad_bits = ENTSTAMP | ENBATCH | ENPROMISC | ENBPFHDR;

  /* set some flags */
  if(-1 == ioctl(pf_fd, EIOCMBIS, &good_bits))
    report_error("seting mode bits", fatal_err);

  /* clear some */
  if(-1 == ioctl(pf_fd, EIOCMBIC, &bad_bits))
    report_error("clearing mode bits", fatal_err);
}

/* set packet filter queue size */
void
pfinitqueue()
{
  u_int pfq = PF_QUEUE_SIZE;
  if(-1 == ioctl(pf_fd, EIOCSETW, &pfq))
    report_error("setting queue size", fatal_err);
}

/* flush queued packets */
void
pfflush()
{
  if(-1 == ioctl(pf_fd, EIOCFLUSH, 0))
    report_error("flushing queued packets", fatal_err);
}


/************************************************************
 * program packet filter
 */

#define PFINST	f.enf_Filter[f.enf_FilterLen++] = 

void
pfprog()
{
  static struct enfilter f;

  f.enf_Priority = 255;
  f.enf_FilterLen = 0;

  PFINST ENF_PUSHWORD+6;
  PFINST ENF_PUSHLIT;
  PFINST 010;
  PFINST ENF_NEQ;
  PFINST ENF_PUSHWORD+15;
  PFINST ENF_PUSHLIT;
  PFINST (128 | (52<<8));
  PFINST ENF_NEQ;
  PFINST ENF_PUSHWORD+16;
  PFINST ENF_PUSHLIT;
  PFINST (33 | (19<<8));
  PFINST ENF_NEQ;
  PFINST ENF_OR;
  PFINST ENF_OR;

  if(-1 == ioctl(pf_fd, EIOCSETF, &f))
    report_error("setting packet filter program", fatal_err);
}

#undef PFINST

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

get_packet()
{
  ssize_t nbytes;

  /* read packet */
  nbytes = read(pf_fd, inbuffer, MAX_PACKET_LENGTH);
  if(nbytes < 0)
    report_error("reading packet", fatal_err);
  receive_count++;
}

int
packet_available_p()
{
  int nbytes;

  if(-1 == ioctl(pf_fd, FIONREAD, &nbytes))
    report_error("pf_fd FIONREAD", fatal_err);
  if(nbytes <= 0)
    return(1==0);
  else
    return(1==1);
}

void process_incomming()
{
  ether_header *eh;

  eh = (ether_header *) inbuffer;
  show_ether(eh, "in");

  switch(eh->e_type) {
  case et_arp:
    show_arp((arp_pkt *) &(eh->e_data0));
    arp_receive(eh);
    break;
  case et_rarp:
    show_arp((arp_pkt *) &(eh->e_data0));
    break;
  case et_ip:
    show_ip((ip_pkt *) &(eh->e_data0));
    break;
  case et_dec_lat:
    /* ignore LAT packets, whatever they are */
    break;
  default:
    printf("unknown  %04x  ", (u_short) eh->e_type);
    show_bytes( (char *) &(eh->e_data0), show_count, "%02x", " ");
    putchar('\n');
    break;
  }
  fflush(NULL);
}

void
time_get_packet()
{
  timespec_t before, after;

  getclock(TIMEOFDAY, &before);
  get_packet();
  getclock(TIMEOFDAY, &after);
  printf("Getting packet took %d seconds, %ld nanoseconds\n",
	 after.tv_sec - before.tv_sec,
	 after.tv_nsec - before.tv_nsec);
  process_incomming();
}


/************************************************************
 * make a bunch of ARP requests
 */

void
com_arp_request(com)
char *com;
{
  arp_request(etna_address);
  arp_request(wilson_address);
  arp_request(fdr_address);
  arp_request(psi_address);
  arp_request(clinton_address);
  arp_request(gateway_address);
  arp_request(life_address);  
}

/************************************************************
 * show values of various counters
 */

void
com_show_counters()
{
  printf("\nCommands: %d;  Received packets: %d; Transmitted packets %d\n",
         command_count, receive_count, transmit_count);
}

/************************************************************
 * verify header structure offsets
 */

void
com_verify_structures(com)
char *com;
{
  com;		/* ignore */

  check_ether_header();
}

/************************************************************
 * data transmission test, sending a junk packet
 */

void
com_xmit_junk(com)
char *com;
{
  int i, data_length = 50;
  int how_many = 1;
  ether_header *pkt = (ether_header *)outbuffer;
  char *c;

  pkt->e_to_addr.shorts[0] = 0xff;
  pkt->e_to_addr.shorts[1] = 0xee;
  pkt->e_to_addr.shorts[2] = 0xdd;

  pkt->e_to_addr.shorts[0] = 0xcc;
  pkt->e_to_addr.shorts[1] = 0xbb;
  pkt->e_to_addr.shorts[2] = 0xaa;

  pkt->e_type = 0xabcd;

  for(c=com+1; *c != '\0'; c++) {
    switch(*c) 
      {
      case '?':
	printf("  ?        this list\n");
	printf("  l<n>     specify length of data, <n> in decimal\n");
	printf("  t<n>     ethernet protocol type in hexadecimal\n");
	printf("  n<n>     how many to send in decimal\n");
	break;
      case 'l':
	data_length = strtol(c+1, &c, 10);
	break;
      case 't':
	pkt->e_type = 0xffff & strtol(c+1, &c, 16);
	break;
      case 'n':
	how_many = strtol(c+1, &c, 10);
	break;
      default:  /* ignore anything else except to delimit args */
	break;
      }
  }

  for(i=0; i < data_length; i++)
    (&(pkt->e_data0))[i] =i;
  
  printf("\nJunk packet type %04x, %d data bytes, %d times\n",
         (u_short) pkt->e_type, data_length, how_many);
  show_bytes((char *) outbuffer, data_length + ETHER_HEADER_SIZE, 
             "%02x", " ");
  putchar('\n');

  for(i=0; i<how_many; i++)
    transmit(outbuffer, data_length + ETHER_HEADER_SIZE,
	     "junk packet");
}


/************************************************************
 * flags for what's shown
 */

typedef struct {
  char shw_char;
  int shw_bits;
  char *shw_doc;
} show_entry;

show_entry show_entries[] =
{
  { 'a', SHOW_ARP_PACKETS, "ARP packets" },
  { 'e', SHOW_ETHER_HEADERS, "Ethernet headers" },
  { 'i', SHOW_IP, "IP headers and some data" },
  { 't', SHOW_TRANSMITTED, "transmitted packets" }, 
  /* last entry */
  { '\0', 0, "" }
};

show_show_entry(se)
show_entry *se;
{
  printf("%s  %c  %s\n",
         (showflags & se->shw_bits) ? "[on] " : "[off]",
         se->shw_char,
         se->shw_doc);
}

void
com_show_flags(com)
char *com;
{
  char *cp = com;
  show_entry *se;

  for(cp = com ;; cp++) {
    switch(*cp) {
    case '\0':
    case '\n':
      goto done;
      break;
    case '?':
      printf("\nUpper case letters enable showing, lower case letters disable.\n");
      printf("Current value indicated in square brackets\n");
      for(se = show_entries; se->shw_bits != 0; se++)
        show_show_entry(se);
      break;
    case '+':
      for(se=show_entries; se->shw_bits != 0; se++)
	showflags |= se->shw_bits;
      printf("Showing everything\n");
      break;
    case '-':
      for(se=show_entries; se->shw_bits != 0; se++)
	showflags &= ~(se->shw_bits);
      printf("Showing nothing\n");
      break;
    default:
      for(se=show_entries; se->shw_bits != 0; se++) {
        if(tolower((*cp)) == tolower((se->shw_char))) {
          if(isupper(*cp))
            showflags |= se->shw_bits;
          else
            showflags &= ~(se->shw_bits);
          show_show_entry(se);
        }
      }
      break;
    }
  }

 done:
  putchar('\n');
}

/************************************************************
 * parameters command
 */

typedef struct {
  char param_char;
  int *param_var;
  int param_base;
  char *param_doc;
} param_entry;

param_entry param_entries[] =
{
  { 'd', &show_count, 10, "number of bytes to show in dump" },
  /* last entry */
  { '\0', NULL, 0, NULL }
};

void
show_param_entry(pe)
param_entry *pe;
{
  printf("  %c  ", pe->param_char);
  switch(pe->param_base) 
    {
    case 8:
      printf("%10oo", *(pe->param_var));
      break;
    case 16:
      printf("  %8xx", *(pe->param_var));
      break;
    case 10:
      /* drop through */
    default:
      printf("  %8d.", *(pe->param_var));
      break;
    }
  printf("  %s\n", pe->param_doc);
}

void
com_parameters(com)
char *com;
{
  char *c;
  param_entry *pe;

  for(c=com+1; *c != '\0'; c++) {
    switch(*c) 
      {
      case '?':
	printf("Parameters.  Current values indicated in square brackets\n");
	for(pe = param_entries; pe->param_char != '\0'; pe++)
	  show_param_entry(pe);
	break;
      default:
	for(pe=param_entries; pe->param_char != '\0'; pe++) {
	  if(*c == pe->param_char) {
	  *(pe->param_var) = strtol(c+1, &c, pe->param_base);
          show_param_entry(pe);
	}
        }
	break;
      }
  }
}



/************************************************************
 * Command dispatch
 */

typedef struct {
  char com_char;
  void (*com_fun)();
  char *com_desc;
} command_entry;

void show_commands();

command_entry command_table[] =
{
  { '?', show_commands, "this listing" },
  { 'a', show_arp_table, "Show ARP Table" },
  { 'A', com_arp_request, "Send ARP requests" },
  { 'c', com_show_counters, "Show counters" },
  { 'j', com_xmit_junk, "transmit a junk packet" },
  { 'p', com_parameters, "set/examine parameters" },
  { 's', com_show_flags, "Control what information is shown, s? for help" },
  { 'v', com_verify_structures, "Verify structure offsets" },
  /* last entry */
  { '\0', NULL, NULL }
};

void
show_commands(com)
char *com;
{
  command_entry *ce;

  printf("\nMany commands have options or subcommands.  Enter a '?' after the\n");
  printf("command name for more information.\n");
  for(ce = command_table; ce->com_char != NULL; ce++) {
    printf("  %c   %s\n", ce->com_char, ce->com_desc);
  }
  printf("\n");
}

char *
read_command()
{
  static char com[100];
  int nbytes;

  if(-1 == ioctl(fileno(stdin), FIONREAD, &nbytes))
    report_error("tty FIONREAD", fatal_err);
  if(nbytes == 0)
    return(NULL);

  fgets(com, 100, stdin);
  return(com);
}

void
do_command(com)
char *com;
{
  command_entry *ce;

  for(ce = command_table; ce->com_char != NULL; ce++)
    if(com[0] == ce->com_char) {
      command_count++;
      (*(ce->com_fun))(com);
    }
}


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

main(argc, argv)
int argc;
char **argv;
{
  char *com;

  progname = argv[0];

  openpf();		/* open packet filter driver */
  get_net_info(pf_fd);

  inbuffer = (short *) malloc(MAX_PACKET_LENGTH);
  outbuffer = (short *) malloc(MAX_PACKET_LENGTH);
  if(inbuffer == NULL || outbuffer == NULL)
    report_error("allocating packet buffers", fatal_err);

  printf("My internet address is  ");
  show_bytes(&my_internet_address, 4, "%d", ".");
  putchar('\n');

  arp_table_clear();	/* initialize ARP table */
  pfprog();		/* set program */
  pfinitmode();		/* set flags */
  pfinitqueue();	/* set queue size */
  pfflush();		/* clear queue */

  /* uses FIONREAD as blocking regimen */

  time_get_packet();

  printf("Type '?' for command summary\n");

  /* any command line arguments are treated as commands */
  for(com = argv[1]; *com !=  NULL; com++)
    do_command(com);

  for(;;) {
    if(packet_available_p()) {
      get_packet();
      process_incomming();
    }
    com = read_command();
    if(com != NULL)
      do_command(com);
    fflush(NULL);
  }

  free(outbuffer);
  free(inbuffer);

}

