/*  statnetd.c is part of Statnet */
/* Statnet is protected under the GNU Public License (GPL2). */
/* Author: Jeroen Baekelandt (jeroenb@igwe.vub.ac.be)       */
/* 13DEC98: Scot E. Wilcoxon (sewilco@fieldday.mn.org)     */

/* This is the main program for the monitoring daemon */
#define MAIN_LINE 1

/* #include <fcntlbits.h> */
#include <fcntl.h>
/* #include <ioctls.h> */
#include <sys/ioctl.h>

#include <signal.h>
#include <stdio.h>
#include <sys/utsname.h>

#include <sys/ipc.h>
#ifdef IPC_DIPC
#include <linux/dipc.h>
#else
/* Always define so all compilations can run distributed. */
#define IPC_DIPC 00010000	/* make it distributed */
#ifndef DIPC_SIG_READER
#define DIPC_SIG_READER SIGURG	/* for compilation on non-DIPC systems */
#endif
#ifndef DIPC_SIG_WRITER
#define DIPC_SIG_WRITER SIGPWR	/* for compilation on non-DIPC systems */
#endif
#endif

#include <sys/shm.h>

#define Stringify(X)	#X
#define Quoteify(X)	Stringify(X)

#if !(defined(__GLIBC__) && __GLIBC__ >= 2)
#include <linux/if_ether.h>
#include <linux/if.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#else
#include <netinet/if_ether.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#endif
#include <errno.h>
#include <values.h>
#include "stat.h"

#ifdef _LINUX_IF_ETHER_H
#undef NET_3			/* not using Net/3 definitions */
#endif

#ifndef ETH_HLEN
#define ETH_HLEN 14		/* Ethernet header length */
#endif

#ifndef ETH_P_ATALK
#define ETH_P_ATALK 0x809B	/* Appletalk */
#endif

#ifndef SOCK_PACKET
#define SOCK_PACKET SOCK_RAW	/* If SOCK_PACKET is wrong, try SOCK_RAW */
#endif

#ifndef NET_3
#define ether_head	ethhdr	/* ether_head structure */
#define ether_type	h_proto	/* Ethernet Packet Type ID field */
#define ip		iphdr	/* IP header structure */
#define ip_off		frag_off	/* IP protocol field */
#define ip_p		protocol	/* IP protocol field */
#define ip_hl		ihl	/* IP header length field */
#define th_sport	source	/* TCP source port field */
#define th_dport	dest	/* TCP destination port field */
#define ui_sport	source	/* UDP source port field */
#define ui_dport	dest	/* UDP destination port field */
#endif

#if 1
#define SN_RCV_BUF_SIZE	1600
#else
#define SN_RCV_BUF_SIZE	(sizeof(struct ether_head)+ \
				 sizeof(struct ip)+ \
				 sizeof(struct options)+ \
				 sizeof(struct tcphdr))
					/* above was 1600, but kernel discards */
					/* excess and tells us full length,   */
					/* so we only need enough to analyze. */
/* Removed the above precision because Linux 1.1.* delivered only the size */
/* requested, but by Linux 2.0.* this optimization no longer worked. */
#endif

struct at_frame_type
  {
    unsigned char filler[20];
#if !(defined(__GLIBC__) && __GLIBC__ >= 2)
    __u16 appletalk_type1;	/* what is formal name? */
    unsigned char filler2[12];
    __u8 appletalk_type2;	/* what is formal name? */
#else
    u_int16_t appletalk_type1;	/* what is formal name? */
    unsigned char filler2[12];
    u_int8_t appletalk_type2;	/* what is formal name? */
#endif
  };				/* Ethernet Appletalk */

void handle_frame (unsigned char *buf, int length, struct sockaddr *saddr);
void handle_ip (struct ip *buf, int length);
void signal_handler (int signal_number);

int shutdown_flag = 0;
struct StatMemStruct *StatMem;	/* pointer to shared memory segment */

