/* 1620, Sat 17 Nov 01 (PST)

   METER_UX.C:  The AU Internet Accouting Meter mainline

   Copyright (C) 1992-2002 by Nevil Brownlee,
   CAIDA | University of Auckland */

/*
 * $Log: meter_ux.c,v $
 * Revision 1.1.1.2.2.12  2002/02/23 01:57:33  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.10  2001/06/07 05:33:41  nevil
 * Fork daemon before starting interface(s).
 * Linux 2.2.19 kernel reset promiscuous mode at the fork!
 *
 * Revision 1.1.1.2.2.9  2001/05/24 02:19:56  nevil
 * LfapMet implemented by Remco Poortinga.
 * MinPDUs implemented by Nevil.
 *
 * Revision 1.1.1.2.2.6  2000/08/08 19:44:53  nevil
 * 44b8 release
 *
 * Revision 1.1.1.2.2.4  2000/06/06 03:38:22  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2.2.1  2000/01/12 02:57:12  nevil
 * Implement 'packet pair matched' turnaroundtime distribution attributes.
 * Fix ASN-related bugs in NeTraMet, distribution-related bugs in fd_filter.
 *
 * Revision 1.1.1.2  1999/10/03 21:06:26  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.14  1999/09/29 22:29:15  nevil
 * Changes (mainly changing // to /* comments) for Irix
 *
 * Revision 1.1.1.1.2.13  1999/09/22 05:38:43  nevil
 * Improve code to work properly on 64-bit machines
 * - Add OS=ALPHA handling to configure.in
 * - Clean up the Alpha compiler warnings
 * - Change all the snmp-related code to use Bit32 instead of unsigned long
 *
 * Revision 1.1.1.1.2.12  1999/09/14 00:45:29  nevil
 * 4.3 Release ..
 *  - Implement -D option to run NeTraMet as a daemon
 *  - Tidy up the on-line help displays
 *
 * Revision 1.1.1.1.2.11  1999/05/26 02:41:41  nevil
 * Integrate V6 and ASN code into PC versions of the meter.
 * This required a rework of the makefiles, using @cflags.opt files
 * to provide a much longer command line to the Borland C compiler.
 *
 * Revision 1.1.1.1.2.10  1999/05/17 00:11:58  nevil
 * Fixed 'reuse of ruleset row' problem:
 *   When a ruleset row is destroyed, set_RowStatus calls free_rulespace()
 *   and recover_flows().  free_rulespace() zeroed its varibales in the
 *   ri[] row, but recover_flows() didn't (it should have zeroed
 *   ri_flow_chain).  This left ri_flow_chain pointing to a free flow,
 *   which cause problems if a manager tried to reuse this row for a
 *   new ruleset.  Fixed by zeroing all of the ri[] row. (Also commented
 *   out the zeroing code from free_rulesace).
 *
 * Revision 1.1.1.1.2.9  1999/04/20 04:45:59  nevil
 * Add some checking to handle_keyboard() so as to prevent it from
 * trying to process garbage from a failed fgets() call on stdin.
 * This was particularly nasty if stdin hadn't been opened (e.g. by
 * using the shell's "<&-" construct) - this resulted in a read from
 * fd0, which was being used for the SNMP socket!
 *                              (Patch supplied by Tony Stoneley)
 *
 * Revision 1.1.1.1.2.8  1999/04/13 05:13:35  nevil
 * Use log_msg for ALL error messages
 *
 * Revision 1.1.1.1.2.7  1999/02/16 03:59:11  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.6  1999/02/16 03:42:18  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.5  1999/02/15 21:24:09  nevil
 * Distribution file for 4.3b9
 *
 * Revision 1.1.1.1.2.4  1999/02/03 21:56:20  nevil
 * Distribution file for 4.3b8
 *
 * Revision 1.1.1.1.2.3  1999/01/20 03:55:52  nevil
 * Implementation of TCP attributes, part 4
 *
 * Revision 1.1.1.1.2.1  1998/11/25 03:30:13  nevil
 * Fix endian problems in NetFlowMet
 *
 * Revision 1.1.1.1  1998/11/16 03:57:29  nevil
 * Import of NeTraMet 4.3b3
 *
 * Revision 1.1.3.1.2.2  1998/10/19 22:32:49  nevil
 * Meter improvements, mostly arising from developments for the
 * OCxMON meter.  These are documented in notes_oc.txt
 *
 * Revision 1.1.3.1.2.1  1998/10/18 20:51:27  nevil
 * Added Nicolai's patches, some 'tidying up' of the source
 *
 * Revision 1.1.3.1  1998/10/13 02:48:41  nevil
 * Import of Nicolai's 4.2.2
 *
 * Revision 1.6  1998/10/02 15:00:30  nguba
 * Added print_help to display command-line options
 *
 * Revision 1.5  1998/06/10 21:12:03  rtfm
 * Implement popto/poptoact actions for SRL
 *
 * Revision 1.3  1998/05/22 03:57:35  rtfm
 * Implement better hashing algorithm for flow and rule tables
 */

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

#define CORAL_TIME_OFFSET   0
#define CORAL_BUFFER_TRACE  1
#define LOG_BAD_TIMESTAMPS  1  /* If you set this, 
               run meter inside gdb, and set breakpoint on quack */
#define noUX_TESTING
#define noCOPY_STRUCTS  /* True to copy fddi 'ethernet header' info.
                              Solaris 2.4 doesn't need this */
#define CHECK_BIT_ALIGN  1
   /*  Check that structures have multiples of 4 bytes */

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <sys/utsname.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <signal.h>

#include <time.h>
#include <sys/time.h>
#include <sys/times.h>
#include <limits.h>

#ifdef SUNOS
#include <stropts.h>
#define void
#define const
#endif

#include "../bgp/types/types.h"  /* Needed here for subnet.h */

#define EXTSNMP
#include "ausnmp.h"
#include "asn1.h"
#include "snmp.h"
#include "snmpimpl.h"

#define MXINTERFACES  4  /* Max nbr of interfaces to meter */

#define PKTSNAP
#include "pktsnap.h"
#include "flowhash.h"
#include "met_vars.h"

#define APPLETALK

extern char version_descr[];  /* In met_vars.c */
extern char *communities[];  /* In snmp/snmpagnt.c */
extern unsigned long pc_noflowpkts;  /* In met_vars.c */

#if NF_CISCO_DATA
# include "flowdata.h"
#elif LFAP_METER
# include "lfapmet.h"
# include <netdb.h>
# include <snmpapi.h>
# include "snmpclnt.h"
#else
# include <unistd.h>
# include "pcap-int.h"
# include "pcap.h"
# include "llc.h"
# if CR_DATA
#  include "libcoral.h"  /* CoralReef needs libpcap */
# endif
#endif

#if NF_OCX_BGP
#include "../bgp/integrat/readbgp.h"
#include "../bgp/integrat/subnet.h"
#endif

struct ether_hdr {  
   unsigned char dest[6];    /* MAC addresses */
   unsigned char source[6];
   Bit16 type;               /* Length or protocol type */
   };

#if DAG_DIRECT
#include "dag.h"
#include <sys/ioctl.h>

/* Dag trace file record layouts ...
      Note that they all use 64-byte fixed-length records */

struct snap_hdr {
   Bit16 lsap;           /* AAAA */
   Bit8 ctrl;            /* 03 */
   Bit8 oid1,oid2,oid3;  /* 00 00 00 */
   Bit16 etype;          /* 0800 */
   };

struct dag_atm {  /* Dag record for ATM (first cell only, a la OC3MON) */
   Bit32 crc;           /* Always zero */
   Bit32 atm_hdr;       /* VPI:VCI, etc */
   struct snap_hdr sh;  /* LLC snap header (8 bytes) */
   Bit8 pload[40];      /* First 40 bytes of IP packet */
   };

struct dag_ether {  /* Dag record for Ethernet */
   Bit16 wlen;          /* Wire length (bytes) */
   struct ether_hdr eh; /* Ethernet MAC header */
   Bit8 pload[40];
   };

struct dag_pos {  /* Dag record for PoS */
   Bit32 slen;          /* Snap length, must be 64 for now */
   Bit32 wlen;          /* Bytes on the wire, seems to include FCS */
   Bit8 chdlc;          /* Cisco HDLC header */
   Bit8 unused;         /* Padding */
   Bit16 etype;         /* EtherType */
   Bit8 pload[44];      /* 4 more bytes than ATM and Ether */
   };

#define TT_ATM    0  /* Dag trace file record types */
#define TT_ETHER  1
#define TT_POS    2

struct dag_rec {
   long long ts;        /* Dag timestamp (64-bit) */
   union {
      struct dag_atm atm;
      struct dag_ether eth;
      struct dag_pos pos;
      } d;
   };
#endif

#define SZ_IFNAME  64
struct interface_info {
   u_char nbr;  /* Interface number (for Surce/DestInterface attributes) */
   u_char adjtype;  /* Adjacent type for interface */
   char name[SZ_IFNAME+1];  /* Interface name, e.g. nf0 */
   u_int SampleRate,  /* 1 = count every packet on this interface */
      sample_count;
   int fd, last_errno;
   u_int ipackets;  /* Total packets since last stats reset */
   u_int idrops;  /* Dropped packets since last stats reset */
   u_int noflowpackets;

#if NF_CISCO_DATA
   struct sockaddr_in from;
   char *nf_buf;

#elif LFAP_METER
   FASInfo fasinfo;

#elif DAG_DIRECT || CR_DATA

# define MN_SELECT_US   5000  /* 5 ms */
# define MX_SELECT_US   (8*MN_SELECT_US)
# define TARGET_CPB  500  /* Try to vary select_us to keep cpb above this */

   int tcells, icells,ncells, nblocks;
   u_int badtspkts;  /* Packets with bad timestamps */
   u_int no_data_warned;
   int last_s;  /* State from last read */
#     define S_NORMAL  0
#     define S_EMPTY   1
#     define S_FULL    2
   u_int full,empty, mx_full,mx_empty,
   consec_badts;

# if DAG_DIRECT
   struct dag_rec *d_buf;
   struct dag_rec *drp;
   long long card_ts;  /* For next cell */
   int dag_type;

#  else  /* CR_DATA */
   coral_source_t *crl_src;
   coral_iface_t *iface;
   struct timeval card_ts;  /* For next cell */
   coral_atm_cell_t *cellp;
#  if CORAL_TIME_OFFSET
      struct timeval ts_offset;  /* Accumulated time error from card */
#  endif

# endif

#else  /* ADSL or libpcap */

# if !DAG_DATA  /* libpcap */
   pcap_t *pd;
   pcap_handler callback;
# endif

   int if_delta_set;
   struct timeval if_time_delta;  /* First packet time - meter_start_time */
#endif
   };

struct interface_info *pci[MXINTERFACES];
int n_interfaces;  /* Nbr of active interfaces */

int live_source = 1;  /* Set to zero to use trace files */

static int crl_n_max = 0;  /* Set by -N option */
static int crl_n_count;    /* Counting to crl_n_max */
static int trace_count;    /* Counter for 1s intervals within trace_interval */

/* tv_sub(x,y):  subtracts y from x; x,y are timevals */
#define tv_sub(x, y)  {			\
   if ((x).tv_usec < (y).tv_usec) {	\
      --(x).tv_sec;			\
      (x).tv_usec += 1000000;		\
      }					\
   (x).tv_sec -= (y).tv_sec;		\
   (x).tv_usec -= (y).tv_usec;		\
   }

/* tv_add(x,y):  adds y to x; x,y are timevals */
#define tv_add(x, y)  {			\
   (x).tv_usec += (y).tv_usec;		\
   if ((x).tv_usec > 1000000) {	        \
      ++(x).tv_sec;			\
      (x).tv_usec -= 1000000;		\
      }					\
   (x).tv_sec += (y).tv_sec;		\
   }

static unsigned long
    npackets_org = 0L, lostpackets_org = 0L, 
    pkts = 0L, drops = 0L;

#if DAG_DIRECT
long long meter_start_time;  /* Dag (64-bit) timestamp */
#else
struct timeval meter_start_time;
#endif

char *strmov(char *d, char *s)
{
   while (*s != '\0') *d++ = *s++;
   return d;
   }

/* Make SIGINT or SIGTERM shut the meter down gracefully */

int request_stop = 0;

void sigint_handler(int x)  /* INT (keyboard) and TERM (kill) */
{                /* To make ^c send INT, use   stty intr '^c' */
   request_stop = 1;
   }

void stop_meter(int code)
{
   display_msg(1,"Shutting down");
#if CR_DATA
   coral_stop_all();  /* Leave Dag cards in a graceful state */
   coral_close_all();
#endif
   exit(code);
   }


#if NF_CISCO_DATA

void interface_read(struct interface_info *pi)
{
   int flen, j;
   char msg[60];
   int nf_version;
   IPStat1Msg *nf1;  IPFlow1Stat *fr1;
   IPStat5Msg *nf5;  IPFlow5Stat *fr5;
   struct pkt pp;
   Bit16 as;
   Bit32 uptime_delta;
   /* SysUptime (from packet header) is normally a few seconds behind
      the NetFlowMet meter, which means that flow FirstTime and LastTime
      are negative numbers.  To get this to work properly, we force an
      integer divide by 10 when converting to centiseconds (below).
      InactivityTimeout is usually measured in minutes (default 600s),
      and garbage_collect() uses unsigned compares against LastTime, so
      NetFlowMet will time flows out properly. */

   flen = sizeof(pi->from);
   if (recvfrom(pi->fd, pi->nf_buf, MAX_FLOW_PAK_SIZE, 0, 
         (struct sockaddr *)&pi->from, &flen) < 0) {
      log_msg(LOG_ERR, 0, "nf_read(%s): recvfrom failed", pi->name);
      return;
      }

   nf_version = pi->nf_buf[0] << 8 | pi->nf_buf[1];
   if (nf_version == FLOW_VERSION_1) {
      nf1 = (IPStat1Msg*)pi->nf_buf;
      uptime_delta = ntohl(nf1->header.SysUptime)/10 - uptime_cs();
      for (fr1 = nf1->records, j = 0; 
            j != ntohs(nf1->header.count); ++fr1, ++j) {
         pi->ipackets += ntohl(fr1->dPkts);

         pp.Low.Interface = ntohs(fr1->input);
         pp.High.Interface = ntohs(fr1->output);
         pp.PeerAddrType = AT_IP4;  /* Dummy up an IP packet */
         memcpy(&pp.Low.PeerAddress.byte,&fr1->srcaddr,IP4_ADDR_LEN);
         memcpy(&pp.High.PeerAddress.byte,&fr1->dstaddr,IP4_ADDR_LEN);
         pp.TransAddrType = fr1->prot;
         pp.Low.TransAddress = fr1->srcport;  /* Network byte order */
         pp.High.TransAddress = fr1->dstport;
         pp.DSCodePoint = fr1->tos >> 2;

         pp.dPkts = ntohl(fr1->dPkts);
         pp.dOctets = ntohl(fr1->dOctets);
         pp.dFirst = ntohl(fr1->First)/10 - uptime_delta;  /* Centiseconds */
         pp.dLast = ntohl(fr1->Last)/10 - uptime_delta;

         pp.Low.AdjType = 0;  /* NetFlow attributes */
         pp.Low.AdjAddr_ms4 = 0;  pp.Low.AdjAddr_ls2 = 0;
#if 0    /* RTFM meter can't match S->D and D->S without having both */
         pp.High.AdjType = AT_IP4;   /* addresses for EVERY packet */
         pp.High.AdjAddr_ms4 = fr1->nexthop;  pp.High.AdjAddr_ls2 = 0;
#else
         pp.High.AdjType = 0;
         pp.High.AdjAddr_ms4 = 0;  pp.High.AdjAddr_ls2 = 0;
#endif
         pp.Low.nf_ASN = pp.High.nf_ASN = 0;
         pp.Low.nf_mask = pp.High.nf_mask = 0;

         pp.ntm_interface = pi->nbr;  
               /* Remember interface from -i option */
         pkt_monitor(&pp);
         }
      }
   else if (nf_version == FLOW_VERSION_5) {
      nf5 = (IPStat5Msg*)pi->nf_buf;
      uptime_delta = ntohl(nf5->header.SysUptime)/10 - uptime_cs();

      for (fr5 = nf5->records, j = 0; 
            j != ntohs(nf5->header.count); ++fr5, ++j) {
         pi->ipackets += ntohl(fr5->dPkts);

         pp.Low.Interface = ntohs(fr5->input);
         pp.High.Interface = ntohs(fr5->output);
         pp.PeerAddrType = AT_IP4;  /* Dummy up an IP packet */
         memcpy(&pp.Low.PeerAddress.byte,&fr5->srcaddr,IP4_ADDR_LEN);
         memcpy(&pp.High.PeerAddress.byte,&fr5->dstaddr,IP4_ADDR_LEN);
         pp.TransAddrType = fr5->prot;
         pp.Low.TransAddress = fr5->srcport;  /* Network byte order */
         pp.High.TransAddress = fr5->dstport;
         pp.DSCodePoint = fr5->tos >> 2;

         pp.dPkts = ntohl(fr5->dPkts);
         pp.dOctets = ntohl(fr5->dOctets);
         pp.dFirst = ntohl(fr5->First)/10 - uptime_delta;  /* Centiseconds */
         pp.dLast = ntohl(fr5->Last)/10 - uptime_delta;
            /* Was ntohl(fr5->First/10) !!!
               Bug reported by Oleg O. Orlov <OOO@vmts.ru>, 3 Oct 01 */

         pp.Low.AdjType = 0;  /* NetFlow attributes */
         pp.Low.AdjAddr_ms4 = 0;  pp.Low.AdjAddr_ls2 = 0;
#if 0    /* RTFM meter can't match S->D and D->S without having both */
         pp.High.AdjType = AT_IP4;   /* addresses for EVERY packet */
         pp.High.AdjAddr_ms4 = fr5->nexthop;  pp.High.AdjAddr_ls2 = 0;
#else
         pp.High.AdjType = 0;
         pp.High.AdjAddr_ms4 = 0;  pp.High.AdjAddr_ls2 = 0;
#endif
#if 0  /* This works on PC, but crashes on SPARC - not short-aligned */
         *(Bit16 *)&pp.Low.nf_ASN = ntohs(fr5->src_as);
         *(Bit16 *)&pp.High.nf_ASN = ntohs(fr5->dst_as);
#endif
         pp.Low.nf_ASN = fr5->src_as;  /* Network byte order */
         pp.High.nf_ASN = fr5->dst_as;
         pp.Low.nf_mask = fr5->src_mask;
         pp.High.nf_mask = fr5->dst_mask;

         pp.ntm_interface = pi->nbr;  
               /* Remember interface from -i option */
         pkt_monitor(&pp);
         }
      }
   else {
      log_msg(LOG_ERR, 0, 
         msg, "nf_read(%s): NF version %d ???", pi->name, nf_version);
      return;
      }
   }