main (int argc, char *argv[])
{
  int daemon_option = 0;
  int length;
  int sd;
  int shmid;			/* shared memory ID */
  int sizeaddr;
  char interface_name[STATNETD_IFACE_LEN+1];	/* name of interface */
  char server_label[STATNETD_NAME_LEN+1];	/* name of this server */
  struct ifreq ifr, oldifr;
  struct sockaddr saddr;
  struct utsname utsbuf;
  unsigned char buf[SN_RCV_BUF_SIZE];
  key_t mem_key;
  extern char *optarg;			/* set by getopt(3) */
  extern int optind, opterr, optopt;	/* set by getopt(3) */

  mem_key = (key_t)STAT_IPC_KEY;
  memset( interface_name, 0, STATNETD_IFACE_LEN+1 );
  strcpy( interface_name, ETH);
  timer_flag = 0;

  memset( server_label, 0, STATNETD_NAME_LEN+1 );
  if( uname(&utsbuf) != 0 )
  {  /* if error getting system name */
  	strcpy( server_label, "unknown server" );
  }  /* if error getting system name */
  else
  {  /* else copy node name */
	  strncpy( server_label, utsbuf.nodename, STATNETD_NAME_LEN );
  }  /* else copy node name */

  if ( 
	    ( signal( SIGINT,	signal_handler ) == SIG_ERR ) ||
	    ( signal( SIGQUIT,	signal_handler ) == SIG_ERR ) ||
	    ( signal( SIGCHLD,	SIG_IGN ) == SIG_ERR ) ||
	    /* Ignore DIPC signals which this app does not care about */
	    ( signal( DIPC_SIG_READER, SIG_IGN ) == SIG_ERR ) ||
	    ( signal( DIPC_SIG_WRITER, SIG_IGN ) == SIG_ERR ) ||
	    ( signal( SIGUSR1,	signal_handler ) == SIG_ERR ) ||
	    ( signal( SIGUSR2,	signal_handler ) == SIG_ERR ) 
	)
    {
	perror ("Can't register signal handler");
	exit (5);
    }

    /* Default settings */
    options.g = 1;		/* General */
    options.ip_option = 1;	/* IP protocols */
    options.at_option = 1;	/* Appletalk activity */
    options.prot_option = 1;	/* Protocol activity */
    options.tcp_option = 1;	/* TCP/IP activity */
    options.udp_option = 1;	/* UDP/IP activity */
    options.sap_option = 1;	/* SAP activity */

    if( argc != 1 )
    {  /* if there are command-line arguments */
      int op;

      while ((op = getopt (argc, argv, "acdghik:n:pstuw:")) != EOF)
	{
	  switch (op)
	    {
	    case 'a':
	      options.at_option = 1;
	      break;
	    case 'c':
		/* clear options */
		options.g = 0;		/* General */
		options.ip_option = 0;	/* IP protocols */
		options.at_option = 0;	/* Appletalk activity */
		options.prot_option = 0;	/* Protocol activity */
		options.tcp_option = 0;	/* TCP/IP activity */
		options.udp_option = 0;	/* UDP/IP activity */
		options.sap_option = 0;	/* SAP activity */
		mem_key = (key_t)STAT_IPC_KEY;
		memset( interface_name, 0, STATNETD_IFACE_LEN+1 );
		strcpy( interface_name, ETH);

		memset( server_label, 0, STATNETD_NAME_LEN+1 );
		if( uname(&utsbuf) != 0 )
		{  /* if error getting system name */
		strcpy( server_label, "unknown server" );
		}  /* if error getting system name */
		else
		{  /* else copy node name */
		  strncpy( server_label, utsbuf.nodename, STATNETD_NAME_LEN );
	      	}  /* else copy node name */
	        break;
	    case 'd':
	      daemon_option = 1;
	      break;
	    case 'g':
	      options.g = 1;
	      break;
	    case 'i':
	      options.ip_option = 1;
	      break;
	    case 'k':
	      if( optarg == (char *)NULL) usage(argv[0]);
	      mem_key = (key_t)strtol(optarg, (char **)NULL, 0);
	      break;
	    case 'n':
	      if( optarg == (char *)NULL) usage(argv[0]);
	      memset( server_label, 0, STATNETD_NAME_LEN+1 );
	      strncpy( server_label, optarg, STATNETD_NAME_LEN );
	      break;
	    case 'p':
	      options.prot_option = 1;
	      break;
	    case 's':
	      options.sap_option = 1;
	      break;
	    case 't':
	      options.tcp_option = 1;
	      break;
	    case 'u':
	      options.udp_option = 1;
	      break;
	    case 'w':
	      if( optarg == (char *)NULL) usage(argv[0]);
	      memset( interface_name, 0, STATNETD_IFACE_LEN+1 );
	      strncpy( interface_name, optarg, STATNETD_IFACE_LEN );
	      break;
	    case 'h':
	    default:
	      usage (argv[0]);
	      exit(0);
	      break;
	    }
	}
    }  /* if there are command-line arguments */

  /* Initialize shared memory */
  if ((shmid = shmget(mem_key, sizeof(struct StatMemStruct), 
	  IPC_DIPC|IPC_CREAT|IPC_EXCL|0644)) == -1 )
  {  /* if it might already exist, use it */
	  if ((shmid = shmget(mem_key, sizeof(struct StatMemStruct), 
		  IPC_DIPC)) == -1 )
	  {  /* error, could not find shared memory segment */
		perror ("Can't get shared memory");
		exit (6);
	  }  /* error, could not find shared memory segment */

  }  /* if it might already exist, use it */
  if(( (char *)StatMem = (struct StatMemStruct *)shmat(shmid, 0, 0) ) == (char *)-1)
  { /* if error in attaching with write permission */
	perror ("Can't attach shared memory");
	exit (7);
  } /* if error in attaching with write permission */

  {	/* Compound statement to make initializers vanish after init. */

    memset (&SM_regis, 0, sizeof (struct registers));
    memset (StatMem, 0, sizeof (struct StatMemStruct));
    strncpy( StatMem->servername, server_label, STATNETD_NAME_LEN );

    tally_init ( &StatMem->prot, disabled,
	(unsigned int	*)0,
	-1, (int	*)0,
	-1, (char		*)0,
	StatMem->prot_types, StatMem->protocol_count, protocol_num,
	SN_NUM_PROTOCOLS-1, SN_MAX_PROTO_DESC, (char *)0);
    tally_init ( &StatMem->ip_types, disabled,
	StatMem->ip_protocol_count, 
	-1, (int	*)0,
	SN_MAX_IP_PORT, (char *)0,
	(unsigned int *)0, (unsigned int *)0, 0,
	0, 0, (char *)0);
    tally_init ( &StatMem->tcp_port, disabled,
	StatMem->tcp_port_ctr, 
	-1, (int	*)0,
	SN_MAX_TCP_PORTS, (char *)0,
	(unsigned int *)0, (unsigned int *)0, 0,
	0, 0, (char *)0);
    tally_init ( &StatMem->udp_port, disabled,
	StatMem->udp_port_ctr,
	-1, (int	*)0,
	SN_MAX_UDP_PORTS, (char *)0,
	(unsigned int *)0, (unsigned int *)0, 0,
	0, 0, (char *)0);
    tally_init ( &StatMem->sap_types, disabled,
	StatMem->sap_count, 
	-1, (int	*)0,
	SN_MAX_SAP, (char *)0,
	(unsigned int *)0, (unsigned int *)0, 0,
	0, 0, (char *)0);

	if( daemon_option )
	{  /* if should run in the background */
		int pid;
		int fileDesc = -1;

		close(0);	/* close stdin */
		close(1);	/* close stdout */
		close(2);	/* close stderr */
		if( (pid = fork()) < 0 )
		{  /* error, could not become a background task */
			exit(10);
		}  /* error, could not become a background task */
		if( pid != 0 ) exit(0);		/* end the original program */

		(void) setsid();	/* process group leader */

		if( (pid = fork()) < 0 )
		{  /* error, could not become a background task */
			exit(10);
		}  /* error, could not become a background task */
		if( pid != 0 ) exit(0);		/* end the original program */

		fileDesc = open( "/dev/null", O_RDWR);	/* stdin */
		(void) dup(fileDesc);	/* stdout */
		(void) dup(fileDesc);	/* stderr */
	}  /* if should run in the background */


    /* OPEN SOCKET */

    if ((sd = socket (AF_INET, SOCK_PACKET, htons (ETH_P_ALL))) < 0)
      {
	perror ("Can't get socket");
	exit (1);
      }

    /* SET PROMISC */

    strcpy (oldifr.ifr_name, interface_name);
    if (ioctl (sd, SIOCGIFFLAGS, &oldifr) < 0)
      {
	close (sd);
	perror ("Can't get flags");
	exit (2);
      }

    /* Should this be rewritten to cooperate with other net tools? */
    memcpy( &ifr, &oldifr, sizeof( struct ifreq ) );
    ifr.ifr_flags |= IFF_PROMISC;

    if (ioctl (sd, SIOCSIFFLAGS, &ifr) < 0)
      {
	close (sd);
	perror ("Can't set flags");
	exit (3);
      }

    StatMem->prot.type_code = options.prot_option ? protocol : disabled;
    StatMem->ip_types.type_code = options.ip_option ? ip_prot : disabled;
    StatMem->tcp_port.type_code = options.tcp_option ? tcp : disabled;
    StatMem->udp_port.type_code = options.udp_option ? udp : disabled;
    StatMem->sap_types.type_code = options.sap_option ? sap : disabled;

    SM_regis.errcode = 0;
    SM_regis.errcount = 0;

    SM_regis.rx_errors_d = 0;
    SM_regis.rx_dropped_d = 0;
    SM_regis.rx_packets_d = 0;
    SM_regis.rx_interval = 1;

#if 0
/* removed for statnet 3.* due to awkwardness in reading data. */
#if !( defined(__GLIBC__) && __GLIBC__ >= 2 )
    memset( &stat_buf1, 0, sizeof(struct enet_statistics) );
    memset( &stat_buf2, 0, sizeof(struct enet_statistics) );
#else
    memset( &stat_buf1, 0, sizeof(struct net_device_stats) );
    memset( &stat_buf2, 0, sizeof(struct net_device_stats) );
#endif
    last_stats = &stat_buf1;
    now_stats = &stat_buf2;
    if_getstats (interface_name, last_stats);        /* get starting interface statistics */
#endif

  }				/* Compound statement to make initializer variables vanish after init. */
  /* END OF INITIALISATION */

  set_null ( StatMem );			/* clear all variables */
  services();			/* default values for services */

  while (!shutdown_flag)
    {				/* while no shutdown requested */
      /* This is the main data-gathering loop; keep it small and fast */
      sizeaddr = sizeof(struct sockaddr);	/* size of saddr */
      length = recvfrom (sd, buf, SN_RCV_BUF_SIZE, 0, &saddr, &sizeaddr);

      if (length < 0)
      {  /* if length < 0 */
	if (errno != EINTR)
	  {			/* if error detected and error is not expected type */
	    SM_regis.errcode = errno;
	    SM_regis.errcount++;
	    continue;
	  }
	else
	  continue;
      }  /* if length < 0 */
      else
	handle_frame (buf, length, &saddr);

      if( !timer_flag )
      {  /* if timer not running */
        if (signal (SIGALRM, itstime) == SIG_ERR)
	{
	  SM_regis.errcode = errno;
	  SM_regis.errcount++;
	  timer_flag = 0;
	}
	else
	{  /* else signal registered */
	  alarm (SN_STATS_SECS);
	  timer_flag = 1;
	}  /* else signal registered */
      }  /* if timer not running */
					        
    }				/* while no shutdown requested */


  /* TERMINATE */

  /* Should this be rewritten to cooperate with other net tools? */
  if (ioctl (sd, SIOCSIFFLAGS, &oldifr) < 0)
    {
      close (sd);
      perror ("Can't unset flags");
      exit (4);
    }

  close (sd);
  shmdt( (char *)StatMem );	/* detach shared memory */
  exit (0);
}