int init_interface(struct interface_info *pi)  /* NetFlow */
{
   int soc;
   struct sockaddr_in socadr;
   struct in_addr netid;
   ushort port;

   if (pi->name[0] == '\0') strcpy(pi->name, "9996");
   port = atoi(pi->name);

   /* Open a UDP port to the NetFlow router */
   socadr.sin_family = AF_INET;
   socadr.sin_addr.s_addr = htonl(INADDR_ANY);
   socadr.sin_port = htons(port);
   netid.s_addr = socadr.sin_addr.s_addr;

   if ((soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
      log_msg(LOG_ERR, 0, "interface %d: couldn't get socket, errno %d\n",
         pi->nbr, (errno));
      return 0;  /* Fail */
      }
   /* Bind the socket */
   if (bind(soc, (struct sockaddr *)&socadr, sizeof(socadr)) < 0) {
      log_msg(LOG_ERR, 0, "interface %d: couldn't bind socket, errno %d\n",
         pi->nbr, (errno));
      return 0;  /* Fail */
      }

   sprintf(pi->name, "udp-%u", port);
   pi->fd = soc;

   pi->nf_buf = (char *)malloc(MAX_FLOW_PAK_SIZE);
   return 1;  /* OK */
   }

unsigned int pkt_counts(int reset)
{
   int j;
   struct interface_info *pi;

   if (reset) {
      npackets_org = pkts;  lostpackets_org = drops;
      }
   pkts = drops = 0L;  /* Get total packets seen and dropped */
   for (j = 0; j != n_interfaces; ++j) {
      pi = pci[j];
      pkts += pi->ipackets;  drops += pi->idrops;
      }
   npackets = pkts - npackets_org;
   lostpackets = drops - lostpackets_org;
   return 1;
   }
 
#elif CR_DATA  /* !NF_CISCO_DATA */

/* ===== Interface code using CoralReef to get packet headers ===== */

#define MERGE_TEST  0

#define MX_TS_JUMP  10000000  /* in us, i.e. 10 seconds */

int tv_diff_us(struct timeval x, struct timeval y)
{  /* Returns x-y microseconds, **assuming y >= x** */
   int s,u;
   s = x.tv_sec - y.tv_sec;
   if ((u = x.tv_usec - y.tv_usec) < 0) {
      --s;  u += 1000000;
      }
   return s*1000000 + u;
   }

int waiting_live_source = -1;

static struct timeval last_ts;
   /* Used by uptime() to get CoralReef times */

int select_us = 4*MN_SELECT_US;
struct timeval select_tv = {0,4*MN_SELECT_US};
#define MX_NODATA_READS  50  /* Don't wait too long! */

void handle_coral_packet(int src_nbr,
   coral_pkt_buffer_t *header, coral_pkt_buffer_t *packet)
{
   union atm_hdr atm_h;
   coral_llcsnap_t *snap_h;
   coral_pkt_buffer_t payload;
   struct ether_hdr *ethp;
   struct llc *llcp;
   unsigned int ether_type, lsap, atype;
   Bit8 *p;
   struct ether_hdr ehdr;
   struct pkt pmp;

   switch (packet->protocol) {
   case CORAL_DLT_ATM_RFC1483:
      atm_h.ui = ntohl(*(Bit32 *)header->buf);
      snap_h = (coral_llcsnap_t *)packet->buf;
      if (coral_get_payload(packet, &payload) < 0) {
         log_msg(LOG_INFO, 0, "coral_get_payload failed");
         return;
         }
      if (adj_reqd) {  /* Don't do this unless we have to! */
         /* Use UNI VPI and VCI as low 3 bytes of MAC address.
            Put it in both source and dest so that rulesets
            which test only one will work as expected */
         *(Bit16 *)&ehdr.dest[0] = *(Bit16 *)&ehdr.source[0] = 0;
         *(Bit32 *)&ehdr.dest[2] = *(Bit32 *)&ehdr.source[2] = atm_h.h.vpvc; 
         }
      ether_type = ntohs(snap_h->snap_type);  lsap = 0xAAAA;
      ethp = &ehdr;  p = (Bit8 *)payload.buf;  atype = AT_AAL5;
      break;
   case CORAL_DLT_ATM_AAL5:
      log_msg(LOG_ERR, 0, "coral_packet(): ATM_AAL5 unimplemented");
      return;
   case CORAL_DLT_LANE_IEEE8023:
      log_msg(LOG_ERR, 0, "coral_packet(): LANE_IEEE8023 unimplemented");
      return;
   case CORAL_DLT_EN10MB:
      ethp = (struct ether_hdr *)packet->buf;
      ether_type = ntohs(ethp->type);
      if (coral_get_payload(packet, &payload) < 0) {
         log_msg(LOG_INFO, 0, "coral_get_payload failed");
         return;
         }
      lsap = 0xAAAA;
      p = (Bit8 *)payload.buf;  atype = AT_ETHERNET;
      break;
   case CORAL_DLT_IEEE802:
      ethp = (struct ether_hdr *)packet->buf;
      if (coral_get_payload(packet, &payload) < 0) {
         log_msg(LOG_INFO, 0, "coral_get_payload failed");
         return;
         }
      p = (Bit8 *)payload.buf;
      llcp = (struct llc *)p;
      lsap = llcp->dsap << 8 | llcp->ssap;
      if (lsap == 0xAAAA) {  /* SNAP packet */
         ether_type = ntohs(*(Bit16 *)llcp->ctl.snap_ether.snap_ethertype);
         p += sizeof(struct llc);
         }
      else ether_type = 0;  /* Other 802.2 packet */
      atype = AT_ETHERNET;
      break;
   case CORAL_DLT_GIGX:
      log_msg(LOG_ERR, 0, "coral_packet(): GIGX unimplemented");
      return;
   default:
#if 0
      { int j;  Bit8 *p8 = (Bit8 *)packet->buf;
         for (j = 0; j != 40; ++j) printf(" %02x", p8[j]);
         printf("\n");
         }
      log_msg(LOG_ERR, 0,
         "coral_packet(): unknown protocol %d", packet->protocol);
#endif
      return;
      }

   pmp.p_len = 0;  /* use_ip_length will get length from IP header */
   if (handle_pkt(&pmp, ether_type,lsap,
         (unsigned char *)ethp, p, payload.caplen)) {
      pmp.Low.Interface = pmp.High.Interface = src_nbr+1;  /* 1-org */
      pmp.Low.AdjType = pmp.High.AdjType = atype;
      pmp.arrival_time = last_ts;  /* struct timeval */
      pkt_monitor(&pmp);
      }
   }

int read_live_block(int);

void fmt_ts(char *t_buf, struct timeval t)
{
   tv_sub(t, meter_start_time);
   sprintf(t_buf, "%lu.%06lu", t.tv_sec, t.tv_usec);
   }

void bad_ts_msg(char *msg, int s, struct interface_info *pi,
   struct timeval ts, struct timeval tb, char *comment)
   /* ts = last good timestamp, tb = timestamp from buffer.
      diff (as printed) = tb - ts */
{
   int d = tv_diff_us(tb,ts)/1000000;
   char b_ts[30], b_tb[30];
   fmt_ts(b_ts, ts);  fmt_ts(b_tb, tb);
   log_msg(LOG_ERR, 0, "%s i/f %d: cells=%u/%u, %s-%s, diff=%ds%s",
      msg, s+1, pi->ncells,pi->icells, b_ts,b_tb, d, comment);
   }

int ts_seq_ok(struct timeval ts, struct timeval tb)
{
   int td = tv_diff_us(tb,ts);
   if (td < 0) td = -td;
   return td <= MX_TS_JUMP;
   }

coral_merge()
{
   struct timeval ts, mints;
   int j, minj, tsec, n;
   struct interface_info *pi;
   coral_pkt_buffer_t outer, hdr;
   char nbt[60];

   pi = pci[0];
   fmt_ts(nbt, pi->card_ts);
   for (;;) {
      mints = pci[0]->card_ts;  minj = 0;
      for (j = 1; j != n_interfaces; ++j) {  /* Find min timestamp */
         ts = pci[j]->card_ts;
         if ((ts.tv_sec == mints.tv_sec && ts.tv_usec <= mints.tv_usec)
               || ts.tv_sec < mints.tv_sec) {
            mints = ts;  minj = j;
            }
         }
      pi = pci[minj];
      last_ts = mints;  /* Used by uptime() routines */

      if (--pi->sample_count == 0) {  /* Process only 1 pkt per SampleRate */
         pi->sample_count = pi->SampleRate;

         if (coral_cell_to_pkt(pi->iface, pi->cellp, &hdr)) {
               /* Dummy up an RFC1483 packet */
            outer.protocol = CORAL_DLT_ATM_RFC1483;
            outer.buf = (Bit8 *)coral_cell_header(pi->iface, pi->cellp);
            handle_coral_packet(pi->nbr, &outer, &hdr);
            }
         }

      if (last_ts.tv_sec > last_t_sec) {
         last_t_sec = last_ts.tv_sec;
         one_second_process();  /* NeTraMet housekeeping */
            /* Note this happens at time seen by interface,
               could get several seconds of data in buffer */
         }
      ++pi->ipackets;  /* For global statistics */

      if (--pi->ncells == 0) {
         waiting_live_source = minj;
         return;  /* Keep outer block alive */
         }
      else {
         pi->cellp += coral_cell_size(pi->iface);
         CORAL_TIMESTAMP_TO_TIMEVAL(pi->iface, 
            coral_cell_time(pi->iface, pi->cellp), &ts);
         if (ts_seq_ok(mints, ts)) {
            pi->card_ts = ts;  /* Timestamp looks OK */
	    }
         else {  /* Bad timestamp, look through block for a good one */
            for (n = pi->ncells;  pi->ncells != 0;  --pi->ncells,
                  pi->cellp += coral_cell_size(pi->iface)) {
               CORAL_TIMESTAMP_TO_TIMEVAL(pi->iface, 
                  coral_cell_time(pi->iface, pi->cellp), &ts);
               if (ts_seq_ok(mints, ts))
                  break;  /* Found a likely timestamp  */
               ++pi->badtspkts;
               }
            if (pi->ncells == 0) {  /* Ignore bad pkts at end of buffer */
               sprintf(nbt, ": %d pkts ignored (end of buffer)", n);
               bad_ts_msg("**T", minj, pi, mints, ts, nbt);
               pi->idrops += n;
               waiting_live_source = minj;
               return;  /* Keep outer block alive */
               }
            else {  /* Ignore bad pkts within buffer */
               sprintf(nbt, ": %d pkts ignored (within buffer)", n-pi->ncells);
               bad_ts_msg("*T*", minj, pi, mints, ts, nbt);
               pi->idrops += n-pi->ncells;
               pi->card_ts = ts;  /* Change timestamp */
               }
            }
         }
      }
   }


int read_live_block(int i)
{
   struct interface_info *pi = pci[i];
   coral_iface_t *iface;
   coral_blk_info_t *binfop;
   coral_atm_cell_t *cellp;
   struct timeval ts;
   char g_ts[30], b_ts[30];

   errno = 0;  /* System call error variable */
   if ((iface = coral_read_block(pi->crl_src, &binfop, &cellp,
         &select_tv)) == NULL) {
      if (errno != pi->last_errno) {
         if (errno != EAGAIN) log_msg(LOG_ERR, 0,
            "if_read(1): i/f %d, error %d (%s)",
            pi->nbr, errno, strerror(errno));
         pi->last_errno = errno;
         }
      /* NOTE: The Coral Dag driver doesn't return partial blocks,
               we have to sit around waiting until we get 16384
               cells on an interface, which can take several
               seconds on a link which doesn't have much traffic! */
      if (++pi->empty > MX_NODATA_READS) {
         /* Guess lowest ts for next pkt */
         tv_add(pi->card_ts,select_tv);
         pi->empty = 0;
         if (!pi->no_data_warned) {
            fmt_ts(g_ts, pi->card_ts);
            log_msg(LOG_WARNING, 0,
               "i/f %d, no data, ts set to %s", pi->nbr, g_ts);
            pi->no_data_warned = 1;
            }
         }
      return 0;  /* Try again */
      }
   else {  /* Data bytes read */
      pi->no_data_warned = pi->last_errno = 0;
      pi->iface = iface;  pi->cellp = cellp;
      pi->icells = pi->ncells = ntohl(binfop->cell_count);
      pi->idrops += ntohl(binfop->cells_lost);
#if MERGE_TEST == 2
      printf("\nif_read(2): blocks=%d, icells=%d", pi->nblocks,pi->icells);
#endif

      CORAL_TIMESTAMP_TO_TIMEVAL(pi->iface, 
         coral_cell_time(pi->iface, pi->cellp), &ts);
      if (pi->nblocks == 0 ||  /* Just getting started */
            pi->empty != 0 ||  /* Had some no_data reads */
            ts_seq_ok(pi->card_ts, ts)) {
         pi->card_ts = ts;  /* Timestamps OK between blocks */
         }
      else {  /* Bad timestamp at start of block */
         fmt_ts(g_ts, pi->card_ts);  fmt_ts(b_ts, ts);
         log_msg(LOG_ERR, 0,
            "nif_read(5): i/f %d, block %d, %d cells, %s -> %s, "
               "bad ts at start of block !!!!!",
               pi->nbr, pi->nblocks, pi->ncells, g_ts, b_ts);
         pi->idrops += pi->icells;
         ++pi->badtspkts;
         return 0;
         }

      ++pi->nblocks;  pi->tcells += pi->ncells;
#if MERGE_TEST >= 1
      fmt_ts(b_ts, pi->card_ts);
      printf("\nif_read(7): i/f %d, ts=%s, %d ncells, %d nblocks\n", 
         pi->nbr, b_ts, pi->ncells, pi->nblocks);
#endif
      }
   return 1;
   }

void init_live_sources(void)
{
#define STARTUP_TRIES  2000  /* There may not be very much traffic */
   int j,n,b;
   struct timeval ts;

   sleep(10);  /* Wait until we get a full buffer */
   for (j = 0; j != n_interfaces; ++j)
      pci[j]->nblocks = 0;
   for (n = 0; n != STARTUP_TRIES; ++n) {
      for (j = 0; j != n_interfaces; ++j) {  /* Get block for each source */
         read_live_block(j);
         }
      for (b = j = 0; j != n_interfaces; ++j)
         if (pci[j]->nblocks != 0) ++b;
      if (b == n_interfaces) break;
      }

   if (n == STARTUP_TRIES) log_msg(LOG_ERR, 98,
      "init_live_sources(): couldn't get data from interface(s)");

   meter_start_time = pci[0]->card_ts;  /* Set to min Coral timestamp */
   for (j = 1; j != n_interfaces; ++j) {
      ts = pci[j]->card_ts;
      if (ts.tv_sec == meter_start_time.tv_sec &&
            ts.tv_usec <= meter_start_time.tv_usec)
         meter_start_time = ts;
      else if (ts.tv_sec < meter_start_time.tv_sec)
         meter_start_time = ts;
      }
   last_ts = meter_start_time;

   printf("init_live_sources(): first blocks read\n");
   fflush(stdout);
   }

int interface_read_trace(struct interface_info *pi)
{
   coral_iface_t *iface;
   coral_pkt_result_t pkt_result;
   coral_interval_result_t interval_result;
   int src_nbr;

   if ((iface = coral_read_pkt(&pkt_result, &interval_result)) == NULL) {
      if (errno == 0) {  /* End of trace file  or  stopped device */
         show_memory();
         show_stream_hash();
         }
      else
         log_msg(LOG_ERR, 0, "coral_read_pkt() failed, %s", strerror(errno));
      return 0;  /* Signals EOF to non-live loop */
      }
   else {
      src_nbr = coral_source_get_number(coral_interface_get_src(iface));
      if (pkt_result.packet != NULL) {  /* We have a packet */

         CORAL_TIMESTAMP_TO_TIMEVAL(iface, pkt_result.timestamp, &last_ts);
         pi->sample_count = pi->SampleRate;

         handle_coral_packet(src_nbr,
            pkt_result.header, pkt_result.packet);

         if (last_ts.tv_sec != last_t_sec) {
            last_t_sec = last_ts.tv_sec;
            one_second_process();  /* NeTraMet housekeeping */
            --trace_count;
            }
         }
      else if (interval_result.stats == NULL) {  /* Coral interval has begun */
         last_ts = interval_result.begin;
	 }
      else {  /* A Coral interval has ended **or** we've reached EOF !!! */
         pi->ipackets += interval_result.stats->pkts_recv;
         pi->idrops += interval_result.stats->pkts_drop;

         printf("End(%d): ts=%d.%03d, active=%u, new=%d, recov=%u, "
               "(GC: %u s, %u flow), pkts=%d, streams=%d\n",
            trace_count, last_ts.tv_sec,last_ts.tv_usec/1000, active_flows(),
            FlowsCreated, FlowsRecovered,
            gc_interval, gc_f,
            mxpktdatas-n_free_pktdata, mxstreams-n_free_streams);
         zero_stats(0);

         last_ts = interval_result.end;
         if (last_ts.tv_sec != last_t_sec)
            --trace_count;  /* Tell receive() loop we're at interval end */
         }         
      }
   return 1;
   }

char *cs_type(int cst)
{
   switch (cst) {
   case CORAL_TYPE_FILE:
      return "CoralReef file";
   case CORAL_TYPE_PCAP:
      return "tcpdump file";
   case CORAL_TYPE_DAGFILE:
      return "Dag file";
   case CORAL_TYPE_TSH:
      return "tsh file";
   case CORAL_TYPE_NONE:
      return "Invalid or Unknown";
   case CORAL_TYPE_FATM:
       return "FORE card";
   case CORAL_TYPE_POINT:
      return "POINT card";
   case CORAL_TYPE_DAG:
      return "DAG card";
   case CORAL_TYPE_PCAPLIVE:
      return "pcap interface";
   default:
      return "Bad cs_type";
      }
   }

int init_interface(struct interface_info *pi)  /* CoralReef */
{
   int src_nbr,src_type, if_nbr, info_len, len, ir;
   time_t ct, min_ct;
   char buf[150], *bp, *crl_info_string;
   coral_source_t *i_src;
   struct timeval c_interval;

   coral_iface_t *iface; int ifn, ift; time_t cap_time;

   if (pi->nbr == 1) {  /* Handling first CoralReef source */
      coral_set_options(0, CORAL_OPT_NORMALIZE_TIME);
      /* We want timestamps in Unix epoch across all sources */
      }
   if (coral_open(i_src = pi->crl_src) < 0) {
      log_msg(LOG_ERR, 0, "coral_open(%s) failed", pi->name);
      return 0;  /* Fail */
      }

   src_nbr = coral_source_get_number(i_src);
   if (src_nbr != pi->nbr-1)
      log_msg(LOG_ERR, 0, "src_nbr(%d) != pi->nbr(%d)", src_nbr,pi->nbr-1);

   src_type = coral_source_get_type(i_src);
   switch (src_type) {
   case CORAL_TYPE_FILE:   /* CoralReef trace file */
   case CORAL_TYPE_PCAP:   /* pcap (tcpdump) trace file */
   case CORAL_TYPE_DAGFILE:
   case CORAL_TYPE_TSH:
   case CORAL_TYPE_NONE:   /* Invalid or unknown */
   default:
      sprintf(pi->name, "crl%d (%s)", src_nbr, cs_type(src_type));
      pi->adjtype = AT_TRACE;  /* Don't know original interface type */
      live_source = 0;
      break;
   case CORAL_TYPE_FATM:   /* FORE OC3 card */
   case CORAL_TYPE_POINT:  /* Apptel POINT card */
   case CORAL_TYPE_DAG:    /* DAG card */
   case CORAL_TYPE_PCAPLIVE:
      sprintf(pi->name, "%s (%s)",
         coral_source_get_filename(i_src), cs_type(src_type));
      pi->adjtype = AT_ATM;
      live_source = 1;
      break;
      }

   if (pi->nbr == n_interfaces) {  /* Handling last CoralReef source */
      log_msg(LOG_INFO, 0, "%d coral interfaces opened", n_interfaces);
      if (coral_start_all() < 0) {  /* Also synchronises all sources */
         log_msg(LOG_ERR, 0, "coral_start failed");
         return 0;  /* Fail */
         }

     if (!live_source) {
         cap_time = 0;  iface = NULL;
         do {
            iface = coral_next_interface(iface);
            if (iface != NULL) {
               if_nbr = coral_interface_get_number(iface);
               ct = coral_interface_get_capturetime(iface);
               if (if_nbr == 0) min_ct = ct;
               else if (ct < min_ct) min_ct = ct;
               }
            } while (iface != NULL);

         sprintf(buf, "%d  source file %s  cap_time %u ", trace_interval,
            coral_source_get_filename(i_src), min_ct);
         info_len = strlen(buf);  bp = &buf[info_len];

         cap_time = 0;  iface = NULL;
         do {
            iface = coral_next_interface(iface);
            if (iface != NULL) {
               if_nbr = coral_interface_get_number(iface);
               src_type = coral_interface_get_type(iface);
               cap_time = coral_interface_get_capturetime(iface);
               sprintf(bp, " (i/f %d %s %d)",
                  if_nbr, cs_type(src_type), cap_time);
               len = strlen(bp);  bp += len;  info_len += len;
               }
            } while (iface != NULL);
         if (info_len > sizeof(buf))
	    log_msg(LOG_ERR, 1, "NeTraMet Coral info string overflow !!!");
         crl_info_string = (char *)malloc(info_len+1);
         strcpy(crl_info_string, buf);
         crl_info = crl_info_string;
            /* NeMaC writes this to a #CoralReef record */
         }
      else {  /* Live source */
         cap_time = 0;  iface = NULL;
         do {
            iface = coral_next_interface(iface);
            if (iface != NULL) {
               if_nbr = coral_interface_get_number(iface);
               ct = coral_interface_get_capturetime(iface);
               if (if_nbr == 0) min_ct = ct;
               else if (ct < min_ct) min_ct = ct;
               }
            } while (iface != NULL);
         last_t_sec = meter_start_time.tv_sec = min_ct;
         meter_start_time.tv_usec = 0;
         }
 
      if (!live_source) {
         if (trace_interval <= 0) {
            printf("No meter reading interval specified (-T option)\n");
            log_msg(LOG_ERR, 0,
               "No meter reading interval specified (-T option)");
            return 0;  /* Fail */
            }

         c_interval.tv_sec = trace_interval;  c_interval.tv_usec = 0;
         if ((ir = coral_read_pkt_init(NULL, NULL, &c_interval)) != 0) {
                                         /* For one_second_process() */
            log_msg(LOG_ERR, 0,
               "coral_read_pkt_init failed, result=%d", ir);
            return 0;  /* Fail */
            }
         /* We only want one interval per second, not one per source */
         trace_count = trace_interval;
         crl_n_count = 0;
         }
      }

   return 1;  /* OK */
   }

unsigned int pkt_counts(int reset)  /* Get total packets seen and dropped */
{              /* 'Source' counters updated at end of CoralReef intervals */
   int j;
   struct interface_info *pi;

   if (reset) {
      npackets_org = pkts;  lostpackets_org = drops;
      }

   pkts = drops = 0L;
   for (j = 0; j != n_interfaces; ++j) { /* One NetraMet interface per source */
      pi = pci[j];
      pkts += pi->ipackets;  drops += pi->idrops;
      }
   npackets = pkts - npackets_org;
   lostpackets = drops - lostpackets_org;
   return 1;
   }

#elif LFAP_METER /* !NF_CISCO_DATA && !CR_DATA */

int    lfap_server_socket;
int    lfap_info_level = 0;
int    lfap_keepalive_interval = 30; /* in seconds */
char   lfap_community[SZ_IFNAME];
struct sockaddr_in	lfap_server_addr;
val64  fasPrefix;

int    handleVR(struct interface_info *pi);
int    handleFAR(struct interface_info *pi);
int    handleFUN(struct interface_info *pi);
int    handleAR(struct interface_info *pi);
int    handleARA(struct interface_info *pi);
void   processMessage(struct interface_info *pi);
void   handleChangedFlows(struct interface_info *pi);
void   removeFlow(struct interface_info *pi, int flownr);

void   tick(struct interface_info *pi);

int    start_lfap(struct utsname *name);
  
#include "lfapmet.c"  /* Too much code to keep in this file! */

#elif DAG_DATA  /* !NF_CISCO_DATA && !CR_DATA && !LFAP_METER */

   /* ===== Dag ADSL (via input pipe)  interface code ===== */

#include <fcntl.h>

/* DAG_ADSL should select this format for the Dag data ... */

struct adsl_rec {
   long long ts;     /* Dag timestamp (64-bit) */
   Bit32 atm_hdr;    /* First byte 0x01 or 00: packet direction */
   Bit8 stuff[32];   /* PPPoE bridged over AAL5 stuff */
   Bit8 ip_hdr[64];  /* First 64 bytes of IP packet */
   };

#if 0
ts=0x3a94c97f2708f900 atm=0x00a00660
0xaaaa0300 0x80c20007 0x0000   0090 0x3935403f
                               Concentrator MAC addr
0x00d0590d 0x325b 8864 0x1100  0066 0x05ae  0021
   Host MAC addr  PPPoE  vt   SessID   len   PPP protocol
0x450005ac 0x42964000 0x4006d73e 0x0a64034a  PPP payload
0x0ac80302 0x08830014 0x0abf8a22 0x00545b09
0x50107edc 0xb8ce0000 0xecfe1d19 0xa5da1b3c
pkt0x3c334000 0x0001121a 0x8be23cfa 0x3683523c
#endif

void handle_adsl_record(struct interface_info *pi,
   struct adsl_rec *a_buf)
{
   struct timeval tv;
   struct pkt pp;
   struct ether_hdr ehdr;
   Bit32 ah;  Bit8 dirn;

/*   printf("ts=%qx atm=%lx\n", a_buf->ts, a_buf->atm_hdr);  */

   tv.tv_sec = (long)(a_buf->ts >> 32);
   tv.tv_usec = ((a_buf->ts & 0xffffffffLL) * 1000000) >> 32;

   if (pi->if_delta_set == 0) {  /* First timestamp for this interface */
      pi->if_time_delta = tv;
      tv_sub(pi->if_time_delta, meter_start_time);
      pi->if_delta_set = 1;
      }

   if (--pi->sample_count == 0) {  /* Process only 1 pkt per SampleRate */
      pi->sample_count = pi->SampleRate;

      ah = ntohl(a_buf->atm_hdr);
      dirn = *(Bit8 *)&ah;  /* 0 or 1, indicates dag0 or dag1 */
      if (adj_reqd) {
         ehdr.source[0] = ehdr.source[1] = 0;
         memcpy(&ehdr.source[3], (Bit8 *)&ah + 1, 3);
         memcpy(&ehdr.dest, &ehdr.source, MAC_ADDR_LEN);
         if (dirn) {
            ehdr.source[2] = 2;  ehdr.dest[2] = 1;
            }
         else {
            ehdr.source[2] = 1;  ehdr.dest[2] = 2;
            }
         }

      if (handle_pkt(&pp, 0x0800, 0xAAAA, (Bit8 *)&ehdr,
            /* Will need to work out what eth_type/lsap should be */
            a_buf->ip_hdr, 64)) {
         pp.Low.AdjType = pp.High.AdjType = AT_PPP;  /* Generic PPP */
         if (dirn) {
            pp.Low.Interface = 2;  pp.High.Interface = 1;
            }
         else {
            pp.Low.Interface = 1;  pp.High.Interface = 2;
            }
         pp.arrival_time = tv;
         pkt_monitor(&pp);

         if (pp.arrival_time.tv_sec != last_t_sec) {
            last_t_sec = pp.arrival_time.tv_sec;
            one_second_process();
            }
         }
      }
   }

Bit8 a_b[sizeof(struct adsl_rec)];  /* ADSL record buffer */
int n_b = 0;  /* Bytes already in a_b */

void interface_read(struct interface_info *pi)
{
   int n, r;

   for (n = 20; n != 0; --n) {
      if ((r = read(pi->fd, &a_b[n_b], sizeof(struct adsl_rec)-n_b)) <= 0) {
         if (errno == EAGAIN) {
            return;  /* No more data in pipe */
            }
         else {
            log_msg(LOG_ERR, 0, "adsl_read(): %s\n", strerror(errno));
            return;
            }
          }
      else {
         n_b += r;
         if (n_b != sizeof(struct adsl_rec))
            return;  /* Wait for rest of record */
         }

      handle_adsl_record(pi, (struct adsl_rec *)a_b);
      n_b = 0;
      }
   }

int init_interface(struct interface_info *pi)  /* Dag ADSL */
{
   int fd, flags;

   if (strcmp(pi->name,"stdin") == 0) {
      fd = dup(STDIN_FILENO);
      close(STDIN_FILENO);
      if (kb_enabled) {
         if (open("/dev/tty", O_RDONLY) != 0) {
            log_msg(LOG_ERR, 0,
               "adsl_open(%s): coudn't open /dev/tty, %s\n", 
               pi->name, strerror(errno));
            exit(1);
            }
         }
      }
   else if ((fd = open(pi->name, O_RDONLY)) < 0) {
      log_msg(LOG_ERR, 0, "adsl_open(%s): %s\n", pi->name, strerror(errno));
      return 0;  /* Fail */
      }

   if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
      log_msg(LOG_ERR, 0, "adsl_open(%s): fcntl get failed\n", pi->name);
      return 0;  /* Fail */
      }
   flags |= O_NONBLOCK;
   if (fcntl(fd, F_SETFL, flags) < 0) {
      log_msg(LOG_ERR, 0, "adsl_open(%s): fcntl set failed\n", pi->name);
      return 0;  /* Fail */
      }

   pi->fd = fd;
   return 1;  /* OK */
   }

unsigned int pkt_counts(int reset)
{
   if (reset) {
      npackets_org = pkts;  lostpackets_org = drops;
      }
   pkts = drops = 0L;  /* Get total packets seen and dropped */
   return 1;
   } 

/* ===== End of ADSL interface code ===== */

#elif DAG_DIRECT  /* !NF_CISCO_DATA && !CR_DATA && !LFAP_METER && !ADSL_DATA */

/* ===== Dag direct interface code ===== */

#include <fcntl.h>

#define MERGE_TEST  0

#define MAX_CONSEC_BADTS  25
   /* Shut down meter after this many bad timestamps */

#define DAG_BUF_SZ  262144  /* Records */
#define MX_TS_JUMP      10  /* Seconds */

int waiting_live_source = -1;

long long last_ts;

int select_us = 4*MN_SELECT_US;
long long select_time;
#define MX_NODATA_READS  100

/* Conversion functions: s and us from Dag (64-bit) timestamp */
#define DAGT_S(dts)   (long)((dts) >> 32)
#define DAGT_US(dts)  (long)((((dts) & 0xffffffffLL) * 1000000) >> 32)

#define US_DAGT(us)   (((long long)(us) << 32)/1000000LL)

void handle_dag_record(struct interface_info *pi)
{
   Bit32 ah;
   Bit32 vpvcmask = 0x0ffffff0;	 /* VPI 8 bits, VCI 16 bits */
   struct snap_hdr *sh;
   struct ether_hdr ehdr, *eh;
   Bit8 *p;  int plen;
   Bit16 lsap, ether_type;

   struct pkt pp;
   int r;

   if (pi->dag_type == TT_POS) {
#if MERGE_TEST == 2
      printf("%d", pi->nbr); /* */
#endif
      if (adj_reqd) {
         pp.Low.AdjType = pp.High.AdjType = AT_SONET;
         memset(&ehdr, 0, sizeof(struct ether_hdr));
         }
      r = handle_pkt(&pp, ntohs(pi->drp->d.pos.etype), 0, (Bit8 *)&ehdr,
         pi->drp->d.pos.pload, sizeof(pi->drp->d.pos.pload));
      }
   else if (pi->dag_type == TT_ATM) {
      ah = ntohl(pi->drp->d.atm.atm_hdr);
      ah &= vpvcmask;  ah >>= 4;  /* VPI:VCI */
      /* printf("ts=%qx (%u.%06u), atm=%06x (%08x) ",
         pi->drp->d.atm.ts,
         DAGT_S(pi->drp->d.atm.ts),DAGT_US(pi->drp->d.atm.ts),
         ah, ntohl(pi->drp->d.atm.atm_hdr)); /* */

      sh = &pi->drp->d.atm.sh;
      if ((lsap = ntohs(sh->lsap)) == 0xAAAA) {  /* SNAP packet */
         ether_type = ntohs(sh->etype);
#if MERGE_TEST == 2
          printf("%d", pi->nbr); /* */
#endif
          }
      else {
         ether_type = ntohs(sh->etype);
         /*      printf("\natm=%06x: ", 
            ah, lsap, ether_type);
         snp = (Bit8 *)&pi->drp->d.atm.sh;
         for (j = 0; j != 24; ++j) printf(" %02x", snp[j]);
         printf("\n"); */
         /* Will need to work out what eth_type/lsap should really be! */
         pp.p_len = 48;  /* Guess it's one ATM cell (!) */
         }

      if (adj_reqd) {
         pp.Low.AdjType = pp.High.AdjType = AT_AAL5;
         *(Bit16 *)&ehdr.dest[0] = *(Bit16 *)&ehdr.source[0] = 0;
         *(Bit32 *)&ehdr.dest[2] = *(Bit32 *)&ehdr.source[2] = ah; 
         }
      r = handle_pkt(&pp, ether_type, lsap, (Bit8 *)&ehdr,
         pi->drp->d.atm.pload, sizeof(pi->drp->d.atm.pload));
      }
   else if (pi->dag_type == TT_ETHER) {
#if MERGE_TEST == 2
      printf("%d", pi->nbr); /* */
#endif
      p = pi->drp->d.eth.pload;
      plen = sizeof(pi->drp->d.eth.pload);
      eh = &pi->drp->d.eth.eh;
      if ((ether_type = ntohs(eh->type)) <= 1500) {  /* 802.3 packet */
         sh = (struct snap_hdr *)p;
         if ((lsap = ntohs(sh->lsap)) == 0xAAAA) {  /* SNAP packet */
            ether_type = ntohs(sh->etype);
            p += sizeof(struct snap_hdr);
            plen -= sizeof(struct snap_hdr);
            }
         else ether_type = 0;  /* Other 802.2 packet */
         }
      else lsap = 0;  /* Blue book packet */

      pp.p_len = pi->drp->d.eth.wlen;  /* Overwritten if use_ip_len true */
      if (adj_reqd)
         pp.Low.AdjType = pp.High.AdjType = AT_ETHERNET;

      r = handle_pkt(&pp, ether_type, lsap, (Bit8 *)eh, p, plen);
      }

   /*  for (j = 8; j != 24; ++j) printf(" %02x", pi->drp->pload[j]);
	      printf(" ");  /* */

   if (r) {
      if (n_interfaces == 2) {  /* Assume they're two directions */
         if (pi->nbr == 2) {          /*        of a single link */          
            pp.Low.Interface = 2;  pp.High.Interface = 1;
            }
         else {
            pp.Low.Interface = 1;  pp.High.Interface = 2;
            }
         }
      else { /* Can only see one packet at a time */
         if (live_source) {
            pp.Low.Interface = pp.High.Interface = pi->nbr;
            }
         else {  /* Assume trace file is dagmerge of *two* Dag files */
            if (pi->drp->ts & 0x1ULL) {  /* LSB of timestamp */
               pp.Low.Interface = 2;  pp.High.Interface = 1;
               }
            else {
               pp.Low.Interface = 1;  pp.High.Interface = 2;
               }
            }
         }

      pp.arrival_time = pi->drp->ts;
      pkt_monitor(&pp);
      }
   }

int read_live_block(int);

void fmt_ts(char *t_buf, long long t)
{
   long long mt = t - meter_start_time;
   sprintf(t_buf, "%lu.%06lu", DAGT_S(mt),DAGT_US(mt));
   }

void bad_ts_msg(char *msg, int s, struct interface_info *pi,
   long long ts, long long tb, char *comment)
   /* ts = last good timestamp, tb = timestamp from buffer.
      diff (as printed) = tb - ts */
{
   int d = DAGT_S(tb - ts);
   char b_ts[30], b_tb[30];
   fmt_ts(b_ts, ts);  fmt_ts(b_tb, tb);
   log_msg(LOG_ERR, 0, "%s i/f %d: cells=%u/%u, %s-%s, diff=%ds%s",
      msg, s+1, pi->ncells,pi->icells, b_ts,b_tb, d, comment);
   }

int ts_seq_ok(long long ts, long long tb)
{
   long long td = tb-ts;
   if (td < 0) td = -td;
   return DAGT_S(td) <= MX_TS_JUMP;
   }

dag_merge()
{
   long long ts, mints;   /* Dag timestamp (64-bit) */
   int j, minj, tsec, n;
   struct interface_info *pi;
   char nbt[60];

   for (;;) {
      mints = pci[0]->card_ts;  minj = 0;
      for (j = 1; j != n_interfaces; ++j) {  /* Find min timestamp */
         ts = pci[j]->card_ts;
         if (ts <= mints) {
            mints = ts;  minj = j;
            }
         }
      pi = pci[minj];
      last_ts = mints;  /* Used by uptime() routines */

      if (--pi->sample_count == 0) {  /* Process only 1 pkt per SampleRate */
         pi->sample_count = pi->SampleRate;

         handle_dag_record(pi);

         tsec = DAGT_S(mints);
         if (tsec > last_t_sec) {
            last_t_sec = tsec;
            one_second_process();  /* NeTraMet housekeeping */
               /* Note this happens at the time seen by the interface,
                  we could get several seconds worth of data in a buffer */
            if (!live_source && --trace_count == 0) {
               waiting_live_source = minj;
               return;  /* Keep trace file loop alive */
               }
            }
         }
      ++pi->ipackets;  /* For global statistics */

      if (--pi->ncells == 0) {
         waiting_live_source = minj;
         return;  /* Keep outer block alive */
         }
      else {
         ++pi->drp;  ts = pi->drp->ts;
         if (ts_seq_ok(mints, ts)) {
            pi->card_ts = ts;  /* Timestamp looks OK */
	    }
         else {  /* Bad timestamp, look through buffer for a good one */
            for (n = pi->ncells;  pi->ncells != 0;  --pi->ncells, ++pi->drp) {
               if (ts_seq_ok(mints, ts = pi->drp->ts))
                  break;  /* Found a likely timestamp  */
               ++pi->badtspkts;
               }
            if (pi->ncells == 0) {  /* Ignore bad pkts at end of buffer */
               sprintf(nbt, ": %d pkts ignored (end of buffer)", n);
               bad_ts_msg("**T", minj, pi, mints, ts, nbt);
               pi->idrops += n;
               waiting_live_source = minj;
               return;  /* Keep outer block alive */
               }
            else {  /* Ignore bad pkts within buffer */
               sprintf(nbt, ": %d pkts ignored (within buffer)", n-pi->ncells);
               bad_ts_msg("*T*", minj, pi, mints, ts, nbt);
               pi->idrops += n-pi->ncells;
               pi->card_ts = ts;  /* Change timestamp */
               }
            }
         }
      }
   }

FILE *dag_data_log = NULL;

void log_dag_data(int cmd, int iface, int block,
   struct dag_rec *d_buf, int bytes)
{
   Bit8 *bp;
   int recs, j, k;

   if (dag_data_log == NULL)
      dag_data_log = fopen("dag_data", "w");

   switch (cmd) {
   case 1:  /* Log last 2 records plus trailing bytes */
      fprintf(dag_data_log, "i/f %d, block %d, last 2+ records:\n",
         iface, block);
      recs = (bytes + sizeof(struct dag_rec)-1)/sizeof(struct dag_rec);
      j = (recs-3)*sizeof(struct dag_rec);
      bp = (Bit8 *)d_buf + j;
      break;
   case 2:   /* Log first 3 records */
      fprintf(dag_data_log, "i/f %d, block %d, first 3 records:\n", iface, block);
      j = 0;
      bp = (Bit8 *)d_buf;
      break;
      }   
   for (k = 0; k != 3; ++k) {
      fprintf(dag_data_log, "   %02x", *bp++);
      for (++j; j < bytes; ) {
         fprintf(dag_data_log, " %02x", *bp++);
         if (++j % sizeof(struct dag_rec) == 0) break;
         if (j % 32 == 0) fprintf(dag_data_log, " ");
         }
      fprintf(dag_data_log, "\n");
      }
   fprintf(dag_data_log, "\n");
   }