void
exatalk (struct at_frame_type *buf, int length)
{
  /* Need some documentation of these definitions and the AT packet types */
  switch (buf->appletalk_type2)	/* Appletalk */
    {
    case 0x01:
      SM_regis.rtmprd++;
      break;
    case 0x02:
      SM_regis.nbp++;
      break;
    case 0x03:
      SM_regis.atp++;
      break;
    case 0x04:
      SM_regis.aep++;
      break;
    case 0x05:
      SM_regis.rtmpreq++;
      break;
    case 0x06:
      SM_regis.zip++;
      break;
    case 0x07:
      SM_regis.adsp++;
      break;
    default:
      break;
    }
}

void
handle_other (unsigned char *buf, int length)
{

  /* frame_protocol is global */
  if (frame_protocol < 1501)
    {				/* if IEEE 802.3 packet instead of Ethernet packet (per RFC 1700) */
      if ((short int) buf[14] <= StatMem->sap_types.c_max)
	{
	  tally( (short int)buf[14], &StatMem->sap_types );	/* count 802.2 SAP number */
	}
      SM_regis.new_ethernet_count++;
    }				/* if IEEE 802.3 packet instead of Ethernet packet */
  else
    {				/* else is an Ethernet packet */
      switch (htons (((struct at_frame_type *) buf)->appletalk_type1))	/* Appletalk */
	{
	case ETH_P_ATALK:
	  if (options.at_option)
	    exatalk ((struct at_frame_type *) buf, length);
	  break;
	case ETH_P_AARP:
	  SM_regis.aarp++;
	  break;
	default:
	  break;
	}
    }				/* else is an Ethernet packet */
}