void dag_reset(int i)
{  
  /* Sometimes, after running happily for hours or days, a Dag card
     can just stop writing cells into memory.  When this happens,
     DAG_DIRECT reads just keep returning 0 bytes + errno 11 (EAGAIN).
     A level 2 reset of the Dag card seems to get it working again!
        Nevil (with help from Stephen and David), Fri 11 Jan 02 */

   dagioaddr dagioa;
   int r;

   /* The card has a reset register at 0x88.  The relevant bitmap is:
         Bit 31: Trigger level 2 reset (auto clearing)
         Bit 30: Trigger level 1 reset (not auto clearing) */

   dagioa.offset = 0x88;  dagioa.val = 0x80000000;
   r = ioctl(pci[i]->fd, DAG_IOWRITE, &dagioa);

   log_msg(LOG_ERR, 0,"=== dag_reset(%d): ioctl returned %d", i,r);
   if (r != 0)
      log_msg(LOG_ERR, 0,"   error %d, %s", errno, strerror(errno));
   }

int read_live_block(int i)
{
   struct interface_info *pi = pci[i];
   int r, en, s, n, rr;
   long long prev_ts;
   char g_ts[30], b_ts[30];
   Bit8 *p8;

   errno = 0;  /* System call error variable */
   if ((r = read(pi->fd, pi->d_buf,
         sizeof(struct dag_rec)*DAG_BUF_SZ)) <= 0) {
      if (!live_source) {
         if (r == 0) {  /* EOF on trace file */
            show_memory();
            return 0;
            }
         }
      else if ((en = errno) != pi->last_errno) {
         log_msg(LOG_ERR, 0,"if_read(1): i/f %d, error %d (%s)",
            pi->nbr, en, strerror(en));
         pi->last_errno = errno;
         }

      /* No data.  This includes EAGAIN reads! */
      if (pi->empty > MX_NODATA_READS) {  /* Guess lowest ts for next pkt */
         pi->card_ts = last_ts + select_time;
         if (!pi->no_data_warned) {
            fmt_ts(g_ts, pi->card_ts);
            log_msg(LOG_WARNING, 0, "i/f %d, no data, ts set to %s",
               pi->nbr, g_ts);
            pi->no_data_warned = 1;
            }
         dag_reset(i);  /* Try to reset the Dag card */
         }
      r = 0;
      s = S_EMPTY;  /* No data in pipe */
      }
   else {  /* Data bytes read */
#if MERGE_TEST == 2
      printf("\nif_read(2): i/f %d, %d records read ",
         pi->nbr, r/sizeof(struct dag_rec));
#endif
      pi->no_data_warned = pi->last_errno = 0;  rr = r;

      n = r % sizeof(struct dag_rec);
      if (n != 0 && n != 32) {  /* Dag4 cards have 'half record' bug */
         fmt_ts(g_ts, pi->card_ts);  fmt_ts(b_ts, pi->drp->ts);
         log_msg(LOG_ERR, 0, "if_read(3): i/f %d, block %d,"
            " %d bytes, %d remain, %s -> %s <---", 
            pi->nbr, pi->nblocks, r, n, g_ts, b_ts);
         log_dag_data(2, i, pi->nblocks, pi->d_buf, rr);
            /*+ Log first 3 records */
         log_dag_data(1, i, pi->nblocks, pi->d_buf, rr);
            /*+ Log last 2 records plus trailing bytes */
         }
      pi->drp = pi->d_buf;
      pi->icells = pi->ncells = r / sizeof(struct dag_rec);
      if (r != 0) {
         if (pi->nblocks == 0 ||  /* Just getting started */
               pi->empty != 0 ||  /* Had some no_data reads */
               ts_seq_ok(pi->card_ts, pi->drp->ts)) {
            pi->card_ts = pi->drp->ts;  /* Timestamps OK between blocks */
            }
         else {  /* Bad timestamp at start of block */
            if ((n == 0 || n == 32) && pi->dag_type == TT_POS) {
               /* Recover from 'half record' glitch */
               p8 = (Bit8 *)pi->drp;  p8 += 32;
               if (ntohl(((struct dag_rec *)p8)->d.pos.slen) == 64) {
                  pi->drp = (struct dag_rec *)p8;
                  r -= n;  ++pi->idrops;
                  }
               else {
                  log_msg(LOG_ERR, 0, "if_read(4): i/f %d,"
                     " couldn't recover Dag4 'half record' !!!", pi->nbr);
                  r = 0;
                  }
	       }
            else r = 0;

            if (r == 0 && ts_seq_ok(pi->drp->ts, pi->drp[1].ts)) {
               fmt_ts(g_ts, pi->card_ts);  fmt_ts(b_ts, pi->drp->ts);
               log_msg(LOG_ERR, 0,"if_read(5): i/f %d, card_ts = %s, "
                  "first two timestamps OK (%s).  Resetting card_ts",
                  pi->nbr, g_ts,b_ts);
               pi->card_ts = pi->drp->ts;  r = rr;
               }

            if (r == 0) {  /* Couldn't recover */
               fmt_ts(g_ts, pi->card_ts);  fmt_ts(b_ts, pi->drp->ts);
               log_msg(LOG_ERR, 0,
                  "if_read(6): i/f %d, block %d, %d cells, %s -> %s, "
                     "bad ts at start of block !!!!!",
                  pi->nbr, pi->nblocks, pi->ncells, g_ts, b_ts);
               log_dag_data(2, i, pi->nblocks, pi->d_buf, rr);
                  /*+ Log first 3 records */
               if (++pi->consec_badts == MAX_CONSEC_BADTS) {
                  log_msg(LOG_ERR, 1,
                     "Too many bad timestamps, shutting down");
                  }
               pi->idrops += pi->icells;
               ++pi->badtspkts;
               }
            else pi->consec_badts = 0;
            }
         ++pi->nblocks;  pi->tcells += pi->ncells;
         }
      s = pi->ncells == DAG_BUF_SZ ? S_FULL : S_NORMAL;
#if MERGE_TEST >= 1
      fmt_ts(b_ts, pi->card_ts);
      printf("\nif_read(7): i/f %d, ts=%s, %d ccells, %d nblocks\n", 
         pi->nbr, b_ts, pi->ncells, pi->nblocks);
#endif
      }
   
   switch (pi->last_s) {
   case S_NORMAL:
      switch (s) {
      case S_NORMAL:
         break;
      case S_FULL:
         pi->full = 1;
         break;
      case S_EMPTY:
         pi->empty = 1;
         break;
         }
      break;
   case S_FULL:
      switch (s) {
      case S_NORMAL:
         if (pi->full > pi->mx_full) pi->mx_full = pi->full;
         pi->full = 0;  
         break;
      case S_EMPTY:
         if (pi->full > pi->mx_full) pi->mx_full = pi->full;
         pi->full = 0;  pi->empty = 1;
         break;
      case S_FULL:
         ++pi->full;
         break;
         }
      break;
   case S_EMPTY:
      switch (s) {
      case S_NORMAL:
         if (pi->empty > pi->mx_empty) pi->mx_empty = pi->empty;
         pi->empty = 0;  
         break;
      case S_FULL:
         if (pi->empty > pi->mx_empty) pi->mx_empty = pi->empty;
         pi->empty = 0;  pi->full = 1;
         break;
      case S_EMPTY:
         ++pi->empty;
         break;
         }
      break;
      }
   pi->last_s = s;
   return r;
   }

void init_live_sources(void)
{
#define STARTUP_TRIES  100
   int j,n,b;
   for (j = 0; j != n_interfaces; ++j)
      pci[j]->nblocks = 0;
   for (n = 0; n != STARTUP_TRIES; ++n) {
      for (j = 0; j != n_interfaces; ++j) {  /* Get block for each i/f */
         read_live_block(j);
         }
      for (b = j = 0; j != n_interfaces; ++j)
         if (pci[j]->nblocks != 0) ++b;
      if (b == n_interfaces) break;
      }

   if (n == STARTUP_TRIES) log_msg(LOG_ERR, 98,
      "init_live_sources(): couldn't get data from interface(s)");

   meter_start_time = pci[0]->card_ts;  /* Set to min Dag timestamp */
   for (j = 1; j != n_interfaces; ++j) {
      if (pci[j]->card_ts <= meter_start_time)
         meter_start_time = pci[j]->card_ts;
      }
   last_ts = meter_start_time;
   last_t_sec = DAGT_S(last_ts);

   printf("init_live_sources(): first blocks read\n");
   fflush(stdout);
   }

int init_interface(struct interface_info *pi)  /* Dag direct */
{
   char buf[150], *crl_info_string;
   int len, ifd, flags;

   if (strncmp(pi->name, "/dev/", 5) != 0) {
      live_source = 0;  /* User wants to read trace file(s) */
      trace_count = trace_interval;
      crl_n_count = 0;

      sprintf(buf, "%d  -i%s (Dag trace file)",
         trace_interval,  /* NeMaC uses this for its collection interval */
         pi->name);
      len = strlen(buf);
      crl_info_string = (char *)malloc(len+1);
      strcpy(crl_info_string, buf);
      crl_info = crl_info_string;
         /* NeMaC writes this to a #CoralReef record */
      }

   if ((ifd = open(pi->name, O_RDONLY)) < 0) {
      log_msg(LOG_ERR, 0,
         "iif: open(%s): %s\n", pi->name, strerror(errno));
      return 0;  /* Fail */
      }

   if (live_source) {
      if ((flags = fcntl(ifd, F_GETFL, 0)) < 0) {
         log_msg(LOG_ERR, 0, "iif: getfl(%s): fcntl get failed\n", pi->name);
         return 0;  /* Fail */
         }
      flags |= O_NONBLOCK;
      if (fcntl(ifd, F_SETFL, flags) < 0) {
         log_msg(LOG_ERR, 0, "iif: nonblck(%s): fcntl set failed\n", pi->name);
         return 0;  /* Fail */
         }
      }

   if ((pi->d_buf = (struct dag_rec *)malloc(
         sizeof(struct dag_rec)*DAG_BUF_SZ)) == NULL) {
      close(ifd);
      log_msg(LOG_ERR, 99,
         "Not enough memory for NeTraMet %s buffer!", pi->name);
      }
   pi->last_errno = 0;

   pi->fd = ifd;
   return 1;  /* OK */
   }

unsigned int pkt_counts(int reset)
{
   int j;
   struct interface_info *pi;

   if (reset) {
      npackets_org = pkts;  lostpackets_org = drops;
      }
   pkts = drops = 0L;  /* Get total packets seen and dropped */
   for (j = 0; j != n_interfaces; ++j) {
      pi = pci[j];
      pkts += pi->ipackets;  drops += pi->idrops;
      }
   npackets = pkts - npackets_org;
   lostpackets = drops - lostpackets_org;
   return 1;
   } 

/* ===== End of Dag direct interface code ===== */

#else  /* !NF_CISCO_DATA && !CR_DATA && !LFAP_METER 
             && !ADSL_DATA && !DAG_DIRECT */

/* ===== Interface code using libpcap to get packet headers ===== */

#define PCAPWAITMS  50  /* ms to wait on packet-header-block reads */

struct ether_hdr null_e_hdr = {  /* For PPP callback */
   0,0,0,0,0,0, 0,0,0,0,0,0, 0
   };

/* ----- Following fddi code is from tcpdump's print-fddi.c ----- */

#ifdef __GNUC__  /* These defines from interface.h .. */
#define inline __inline
#ifndef __dead
#define __dead volatile
#endif
#else
#define inline
#define __dead
#endif

#include <fddi.h>

/*
 * Some FDDI interfaces use bit-swapped addresses.
 */
#if defined(ultrix) || defined(__alpha)
int	fddi_bitswap = 0;
#else
int	fddi_bitswap = 1;
#endif

/*
 * FDDI support for tcpdump, by Jeffrey Mogul [DECWRL], June 1992
 *
 * Based in part on code by Van Jacobson, which bears this note:
 *
 * NOTE:  This is a very preliminary hack for FDDI support.
 * There are all sorts of wired in constants & nothing (yet)
 * to print SMT packets as anything other than hex dumps.
 * Most of the necessary changes are waiting on my redoing
 * the "header" that a kernel fddi driver supplies to bpf:  I
 * want it to look like one byte of 'direction' (0 or 1
 * depending on whether the packet was inbound or outbound),
 * two bytes of system/driver dependent data (anything an
 * implementor thinks would be useful to filter on and/or
 * save per-packet, then the real 21-byte FDDI header.
 * Steve McCanne & I have also talked about adding the
 * 'direction' byte to all bpf headers (e.g., in the two
 * bytes of padding on an ethernet header).  It's not clear
 * we could do this in a backwards compatible way & we hate
 * the idea of an incompatible bpf change.  Discussions are
 * proceeding.
 * * Also, to really support FDDI (and better support 802.2
 * over ethernet) we really need to re-think the rather simple
 * minded assumptions about fixed length & fixed format link
 * level headers made in gencode.c.  One day...
 *
 *  - vj
 */

#define FDDI_HDRLEN (sizeof(struct fddi_header))

static u_char fddi_bit_swap[] = {
	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};

/* Extract src, dst addresses */
static inline void extract_fddi_addrs(
   const struct fddi_header *fddip, 
   unsigned char *fsrc, unsigned char *fdst)
{
	register int i;

	if (fddi_bitswap) {
		/*
		 * bit-swap the fddi addresses (isn't the IEEE standards
		 * process wonderful!) then convert them to names.
		 */
		for (i = 0; i < 6; ++i)
			fdst[i] = fddi_bit_swap[fddip->fddi_dhost[i]];
		for (i = 0; i < 6; ++i)
			fsrc[i] = fddi_bit_swap[fddip->fddi_shost[i]];
	}
	else {
		memcpy(fdst, fddip->fddi_dhost, 6);
	        memcpy(fsrc, fddip->fddi_shost, 6);
	}
}

/* ----- End of fddi code from tcpdump's print-fddi.c ----- */

#define get_short(a)  (a[0] << 8 | a[1])

void fddi_callback(struct interface_info *user,
   struct pcap_pkthdr *h, u_char *p)
{
   const struct fddi_header *fp = (struct fddi_header *)p;
   int caplen = h->caplen;
   struct ether_hdr ehdr;
#ifdef COPY_STRUCTS
   struct llc llc;
#else
   struct llc *llc;
#endif
   struct pkt pp;
   unsigned int ether_type, lsap;

   if (user->if_delta_set == 0) {  /* First timestamp for this interface */
      user->if_time_delta = h->ts;
      tv_sub(user->if_time_delta, meter_start_time);
      user->if_delta_set = 1;
      }