void
handle_ip (struct ip *buf, int length)
{
  static int src_port, dest_port;

  if (buf->ip_p < StatMem->ip_types.c_max)
    {				/* if IP protocol type is to be tallied */
      tally( buf->ip_p, &StatMem->ip_types );	/* count IP protocol */
    }				/* if IP protocol type is to be tallied */

  switch (buf->ip_p)		/* IP Protocol */
    {

    case IPPROTO_TCP:		/* TCP */
      {

	/* Fix to ignore "Don't Fragment" packets pointed out by spock@phoenix.earthlight.co.nz */
	if (
#if (__BYTE_ORDER == __BIG_ENDIAN)
	     (buf->ip_off != 0) && ((buf->ip_off & 0x4000) == 0)
#elif (__BYTE_ORDER == __LITTLE_ENDIAN)
	     (buf->ip_off != 0) && ((buf->ip_off & 0x0040) == 0)
#else
#error	"Please fix <endian.h>"
#endif
	  )
	  {			/* if this is not the first frame of a fragmented packet */
	    tally( 0, &StatMem->tcp_port );	/* count fragment */
	  }			/* if this is not the first frame of a fragmented packet */
	else
	  {			/* else this is the first frame of a packet */
	    /* The below may count packets twice, but probably both are not displayed */
	    if (buf->ip_hl == 5)
	      {			/* if IP header is normal length we can assume 20 octects length */
		if ((src_port = ntohs (((struct tcphdr *) ((void *) buf + 20))->th_sport)) <= SN_MAX_TCP_PORTS)
	          tally( src_port, &StatMem->tcp_port );	/* count source port */
		if ((dest_port = ntohs (((struct tcphdr *) ((void *) buf + 20))->th_dport)) <= SN_MAX_TCP_PORTS)
		  if (dest_port != src_port)
		    {		/* ignore when dest same as src to avoid apparent over-tallying */
		      tally( dest_port, &StatMem->tcp_port );
		    }		/* ignore when dest same as src to avoid apparent over-tallying */
	      }			/* IP header normal length */
	    else
	      {			/* IP header with options */
		/* Incidentally, the (void *) is to cause byte-level math not sizeof(struct ip) */
		if ((src_port = ntohs (((struct tcphdr *) ((void *) buf + ((buf->ip_hl) * 4)))->th_sport)) <= SN_MAX_TCP_PORTS)
	          tally( src_port, &StatMem->tcp_port );	/* count source port */
		if ((dest_port = ntohs (((struct tcphdr *) ((void *) buf + ((buf->ip_hl) * 4)))->th_dport)) <= SN_MAX_TCP_PORTS)
		  if (dest_port != src_port)
		    {		/* ignore when dest same as src to avoid apparent over-tallying */
		      tally( dest_port, &StatMem->tcp_port );
		    }		/* ignore when dest same as src to avoid apparent over-tallying */
	      }			/* IP header with options */
	  }			/* else this is the first frame of a packet */
      }
      break;

    case IPPROTO_UDP:		/* UDP */
      {
	/* Fix to ignore "Don't Fragment" packets pointed out by spock@phoenix.earthlight.co.nz */
	if (
#if (__BYTE_ORDER == __BIG_ENDIAN)
	     (buf->ip_off != 0) && ((buf->ip_off & 0x4000) == 0)
#elif (__BYTE_ORDER == __LITTLE_ENDIAN)
	     (buf->ip_off != 0) && ((buf->ip_off & 0x0040) == 0)
#else
#error	"Please fix <endian.h>"
#endif
	  )
	  {			/* if this is not the first frame of a fragmented packet */
	    tally( 0, &StatMem->udp_port );	/* count fragment */
	  }			/* if this is not the first frame of a fragmented packet */
	else
	  {			/* else this is the first frame of a packet */
	    /* The below may count packets twice, but probably both are not displayed */
	    if (buf->ip_hl == 5)
	      {			/* if IP header is normal length we can assume 20 octects length */
		if ((src_port = ntohs (((struct udphdr *) ((void *) buf + 20))->ui_sport)) <= SN_MAX_UDP_PORTS)
		  tally( src_port, &StatMem->udp_port ); /* count source port */
		if ((dest_port = ntohs (((struct udphdr *) ((void *) buf + 20))->ui_dport)) <= SN_MAX_UDP_PORTS)
		  if (dest_port != src_port)
		    {		/* ignore when dest same as src to avoid apparent over-tallying */
		      tally( dest_port, &StatMem->udp_port );
		    }		/* ignore when dest same as src to avoid apparent over-tallying */
	      }			/* IP header normal length */
	    else
	      {			/* IP header with options */
		/* Incidentally, the (void *) is to cause byte-level math not sizeof(struct ip) */
		if ((src_port = ntohs (((struct tcphdr *) ((void *) buf + ((buf->ip_hl) * 4)))->ui_sport)) <= SN_MAX_UDP_PORTS)
		  tally( src_port, &StatMem->udp_port ); /* count source port */
		if ((dest_port = ntohs (((struct tcphdr *) ((void *) buf + ((buf->ip_hl) * 4)))->ui_dport)) <= SN_MAX_UDP_PORTS)
		  if (dest_port != src_port)
		    {		/* ignore when dest same as src to avoid apparent over-tallying */
		      tally( dest_port, &StatMem->udp_port );
		    }		/* ignore when dest same as src to avoid apparent over-tallying */
	      }			/* IP header with options */
	  }			/* else this is the first frame of a packet */
      }

      break;

    default:
      /* Don't increment "Other" because are not displaying "Other IP" counts */
      /* This packet does show in the "IP" total, but not elsewhere. */
      break;
    }
}

void
handle_frame (unsigned char *buf, int length, struct sockaddr *saddr)
{
  int prot_int;
  int search_int;
  int prot_now;
  struct ip *ip_ptr;

  ip_ptr = (struct ip *) ((void *) buf + ETH_HLEN);

  if (length > 0)
    {

      packet_type = ((struct ether_head *) buf)->ether_type;	/* Ethernet packet type ID field */
      frame_protocol = ntohs (packet_type);	/* Convert from network to host seq */
      if (frame_protocol < 1501)
	{			/* if an IEEE 802.3 packet */
	  frame_protocol = SN_PROT_IEEE802_3;
	}			/* if an IEEE 802.3 packet */

      /* The grand totals by interface type */
      if (strncmp (saddr->sa_data, "eth", 3) == 0)
	{
	  SM_regis.etherbytes += length;
	  SM_regis.ethercount++;
	}
      else if (strncmp (saddr->sa_data, "plip", 4) == 0)
	{
	  SM_regis.plipbytes += length;
	  SM_regis.plipcount++;
	}
      else if (strncmp (saddr->sa_data, "lo", 2) == 0)
	{
	  SM_regis.loopbytes += length;
	  SM_regis.loopcount++;
	  frame_protocol = SN_PROT_LOOP;
	}
      else if (strncmp (saddr->sa_data, "ppp", 3) == 0)
	{
	  SM_regis.pppbytes += length;
	  SM_regis.pppcount++;
	  frame_protocol = SN_PROT_PPP;
	  ip_ptr = (struct ip *) ((void *) buf);	/* No Ethernet Header */
	}
      else if (strncmp (saddr->sa_data, "sl", 2) == 0)
	{
	  SM_regis.slipbytes += length;
	  SM_regis.slipcount++;
	  frame_protocol = SN_PROT_SLIP;
	  ip_ptr = (struct ip *) ((void *) buf);	/* No Ethernet Header */
	}
      else
	{
	  SM_regis.otherbytes += length;
	  SM_regis.othercount++;
	}

	      if( tally( frame_protocol, &StatMem->prot ) > 0 )
		{		/* if protocol was not found */
		  SM_regis.unknown_type++;
		  SM_regis.unknown_frame_type = frame_protocol;
		}		/* if protocol was not found */

      switch (frame_protocol)
	{
	case ETH_P_IP:
	case SN_PROT_PPP:
	case SN_PROT_SLIP:
	case SN_PROT_LOOP:
	  if (options.tcp_option || options.udp_option || options.ip_option)
	    handle_ip (ip_ptr, length);
	  break;
	case ETH_P_ATALK:
	  if (options.at_option)
	    exatalk ((struct at_frame_type *) ((void *) buf + ETH_HLEN), length);
	  break;
	default:
	  if (options.ip_option || options.at_option)
	    handle_other (buf, length);
	  break;
	}

    }				/* if length > 0 */
}