   if (--user->sample_count == 0) {  /* Process only 1 pkt per SampleRate */
      user->sample_count = user->SampleRate;

      if (caplen < FDDI_HDRLEN) return;  /* Too short! */

      /* Get the FDDI addresses into a canonical form */
      if (adj_reqd)  /* Don't do this unless we have to! */
         extract_fddi_addrs(fp, ehdr.source, ehdr.dest);

      if ((fp->fddi_fc & FDDIFC_CLFF) == FDDIFC_LLC_ASYNC) {
         caplen -= FDDI_HDRLEN;
         if (caplen < sizeof(struct llc)) return;  /* Too short! */
         p += FDDI_HDRLEN;
#ifdef COPY_STRUCTS  /* tcpdump does this; don't know why */
         memcpy((char *)&llc, (char *)p, sizeof(llc));
         lsap = llc.dsap << 8 | llc.ssap;
         if (lsap == 0xAAAA) {  /* SNAP packet */
            ether_type = get_short(llc.ctl.snap_ether.snap_ethertype);
            p += sizeof(struct llc);
            }
         else ether_type = 0;  /* Other 802.2 packet */
#else  /* This works, at least for Solaris 2.4 */
         llc = (struct llc *)p;
         lsap = llc->dsap << 8 | llc->ssap;
         if (lsap == 0xAAAA) {  /* SNAP packet */
            ether_type = get_short(llc->ctl.snap_ether.snap_ethertype);
            p += sizeof(struct llc);
            }
         else ether_type = 0;  /* Other 802.2 packet */
#endif
         pp.p_len = h->len;  /* Bug fix from Dylan Hall, 31 May 00 */
         if (handle_pkt(&pp, ether_type,lsap, (unsigned char *)&ehdr,
               p, caplen - sizeof(struct llc))) {
            pp.Low.Interface = pp.High.Interface = user->nbr;
               /* NETFLOW uses Low.Interface for ntm_interface */
            pp.Low.AdjType = pp.High.AdjType = AT_FDDI;
            pp.arrival_time = h->ts;  /* struct timeval */
            tv_sub(pp.arrival_time, user->if_time_delta);
            pkt_monitor(&pp);

            if (pp.arrival_time.tv_sec != last_t_sec) {
               last_t_sec = pp.arrival_time.tv_sec;
               one_second_process();
	       }
	    }
         }
      }
   }

void ether_callback(struct interface_info *user,
   struct pcap_pkthdr *h, u_char *p)
{
   struct ether_hdr *ethp;
   struct llc *llcp;
   struct pkt pp;
   unsigned int ether_type, lsap;
   int len, j;

   if (user->if_delta_set == 0) {  /* First timestamp for this interface */
      user->if_time_delta = h->ts;
      tv_sub(user->if_time_delta, meter_start_time);
      user->if_delta_set = 1;
      }

   if (--user->sample_count == 0) {  /* Process only 1 pkt per SampleRate */
      user->sample_count = user->SampleRate;

      ethp = (struct ether_hdr *)p;
      ether_type = ntohs(ethp->type);
      p += sizeof(struct ether_hdr);
      if (ether_type <= 1500) {  /* 802.3 packet */
         llcp = (struct llc *)p;
         lsap = llcp->dsap << 8 | llcp->ssap;
         if (lsap == 0xAAAA) {  /* SNAP packet */
            ether_type = get_short(llcp->ctl.snap_ether.snap_ethertype);
            p += sizeof(struct llc);
            }
         else ether_type = 0;  /* Other 802.2 packet */
         }
      else lsap = 0;  /* Blue book packet */

      pp.p_len = h->len;  /* Bug fix from Dylan Hall, 31 May 00 */
      if (handle_pkt(&pp, ether_type,lsap, (unsigned char *)ethp,
            p, h->caplen - sizeof(struct ether_hdr))) {
         pp.Low.Interface = pp.High.Interface = user->nbr;
            /* NETFLOW uses Low.Interface for ntm_interface */
         pp.Low.AdjType = pp.High.AdjType = user->adjtype;
         pp.arrival_time = h->ts;  /* struct timeval */
         tv_sub(pp.arrival_time, user->if_time_delta);
         pkt_monitor(&pp);

         if (pp.arrival_time.tv_sec != last_t_sec) {
            last_t_sec = pp.arrival_time.tv_sec;
            one_second_process();
            }
         }
      }
   }

void ppp_callback(struct interface_info *user,
   struct pcap_pkthdr *h, u_char *p)
{
   struct ether_hdr *ethp;
   struct llc *llcp;
   struct pkt pp;
   unsigned int ether_type, lsap;
   int len, j;

   if (user->if_delta_set == 0) {  /* First timestamp for this interface */
      user->if_time_delta = h->ts;
      tv_sub(user->if_time_delta, meter_start_time);
      user->if_delta_set = 1;
      }

   if (--user->sample_count == 0) {  /* Process only 1 pkt per SampleRate */
      user->sample_count = user->SampleRate;

      pp.p_len = h->len;  /* Bug fix from Dylan Hall, 31 May 00 */
      if (handle_pkt(&pp, 0x0800,0,  /* Assume it's an IP packet */
            (unsigned char *)&null_e_hdr, p, h->caplen)) {
         pp.Low.Interface = pp.High.Interface = user->nbr;
            /* NETFLOW uses Low.Interface for ntm_interface */
         pp.Low.AdjType = pp.High.AdjType = AT_PPP;
         pp.arrival_time = h->ts;  /* struct timeval */
         tv_sub(pp.arrival_time, user->if_time_delta);
         pkt_monitor(&pp);

         if (pp.arrival_time.tv_sec != last_t_sec) {
            last_t_sec = pp.arrival_time.tv_sec;
            one_second_process();
            }
         }
      }
   }

void interface_read(struct interface_info *pi)
{
   pcap_read(pi->pd, -1, pi->callback, (u_char *)pi);
   }

int init_interface(struct interface_info *pi)  /* libpcap */
{
   char *interface, errbuf[PCAP_ERRBUF_SIZE];
   pcap_t *pd;
   int type;
#define PCAPWAITMS  50
   if (pi->name[0] == '\0')
      if (!(interface = pcap_lookupdev(errbuf))) {
         log_msg(LOG_ERR, 0, "pcap_lookupdevice(): %s\n",errbuf);
         return 0;  /* Fail */
         }
      else strcpy(pi->name,interface);
   if ((pd = pcap_open_live(pi->name, SNAPSIZE, 1,
         PCAPWAITMS, errbuf)) == NULL) {
      log_msg(LOG_ERR, 0, "pcap_open_live(%s): %s\n", pi->name,errbuf);
      return 0;  /* Fail */
      }
   pi->pd = pd;  pi->fd = pd->fd;

   switch (type = pcap_datalink(pd)) {
   case DLT_EN10MB:
   case DLT_IEEE802:
#if CYGWIN32
   case DLT_EN100MB:
#endif
      pi->adjtype = AT_ETHERNET;
      pi->callback = (pcap_handler)ether_callback;
      break;
#if CYGWIN32
   case DLT_PPP_WIN32:  /* WinDump dummies up a MAC header (!) */
      pi->adjtype = AT_PPP;
      pi->callback = (pcap_handler)ether_callback;
      break;
#endif
   case DLT_RAW:
   case DLT_SLIP_BSDOS:
   case DLT_PPP_BSDOS:
      pi->adjtype = AT_PPP;
      pi->callback = (pcap_handler)ppp_callback;
      break;
   case DLT_FDDI:
      pi->adjtype = AT_FDDI;
      pi->callback = (pcap_handler)fddi_callback;
      break;
   default:
      log_msg(LOG_ERR, 0, "pcap bad link type 0x%x!\n", type);
      pcap_close(pd);
      return 0;  /* Fail */
      }
   return 1;  /* OK */
   }

unsigned int pkt_counts(int reset)
{
   int j;
   struct interface_info *pi;
   struct pcap_stat ps;
   char errbuf[PCAP_ERRBUF_SIZE];

   if (reset) {
      npackets_org = pkts;  lostpackets_org = drops;
      }
   pkts = drops = 0L;  /* Get total packets seen and dropped */
   for (j = 0; j != n_interfaces; ++j) {
      pi = pci[j];
      if (pcap_stats(pi->pd,&ps)) {
         log_msg(LOG_ERR, 0, "pcap_stats(%d): %s\n",pi->nbr,errbuf);
         return 0;
         }
      pkts += ps.ps_recv;  drops += ps.ps_drop;
      pi->ipackets = ps.ps_recv;  pi->idrops = ps.ps_drop;
      }
   npackets = pkts - npackets_org;
   lostpackets = drops - lostpackets_org;
   return 1;
   } 

#endif  /* !NETFLOW */

/* ===== End of libpcap interface code ===== */

unsigned long iface_info(int if_nbr, int which)
{
   struct interface_info *pi;
   pi = pci[if_nbr-1];
   if (which == IFACE_LOST) 
      return pi->idrops + pi->noflowpackets;
   else if (which == IFACE_RATE)
      return pi->SampleRate;
   else return 0;
   }

void bump_noflowpkts(int if_nbr)
{
   ++noflowpackets;
   ++pci[if_nbr-1]->noflowpackets;  /* 1-org */
   }


void start_uptime_clock(void)
{
#if !DAG_DIRECT
   struct timeval tv;
# if CR_DATA
   tv = last_ts;  /* Use time from trace file */
# else
   gettimeofday(&tv, (struct timezone *)0);
# endif
   meter_start_time = tv;
#endif
   }

Bit32 uptime_s(void) /* seconds*/
{
#if DAG_DIRECT
   long long tsec = last_ts - meter_start_time;
   return DAGT_S(tsec);  
#else
   struct timeval tv;
# if CR_DATA
   tv = last_ts;  /* Use time from trace file */
# else
   gettimeofday(&tv, (struct timezone *)0);
# endif
   tv_sub(tv, meter_start_time);
   return tv.tv_sec;
#endif
   }

Bit32 uptime_cs(void) /* centiseconds */
{
#if DAG_DIRECT
   long long tsec = last_ts - meter_start_time;
   Bit32 s = DAGT_S(tsec);
   Bit32 us = DAGT_US(tsec);
   return s*100 + us/10000;
#else
   struct timeval tv;
# if CR_DATA
   tv = last_ts;  /* Use time from trace file */
# else
   gettimeofday(&tv, (struct timezone *)0);
# endif
   tv_sub(tv, meter_start_time);
   return tv.tv_sec*100 + tv.tv_usec/10000;
#endif
   }