void signal_handler (int signal_number)
{
	shutdown_flag = 1;	/* shut down upon any signal */
}

void
usage (char *arg)
{
  fprintf (stderr, "\n%s [-adgipstuh] [-k key] [-n name] [-w interface]\n\n", arg);
  fprintf (stderr, "   Gather network statistics\n");
  fprintf (stderr, "   version " Quoteify(SNVERSION) "\n\n");
  fprintf (stderr, "    -a       gather Appletalk statistics\n");
  fprintf (stderr, "    -c       clear the other option settings\n");
  fprintf (stderr, "    -d       daemonize (run in background)\n");
  fprintf (stderr, "    -g       gather General statistics\n");
  fprintf (stderr, "    -i       gather IP Protocols statistics\n");
  fprintf (stderr, "    -k key   define shared memory key (base 8, 10, 16)\n");
  fprintf (stderr, "    -n name  label this server with this name\n");
  fprintf (stderr, "    -p       gather Protocols statistics\n");
  fprintf (stderr, "    -s       gather IEEE 802.2 SAP statistics\n");
  fprintf (stderr, "    -t       gather TCP/IP statistics\n");
  fprintf (stderr, "    -u       gather UDP/IP statistics\n");
  fprintf (stderr, "    -w eth0  interface to watch\n");
  fprintf (stderr, "    -h       show this message\n");
  fprintf (stderr, "    no options: gather most statisticss\n");
  exit (0);
}