#if DAG_DIRECT
double centiseconds(long long nt)
{
   long long t = nt - meter_start_time;
   return ((double)DAGT_S(t))*100.0 + ((double)DAGT_US(t))/10000.0;
#else
double centiseconds(struct timeval nt)
{
   tv_sub(nt, meter_start_time);
   return ((double)nt.tv_sec)*100.0 + ((double)nt.tv_usec)/10000.0;
#endif
   }

#if DAG_DIRECT
double microseconds(long long nt)
{
   long long t = nt - meter_start_time;
   return ((double)DAGT_S(t))*1000000.0 + (double)DAGT_US(t);
#else
double microseconds(struct timeval nt)
{
   tv_sub(nt, meter_start_time);
   return ((double)nt.tv_sec)*1000000.0 + (double)nt.tv_usec;
#endif
   }

void show_meter_time()
{
   char msg[60], *ts;
   ts = ctime((time_t *)&meter_start_time);
   sprintf(msg,"%lu seconds since %c%c%c%c:%c%c",
      uptime_s(),
      ts[11],ts[12],ts[14],ts[15], ts[17],ts[18]);
   display_msg(0,msg);
   }

extern int errno;
int snmp_dump_packet = 0;
#define MXINTERFACES 4

char interface_name[SZ_IFNAME*MXINTERFACES+1] = "";
   /* Interface name (used by met_vars.c) */
char r_community[SZ_IFNAME+1], w_community[SZ_IFNAME+1];

/* Help screen.  Add any new command line options here */
void print_help(void)
{
   fprintf(stderr, "\nUsage: NeTraMet [OPTION]...\n");
   fprintf(stderr, 
      "A meter for network traffic flows:\n\n"
      /* "  -d\n" */
      "  -i IFN \t Specify interface to monitor (maximum of 4)\n"
      "  -k     \t Disable keyboard input processing\n"
      "  -l     \t Use length field from IP headers\n"
      "  -m PRT \t Use port PRT to communicate with readers/managers\n"
#if NF_OCX_BGP
      "  -o     \t Use 'owner' ASNs instead of 'next-hop'\n"
#endif
#ifdef LFAP_METER
      "  -p1    \t Display flow info received from a CCE\n"
      "  -p2    \t Display messages sent between CCE and FAS (LfapMet)\n"
      "  -p4    \t Display raw messages (hexdump) received from CCE\n"
      "  -p8    \t log flow info received from a CCE in file "
                   "'LfapMet.logflowstats'\n"
      "  -p16   \t log messages sent between CCE and FAS in file "
                   "'LfapMet.logmessages'\n"
      "  -p32   \t log raw messages (hexdump) received from CCE in file"
                   "'LfapMet.loghexmessages' \n\t\t (useful for debugging)\n"
      "         \t -p options can be combined, e.g:\n"
      "         \t -p63 enables all\n"
      "         \t -p9 enables display & logging of flow info\n"
      "  -R RSC \t Set snmp read community for CCEs\n"
#endif
      "  -r RSC \t Set snmp read community to RSC\n"
      "  -s     \t Disable screen display\n"
      "  -w WSC \t Set snmp write community to WSC\n"
      "  -D     \t Run NeTraMet as a daemon (superset of -k -s)\n"
      "\n     \t\t Memory allocation (defaults)\n"
      "  -f fff \t fff flows (%lu)\n"
      "  -u rrr \t rrr rUles (%lu)\n"
      "  -a aaa \t aaa pAcket data blocks (%lu)\n"
      "  -t ttt \t ttt IP sTreams (%lu)\n"
      "  -b bbb \t bbb stream data Blocks (%lu)\n"
      "  -v ddd \t ddd distributions (%lu)\n"
      "  -e eee \t eee distrib Events (%lu)\n"
      "\n"
      "A meter reader can collect flow data from many meters, and each meter\n"
      "may have its data retrieved by serveral meter readers.  Traffic flows\n"
      "of interest are defined by user-specified sets of rules\n"
      "\n"
      "For more information see the NeTraMet Reference Manual and User Guide\n"
      "\n"
      "Report bugs to netramet@auckland.ac.nz\n",
      mxflows, mxrules, mxstr_blocks, mxstreams, 
      mxdistribs, mxdistevents, mxpktdatas);
   }

void check_struct_sizes(void)
{
   /* Using Bit16 and Bit32 instead of arrays of Bit8 requires
      us to keep these things correctly aligned in memory.
      gcc pads structures to multiples of 4 bytes, but other
      compilers might not, hence this check */
#if 0
   struct stream s;

   printf("size(pktsnap.h):\n"
      "   address=%d, pkt_key=%d\n",
      sizeof(union address), sizeof(struct pkt_key));
   printf("size(flowhash.h):\n"
      "   stream_name=%d, key=%d, flow_key=%d, rule=%d\n",
      sizeof(struct stream_name), sizeof(struct key),
      sizeof(struct flow_key), sizeof(struct rule));

   printf("   struct stream: offset of sfn = %d\n",
      (IntFromPtr)&s.sfn - (IntFromPtr)&s.next_hc);

   if (sizeof(struct pkt_key) % 4 != 0 ||
         sizeof(struct stream_name) % 4 != 0 ||
         sizeof(struct key) % 4 != 0 ||
         sizeof(struct flow_key) % 4 != 0 ) {
      printf("sizeof(struct) not a multiple of 4 bytes <<<<<\n");
      exit(-1);
      }
#endif
   }

int main(int argc, char *argv[])
{
   int arg,c, sd, s_n, daemon, bgp_result, k;
   char *ap;
   struct sockaddr_in me;
   struct utsname name;
   int dag_type = 0;

   if (argc < 2) {  /* Help Screen when called with no args */
      print_help();
      check_struct_sizes();
      exit(-1);
      }

   mxflows = DFMXFLOWS;  mxrules = DFMXRULES;
   mxstreams = DFMXSTREAMS;  mxstr_blocks = DFMXSTRBLOCKS;
   mxdistribs = DFMXDISTRIBS;  mxdistevents = DFMXDISTEVENTS;
   mxpktdatas = DFMXPKTDATS;

   signal(SIGINT, sigint_handler);
   signal(SIGTERM, sigint_handler);

#if CR_DATA
   use_ip_length = 1;  /* Use IP header lengths */
   coral_set_api(CORAL_API_CELL);  /* Allows config file with proto rules */
#elif DAG_DATA || DAG_DIRECT
   use_ip_length = 1;  /* Use IP header lengths */
#endif

   printf("%s\n",version_descr);
   display_enabled = kb_enabled = 1;  /* Enabled by default */
   daemon = 0;
   /* Default (CMU) communities are:  0 = "public", 1 = "proxy",
	 2 = "private", 3 = "regional", 4 = "core"
      We only allow "public" and "private" by default */
   communities[1] = communities[3] = communities[4] = "";
   for (n_interfaces = 0;  n_interfaces != MXINTERFACES; ++n_interfaces)
      pci[n_interfaces] = NULL;
   s_n = 1;  n_interfaces = 0;
   for (c = 0, arg = 1; arg < argc; ++arg) {
      if (argv[arg][0] == '-') {
         ap = argv[arg]+2;
         switch (argv[arg][1]) {
         case 'a':
            if (*ap == '\0') ap = argv[++arg];
            mxpktdatas = atoi(ap);
            break;
	 case 'b':
	    if (*ap == '\0') ap = argv[++arg];
	    mxstr_blocks = atoi(ap);
	    break;
	 case 'd':
	    snmp_dump_packet++;
	    break;
         case 'e':
            if (*ap == '\0') ap = argv[++arg];
            mxdistevents = atoi(ap);
            break;
     	 case 'f':
	    if (*ap == '\0') ap = argv[++arg];
	    mxflows = atoi(ap);
	    break;
#if DAG_DIRECT
         case 'g':  /* -g dag type, default = 0 (atm) */
            if (*ap == '\0') ap = argv[++arg];
            dag_type = atoi(ap);
            if (dag_type < TT_ATM || dag_type > TT_POS)
               log_msg(LOG_ERR, 1,
                  "Dag type (-g) not 0 (ATM), 1 (Ether) or 2 (PoS)\n");
            break;
#endif
	 case 'i':  /* -i name of interface to monitor */
	    if (*ap == '\0') ap = argv[++arg];
            pci[n_interfaces] = (struct interface_info *)calloc(
               sizeof(struct interface_info), 1);
            strncpy(pci[n_interfaces]->name,ap,SZ_IFNAME);
            pci[n_interfaces]->name[SZ_IFNAME] = '\0';
#if DAG_DIRECT
            pci[n_interfaces]->dag_type = dag_type;
#endif
            pci[n_interfaces]->nbr = n_interfaces+1;  /* 1-org */
            ++n_interfaces;
	    break;
	 case 'k':
	    kb_enabled = 0;  /* -k to disable keyboard */
	    break;
      	 case 'l':  /* Use 'Total Length' field from IP datagrams */
	    use_ip_length = 1;
	    break;
	 case 'm':
	    if (*ap == '\0') ap = argv[++arg];
            au_snmp_port = atoi(ap);
            break;
 	 case 'n':
 	    if (*ap == '\0') ap = argv[++arg];
            if (ap != NULL) s_n = atoi(ap);
            else s_n = 1;
            if (s_n < 1) s_n = 1;
 	    break;
#if NF_OCX_BGP
	 case 'o':
	    use_owner_asns++;
	    break;
#endif
#ifdef LFAP_METER
 	 case 'p':
 	    if (*ap == '\0') ap = argv[++arg];
		if (ap!=NULL) lfap_info_level = atoi(ap);
		else lfap_info_level = LFAP_INFLVL_FLOW|LFAP_INFLVL_MSG;
            if (lfap_info_level < 0) lfap_info_level = 0;
/*          else if (lfap_info_level > 15) lfap_info_level = 15; */
 	    break;
#endif
	 case 'r':
	    if (*ap == '\0') ap = argv[++arg];  /* -r to set read community */
            strncpy(r_community,ap,SZ_IFNAME);
            r_community[SZ_IFNAME] = '\0';
 	    communities[0] = r_community;
	    break;
	 case 's':
	    display_enabled = 0;  /* -s to disable screen */
	    break;
         case 't':
            if (*ap == '\0') ap = argv[++arg];
            mxstreams = atoi(ap);
            break;
	 case 'u':
	    if (*ap == '\0') ap = argv[++arg];
	    mxrules = atoi(ap);
	    break;
 	 case 'v':
            if (*ap == '\0') ap = argv[++arg];
            mxdistribs = atoi(ap);
	    break;
	 case 'w':
	    if (*ap == '\0') ap = argv[++arg];  /* -w to set write community */
            strncpy(w_community,ap,SZ_IFNAME);
            w_community[SZ_IFNAME] = '\0';
 	    communities[2] = w_community;
	    break;
#if CR_DATA
         case 'C':  /* CoralReef option (other than source) */
            if (*ap == '\0') ap = argv[++arg];
            if (coral_config_command(ap) < 0)
                exit(-1);
            break;
#endif
         case 'D':
	    if (!trace_interval) {
               display_enabled = kb_enabled = 0;
               daemon++;
	       }
            else log_msg(LOG_WARNING, 0,
               "-D options reqires -T0 to run as daemon!");
            break;
#if CR_DATA || DAG_DIRECT
         case 'N':  /* Stop after N*T s of tracefile.  0 => stop at EOF */
            if (*ap == '\0') ap = argv[++arg];
            crl_n_max = atoi(ap);
            break;
#endif
#ifdef LFAP_METER
 	 case 'R':
 	    if (*ap == '\0') ap = argv[++arg];
            strncpy(lfap_community,ap,SZ_IFNAME);
            lfap_community[SZ_IFNAME] = '\0';
 	    break;
#endif
#if CR_DATA
         case 'S':  /* CoralReef source */
            if (*ap == '\0') ap = argv[++arg];
            pci[n_interfaces] = (struct interface_info *)calloc(
               sizeof(struct interface_info), 1);
            strncpy(pci[n_interfaces]->name,ap,SZ_IFNAME);
            pci[n_interfaces]->name[SZ_IFNAME] = '\0';
            pci[n_interfaces]->nbr = n_interfaces+1;  /* 1-org */
            pci[n_interfaces]->crl_src = coral_new_source(ap);
            if (pci[n_interfaces]->crl_src == NULL) {
               printf("Failed to open coral source %s\n", ap);
               exit(-1);
               }
            ++n_interfaces;
            break;
#endif
#if CR_DATA || DAG_DIRECT
         case 'T':  /* Collection interval (seconds) for trace file.
                       T0 (default) implies 'use live source(s)' */
            if (*ap == '\0') ap = argv[++arg];
	    trace_interval = atoi(ap);
            break;
#endif
	 default:
	    log_msg(LOG_ERR, 1, "Invalid option: -%c\n", argv[arg][1]);
	    }
	 }
      else log_msg(LOG_ERR, 1, "Option not preceded by -\n");
      }

   if (daemon) {  /* User asked to run NeTraMet as daemon */
      switch (k = fork()) {
      case 0:  /* Child (left running on it's own) */
         break;
      case -1:
         fprintf(stderr, "Fork failed!!\n");
         exit(1);
      default:  /* Parent */
         exit(0);
         }
      setsid();  /* Move into a new session */
      }

#if NF_OCX_BGP
    InitSubnet();
    bgp_result = read_bgp_file("bgp.txt");
    if (bgp_result) {
       log_msg(LOG_ERR, 1,
          "read_bgp_file failed, bgp_result=%d\n", bgp_result);
       }
#endif

   /* Set up connections */
   sd = socket(AF_INET, SOCK_DGRAM, 0);
   if (sd < 0)
      log_msg(LOG_ERR, 1, "Couldn't get socket");
   me.sin_family = AF_INET;
   me.sin_addr.s_addr = INADDR_ANY;
   me.sin_port = htons(SNMP_PORT);
   if (bind(sd, (struct sockaddr *)&me, sizeof(me)) != 0)
      log_msg(LOG_ERR, 2,  /* SNMP_PORT defined in snmplib/snmp.h */
         "Couldn't bind port %d: %s", SNMP_PORT,strerror(errno));
   if (n_interfaces == 0) {
      pci[0] = 
         (struct interface_info *)calloc(sizeof(struct interface_info),1);
      pci[0]->nbr = n_interfaces = 1;
      }
   for (c = 0; c != n_interfaces; ++c)
      pci[c]->SampleRate = pci[c]->sample_count = s_n;

#ifdef LFAP_METER
   if (!start_lfap(&name)) return 0;  /* Fail */
#endif

   init_monitor(n_interfaces);
   init_snmp();
   uname(&name);
   ap = interface_name;  k = sizeof(interface_name);
   for (c = 0; c != n_interfaces; ++c) {
      if (!init_interface(pci[c])) exit(3);
      if (strlen(pci[c]->name)+3 < k) {
         if (c != 0) ap = strmov(ap,", ");
         ap = strmov(ap,pci[c]->name);
         k -= strlen(pci[c]->name)+2;
         }
      }
   *ap = '\0';
   printf("Running on %s", name.nodename);
#if defined(NETFLOW)
   printf(", port(s) %s\n", interface_name);
#else
   printf(", interface(s) %s\n", interface_name);
#endif
fflush(stdout);
   setuid(getuid());
   receive(sd,n_interfaces);
   }

void zero_pkt_stats()
{
   struct tms tbuf;
   srealtime = realtime_org = times(&tbuf);
   sproctime = proctime_org = tbuf.tms_utime+tbuf.tms_stime;
      /* tms_cutime, tms_cstime give times for child processes */

   pc_noflowpkts = noflowpackets;
   pkt_counts(1);  /* Reset origins */
   min_idle1000 = 1000L;
   stats_time = spackets = 0L;
   max_pkt_rate = 0;
   clear_pkt_stats = 0;
   }

int chart_interface = 0;  /* Use SampleRate from first interface */
unsigned int bkgi;  /* Seconds before next run of backgound process */
#define BKG_INTERVAL  30  /* Seconds */
int gci;

#if CR_DATA || DAG_DIRECT
unsigned int mtrlogi = 10;  /* Seconds before next run of meter log process */
#define LOG_INTERVAL  300  /* Seconds (5 min) */
/* #define LOG_INTERVAL  900  /* Seconds (15 min) */
#endif

void one_second_process(void)
{
   int j, x;
   clock_t rt,pt;
   struct tms tbuf;
   unsigned long idle1000, pd;
   unsigned char p;
   struct mgr_rec *mip;
   struct rdr_rec *cip;
   char msg[100];
#if CR_DATA || DAG_DIRECT
   int i;
   char *mp, ibuf[40];
#endif

#ifdef UX_TESTING
   printf(".");
#endif

#if CR_DATA
   if (request_stop) stop_meter(0);
#endif

   if (clear_pkt_stats) zero_pkt_stats();
   else {
      ++stats_time;
      realtime = times(&tbuf);
      proctime = tbuf.tms_utime+tbuf.tms_stime;
      if (realtime != srealtime) {
         idle1000 = 1000 - (proctime-sproctime)*1000/(realtime-srealtime);
         if (idle1000 < min_idle1000) min_idle1000 = idle1000;
         }
      pkt_counts(0);
      pd = (npackets-spackets)*pci[chart_interface]->SampleRate;
      if (pd > max_pkt_rate) max_pkt_rate = pd;
      srealtime = realtime;  sproctime = proctime;
      spackets = npackets;
      }
#if NEW_ATR
   check_rates(uptime_s());  /* uptime in seconds */
#endif
   if (--gci == 0) {
#ifdef UX_TESTING
     printf("g");
#endif
      garbage_collect(1);  /* Routine incremental collection */
      gci = gc_interval;
      }

#if CR_DATA || DAG_DIRECT
   if (--mtrlogi == 0) {  /* Write regular meter log entry */
      int cpb, mxcpb;
      for (mp = msg, mxcpb = i = 0; i != n_interfaces; ++i) {
         cpb = pci[i]->nblocks == 0 ? 0 :
            pci[i]->tcells/pci[i]->nblocks;
         sprintf(ibuf, "%d:  %lu badts, %lu cpb, %lu_%lu^%lu, ",
            i, pci[i]->badtspkts, cpb, 
            pci[i]->nblocks, pci[i]->mx_empty, pci[i]->mx_full);
         pci[i]->tcells = pci[i]->nblocks =
            pci[i]->mx_empty = pci[i]->mx_full = 0;  
         if (cpb > mxcpb) mxcpb = cpb;
         mp = strmov(mp, ibuf);
         }
      mp[-2] = '\0';
      log_msg(LOG_WARNING, 0, msg);
      show_stream_hash(0);
      mtrlogi = LOG_INTERVAL;

      if (mxcpb < TARGET_CPB) {  /* Should we adjust the select time? */
         if (select_us < MX_SELECT_US) {
            select_us *= 2;
#if DAG_DIRECT
            select_time = US_DAGT(select_us);
#elif CR_DATA
            select_tv.tv_usec = select_us;
#endif
            log_msg(LOG_WARNING, 0, "--* select_us = %d", select_us);
            }
         }
      else if (mxcpb >= 4*TARGET_CPB) {
         if (select_us > MN_SELECT_US) {
            select_us /= 2;
#if DAG_DIRECT
            select_time = US_DAGT(select_us);
#elif CR_DATA
            select_tv.tv_usec = select_us;
#endif
            log_msg(LOG_WARNING, 0, "--/ select_us = %d", select_us);
            }
         }
      }
#endif

   if (--bkgi == 0) {  /* Check % of flows active */
#ifdef UX_TESTING
      printf("b\n");
#endif
      p = (unsigned long)active_flows()*100/(mxflows);
      if (p > FloodMark && FloodMode != TV_TRUE &&
            FloodMark > 0 && FloodMark < 100) {
         FloodMode = TV_TRUE;  /* Stop creating new flow records */
         log_msg(LOG_WARNING, 0, "Meter in Flood mode");
	 }
      else for (x = 0; x != sizeof(mi)/sizeof(struct mgr_rec); ++x) {
         mip = &mi[x];
         if (mip->mi_Status != RS_ACTIVE
               || mip->mi_RunningStandby == TV_TRUE)
            continue;
	 else if (p > mip->mi_HighWaterMark &&
	       mip->mi_HighWaterMark > 0 && 
               mip->mi_HighWaterMark < 100 &&
	       mip->mi_StandbyRuleSet != mip->mi_CurrentRuleSet &&
               (mip->mi_StandbyRuleSet != 0 &&
                  ri[mip->mi_StandbyRuleSet-1].ri_Status == RS_ACTIVE)
               ) {
            open_rule_set(mip->mi_CurrentRuleSet, 0);  /* Close */
	    open_rule_set(mip->mi_StandbyRuleSet, 1);  /* Open */
            mip->mi_RunningStandby = TV_TRUE;
            log_msg(LOG_WARNING, 0, "Manager %d running Standby", x+1);
	    }
	 else if (p > HighWaterMark*2/3)
	    more_garbage();
         }
      pd = uptime_cs();
      for (x = 0; x != sizeof(ci)/sizeof(struct rdr_rec); ++x) {
         cip = &ci[x];
         if (cip->ci_Status != RS_ACTIVE || cip->ci_Timeout == 0)
            continue;
         if ((pd - cip->ci_LastTime)/100 > cip->ci_Timeout)
            cip->ci_Status = RS_UNUSED;  /* Timed out */
         }
      bkgi = BKG_INTERVAL;
      }
#ifdef UX_TESTING
   fflush(stdout);
#endif
   }

handle_keyboard()
{
   char kb_buf[25];
   int ch;
   if (fgets(kb_buf, sizeof kb_buf, stdin) == NULL)
      return;  /* Error or EOF */
   if ((ch = kb_buf[0]) == 27 && kb_buf[1] == 27)  /* ESC ESC */
      stop_meter(0);
   switch (tolower(ch)) {
   case 'v':
      printf("\n%s\n",version_descr);
      break;
#if CR_DATA
   case '*':
      if (coral_start_all() < 0)  /* Also synchronises all sources */
         log_msg(LOG_ERR, 0, "coral_start failed");
      break;
#endif
   default:
      handle_kb(ch);
      break;
      }
   }

void snmp_read_sp(int sd);  /* Handles meter's SNMP requests */

#if CR_DATA || DAG_DIRECT
int non_live_source(int sd)
{
   fd_set fdset;
   struct timeval t;
   int j, width, count;
#define DEBUG_TRACE_STATE  0
#if DEBUG_TRACE_STATE
   int last_state = -1;
#endif

#if CR_DATA
   int last_trace_count;

#elif DAG_DIRECT
   init_live_sources();  /* Read first block, set meter_start_time */
#endif

   width = STDIN_FILENO;  /* Determine size of fd_set */
   if (sd > width) width = sd;
   ++width;
   FD_ZERO(&fdset);
   t.tv_sec = 0;  

   for (;;) {
#if DEBUG_TRACE_STATE
      if (trace_state != last_state) {
         printf("Main loop: state=%d, crl_n_count=%d, last_t_sec=%d"
            ", last_ts=%d.%06d\n",
            trace_state, crl_n_count, last_t_sec,
            last_ts.tv_sec, last_ts.tv_usec);
         last_state = trace_state;
         }
#endif
      t.tv_usec = 250000;  /* 0.25 s */
      switch (trace_state) {  /* Reading from trace file */
      case CT_WAIT_RULES:
         /* met_vars will change to CT_RULES_READY */
         break;
      case CT_RULES_READY:
#if CR_DATA
         interface_read_trace(pci[0]);
            /* Get timestamp for beginning of first interval */
         last_t_sec = last_ts.tv_sec;
         meter_start_time.tv_sec = last_ts.tv_sec-1;
            /* Don't let uptime start at -1 */
#endif
         trace_state = CT_WAIT_FLOWS_READ;
         break;
      case CT_PROCESS_FLOWS:
#if CR_DATA
         t.tv_usec = 20000;  /* 20 ms */
         last_trace_count = trace_count;
         do {
            if (!interface_read_trace(pci[0])) {
               trace_state = CT_TRACE_EOF;  /* No more blocks */
               break;
               }
            } while (trace_count != 0);
         if (trace_state == CT_TRACE_EOF)
            break;  /* Let NeMaC do EOF processing (i.e. last reading) */

#elif DAG_DIRECT
         dag_merge();  /* Return at end of block */
         if (pci[waiting_live_source]->ncells == 0) {  /* Block now empty */
            if (read_live_block(waiting_live_source) == 0) {
               trace_state = CT_TRACE_EOF;  /* No more blocks */
               break;
               }
            }
#endif
         if (trace_count == 0) {  /* Reached -T interval */
/*            show_memory();  /* Show memory every -T interval */
/*            show_stream_hash();  */
            trace_count = trace_interval;
            if (crl_n_max) {  /* Stop after -N meter readings */
               if (++crl_n_count == crl_n_max) {
                 trace_state = CT_TRACE_EOF;  /* Dummy up EOF */
                 }
               else
                  trace_state = CT_WAIT_FLOWS_READ;
               }
            else trace_state = CT_WAIT_FLOWS_READ;
            }
         break;
      case CT_WAIT_FLOWS_READ:
         /* NeMaC will change to CT_PROCESS_FLOWS */
         break;
      case CT_TRACE_EOF:
         /* NeMaC will change to CT_SHUTDOWN */
         break;
      case CT_SHUTDOWN:
         stop_meter(0);
         break;
         }

      FD_SET(sd, &fdset);
      if (kb_enabled) FD_SET(STDIN_FILENO, &fdset);  /* stdin */
      count = select(width, &fdset, 0, 0, &t);
      if (count > 0) {
         if (FD_ISSET(sd, &fdset))
	    snmp_read_sp(sd);
         if (kb_enabled && FD_ISSET(STDIN_FILENO, &fdset))  /* stdin */
            handle_keyboard();
         }
      else switch (count) {
      case 0:
	 break;
      case -1:
	 if (errno == EINTR) continue;
	 else log_msg(LOG_ERR, 0,
            "select returned %d, %s", count, strerror(errno));
	 return 0;
      default:
	 log_msg(LOG_ERR, 0, "select returned %d", count);
	 return 0;
	 }
      }
   return 1;
   }
#endif  /* CR_DATA || DAG_DIRECT */

int receive(int sd,int n_interfaces)
{
   fd_set fdset;
   int j, width, count;
   struct timeval t;
#if defined (NETFLOW) || CR_DATA || LFAP_METER
   time_t now,prev;
#endif
#if DAG_DIRECT
   int i_count = 0;
#endif

   /*   printf("running receive():\n");  fflush(stdout); */
   zero_pkt_stats();
   gci = gc_interval;  /* Garbage collect interval */
   bkgi = BKG_INTERVAL;  /* Background interval */
   gc_f = (mxflows)/100;  /* Flow indexes to check */
   if (gc_f < 4) gc_f = 4;

#if DAG_DIRECT
   select_time = US_DAGT(select_us);
#endif

#if CR_DATA || DAG_DIRECT
   if (!live_source)
      return non_live_source(sd);
#endif

   width = STDIN_FILENO;  /* Determine size of fd_set */
   if (sd > width) width = sd;
#if !CYGWIN32
   for (j = 0; j != n_interfaces; ++j)
      if (pci[j]->fd > width) width = pci[j]->fd;
#endif
   ++width;
#if defined(NETFLOW) || LFAP_METER
   time(&prev);
#endif

#if CR_DATA
   init_live_sources();  /* Read some cells from each interface */
   coral_merge();  /* Return when ready for next block */
#elif DAG_DIRECT
   init_live_sources();  /* Read some cells from each interface */
   dag_merge();  /* Return when ready for next block */
#endif

   for (;;) {
      FD_ZERO(&fdset);
      t.tv_sec = 0;  

#if CR_DATA
      if (read_live_block(waiting_live_source))
         coral_merge();  /* Return when ready for next block */
      else
         if (request_stop) stop_meter(0);
      t.tv_usec = select_us;

#elif DAG_DIRECT  /* !CR_DATA */
      if (read_live_block(waiting_live_source))
         dag_merge();  /* Return when ready for next block */
      t.tv_usec = select_us;

#elif CYGWIN32  /* !DAG_DATA */
      t.tv_usec =  15000;  /* 15 ms */
      for (j = 0; j != n_interfaces; ++j)  /* Read interfaces every cycle */
         interface_read(pci[j]);  /* Can block for several seconds !!!!!! */

#elif LFAP_METER /* LFAP_METER */
      t.tv_usec = 250000;  /* 0.25 s */
      for (j = 0; j != n_interfaces; ++j) {
         tick(pci[j]);
         if (pci[j]->fd != -1) {
            FD_SET(pci[j]->fd,&fdset);
            if (pci[j]->fd >= width) width = pci[j]->fd + 1;
            }
         }
      FD_SET(lfap_server_socket,&fdset);
      if (lfap_server_socket >= width) width = lfap_server_socket + 1;

#else  /* Unix */
      t.tv_usec = 250000;  /* 0.25 s */
      for (j = 0; j != n_interfaces; ++j) FD_SET(pci[j]->fd,&fdset);
#endif

      FD_SET(sd, &fdset);
      if (kb_enabled) FD_SET(STDIN_FILENO, &fdset);  /* stdin */
      count = select(width, &fdset, 0, 0, &t);
      if (count > 0) {

#if LFAP_METER
         if (FD_ISSET(lfap_server_socket, &fdset)) {
            printf("client wants to connect.\n");
            /* Apparently someone knocking on my door...
               find a free interface */
            for (j = 0; j < n_interfaces; ++j) {
               printf("trying i/f %i.\n",j);
               if (pci[j]->fd == -1) {
                  printf("i/f %i is free.\n",j);  /* Found a free one */
                  pci[j]->fd = accept(lfap_server_socket,NULL,NULL);
                  pci[j]->fasinfo.conn = pci[j]->fd;
                  j = n_interfaces + 1;
                  }
               }
            if (j == n_interfaces) {
               int sock;
               LFAPHeader *lfap;
               LFAP_IE_Header* ie;

               printf("No interfaces left !\n");
               /* Dirty hack to refuse connections, need this in LFAPv5 ? */
               sock = accept(lfap_server_socket, NULL, NULL);
               /* Assume it is the right one ... */
               lfap = newLFAPMessage(LFAP_OPCODE_AR, LFAP_STATUS_SUCCESS, 1);
               /* Fill in command code IE */
               ie = newIE(LFAP_IE_COMMAND_CODE,4);
               putIEDword(ie, (Bit32)LFAP_CMD_CONNECTION_ACCEPTED, 0);
               addIE(&lfap, ie);
               free(ie);
               sendLFAPMessage(lfap, sock);
               free(lfap);
               close(sock);
               }
	    }
#endif

#if !CYGWIN32 && !CR_DATA
         for (j = 0; j != n_interfaces; ++j) {
# if LFAP_METER
            if(pci[j]->fd != -1) {
               if (FD_ISSET(pci[j]->fd, &fdset)) interface_read(pci[j]);
               }
# elif DAG_DIRECT
            if (FD_ISSET(pci[j]->fd, &fdset)) {
	       printf(">>> select returned from Dag i/f %d\n", j);
	       fflush(stdout);
               }

# else  /* !LFAP_METER */
            if (FD_ISSET(pci[j]->fd, &fdset)) interface_read(pci[j]);
# endif
	    }
#endif
         if (FD_ISSET(sd, &fdset))
	    snmp_read_sp(sd);
         if (kb_enabled && FD_ISSET(STDIN_FILENO, &fdset))  /* stdin */
            handle_keyboard();
         }
      else switch (count) {
      case 0:
	 break;
      case -1:
	 if (errno == EINTR) continue;
	 else log_msg(LOG_ERR, 0, "select returned %d (not EINTR)", count);
	 return 0;
      default:
	 log_msg(LOG_ERR, 0, "select returned %d", count);
	 return 0;
	 }

#if defined(NETFLOW) || LFAP_METER
      time(&now);
      if (now != prev) {
         one_second_process();
         prev = now;
         }
#endif
      }
   return 1;
   }

Bit32 snmp_peer_addr;  /* IPv4 address of SNMP client */

void snmp_read_sp(int sd)
{
   struct sockaddr_in from;
   int length, out_length, fromlength, count;
   Bit8 packet[1500], outpacket[1520];  /* Leave some space just in case */
   char snmp_peer_name[50];  /* Name of host which sent the snmp request */

   fromlength = sizeof from;
   length = recvfrom(sd, packet, 1500, 0, 
      (struct sockaddr *)&from, &fromlength);
   snmp_peer_addr = from.sin_addr.s_addr;
   strcpy(snmp_peer_name,inet_ntoa(from.sin_addr));
   if (length == -1) {
      log_msg(LOG_ERR, 0,
         "snmp_read_sp() recvfrom failed: %s", strerror(errno));
      return;
      }
   if (snmp_dump_packet) {
      printf("received %d bytes from %s:\n", length, snmp_peer_name);
      for (count = 0; count < length; count++) {
         printf("%02X ", packet[count]);
	 if ((count % 16) == 15) printf("\n");
	 else if ((count % 4) == 3) printf(" ");
	 }
      printf("\n\n");
      }

   out_length = 1500;
   if (snmp_agent_parse(packet, length, outpacket, &out_length, 
         from.sin_addr.s_addr)) {
      if (snmp_dump_packet) {
	 printf("sent %d bytes to %s:\n", out_length, snmp_peer_name);
	 for (count = 0; count < out_length; count++) {
            printf("%02X ", outpacket[count]);
	    if ((count % 16) == 15) printf("\n");
	    }
	    printf("\n\n");
	 }
      if (sendto(sd, (char *)outpacket, out_length, 0, 
            (struct sockaddr *)&from, sizeof(from)) < 0) {
         log_msg(LOG_ERR, 0,
            "snmp_read_sp() sendto failed: %s", strerror(errno));
	 }
      }
   }
