/* 1102, Sun 4 Mar 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.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 */

#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 <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"
#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];
   unsigned char type[2];    /* Length or protocol type */
   };

#define SZ_IFNAME  32
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;
   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;
#else  /* libpcap */
# if CR_DATA
   coral_source_t *crl_src;
   coral_iface_t *iface;
   int ncells, nblocks;
   coral_atm_cell_t *cellp;
   int first_block;
   struct timeval card_ts;  /* For next cell */
# if CORAL_TIME_OFFSET
   struct timeval ts_offset;  /* Accumulated time error from card */
# endif
   u_int badtspkts;  /* Packets with bad timestamps */
# else
   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 */

/* 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;		\
   }

#if CORAL_TIME_OFFSET
/* 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;		\
   }
#endif

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

struct timeval meter_start_time;

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

#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,&fr1->srcaddr,IP4_ADDR_LEN);
         memcpy(pp.High.PeerAddress,&fr1->dstaddr,IP4_ADDR_LEN);
         pp.TransAddrType = fr1->prot;
         memcpy(pp.Low.TransAddress, &fr1->srcport, TRANS_ADDR_LEN);
         memcpy(pp.High.TransAddress, &fr1->dstport, TRANS_ADDR_LEN);
         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 */
         memset(pp.Low.AdjAddress,0,MAC_ADDR_LEN);
         pp.High.AdjType = AT_IP4;
         memcpy(pp.High.AdjAddress,&fr1->nexthop,IP4_ADDR_LEN);
         memset(&pp.High.AdjAddress[IP4_ADDR_LEN],0,MAC_ADDR_LEN-IP4_ADDR_LEN);
         memset(pp.Low.nf_ASN,0,TRANS_ADDR_LEN);
         memset(pp.High.nf_ASN,0,TRANS_ADDR_LEN);
         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,&fr5->srcaddr,IP4_ADDR_LEN);
         memcpy(pp.High.PeerAddress,&fr5->dstaddr,IP4_ADDR_LEN);
         pp.TransAddrType = fr5->prot;
         memcpy(pp.Low.TransAddress, &fr5->srcport, TRANS_ADDR_LEN);
         memcpy(pp.High.TransAddress, &fr5->dstport, TRANS_ADDR_LEN);
         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;

         pp.Low.AdjType = 0;  /* NetFlow attributes */
         memset(pp.Low.AdjAddress,0,MAC_ADDR_LEN);
#if 0    /* RTFM meter can't match S->D and D->S without having both */
         pp.High.AdjType = AT_IP4;   /* addresses for EVERY packet */
         memcpy(pp.High.AdjAddress,&fr5->nexthop,IP4_ADDR_LEN);
         memset(&pp.High.AdjAddress[IP4_ADDR_LEN],0,MAC_ADDR_LEN-IP4_ADDR_LEN);
#else
         pp.High.AdjType = 0;  /* NetFlow attributes */
         memset(pp.High.AdjAddress,0,MAC_ADDR_LEN);
#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
         memcpy(pp.Low.nf_ASN, &fr5->src_as, TRANS_ADDR_LEN);
         memcpy(pp.High.nf_ASN, &fr5->dst_as, TRANS_ADDR_LEN);
         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)
{
   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 ===== */

int live_source = 0;
int waiting_live_source = -1;

static struct timeval last_ts = {0,0};  /* Used by uptime() to get Coralreef times */
static Bit32 last_t_sec;

#define MX_TS_JUMP  2000000  /* 2 seconds */

int tv_diff_us(struct timeval x, struct timeval y)
{  /* Returns x-y microseconds */
   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;
   }

# if LOG_BAD_TIMESTAMPS
int mxbadt = 1000;
# endif

int restart_interface(int s)
{
   int r;
   coral_source_t *i_src;

   if ((r = coral_stop(i_src = pci[s]->crl_src)) != 0) {
      log_msg(LOG_ERR, 0, "restart_int(%d): stop failed, result=%d", s,r);
      return 0;  /* Fail */
      }
   if ((r = coral_close(i_src)) != 0) {
      log_msg(LOG_ERR, 0, "restart_int(%d): close failed, result=%d", s,r);
      return 0;  /* Fail */
      }

   if ((r = coral_open(i_src)) != 0) {
      log_msg(LOG_ERR, 0, "restart_int(%d): open failed, result=%d", s,r);
      return 0;  /* Fail */
      }
   if ((r = coral_start(i_src)) != 0) {
      log_msg(LOG_ERR, 0, "restart_int(%d): start failed, result=%d", s,r);
      return 0;  /* Fail */
      }

   log_msg(LOG_ERR, 0, "restart_int(%d): successful Coral restart", s);
   return 1;
   }

#define MX_CRB_FAILS  4000  /* 5 ms waits * 20 seconds */
int crb_fails = 0, last_crb_err = 0;

#define MX_EAGAINS  60000  /* 5 ms waits * 300 seconds */
/* #define MX_EAGAINS  12000  /* 5 ms waits * 60 seconds */
int eagains = 0;

#if CORAL_BUFFER_TRACE
#define MX_BT_RECORDS  10
int bt_recs = 0;
#define NORMAL_LOGRECS  20
int coral_logging = NORMAL_LOGRECS;
#endif

int save_errno = 0;  /* Last errno from read_live_block() */

int coral_to_us = 5000;  /* 5 ms */
int read_live_block(int s)
{
   struct timeval t, last_t;
   coral_iface_t *iface;
   coral_blk_info_t *binfop;
   coral_atm_cell_t *cellp;
   int ncells, ts_delta;

#if CORAL_BUFFER_TRACE
   char *mp, msg[200], ibuf[50];
   int i;
#endif

   t.tv_sec = 0;  t.tv_usec = coral_to_us;
   if ((iface = coral_read_block(pci[s]->crl_src, &binfop, &cellp, &t))
         == NULL) {
      if ((save_errno = errno) != EAGAIN) {
         if (save_errno != last_crb_err || crb_fails == MX_CRB_FAILS) { 
            printf("coral_read_block(%d) failed, errno=%d, count=%d",
               s, save_errno, crb_fails);
            fflush(stdout);
            log_msg(LOG_ERR, 0,
               "coral_read_block(%d) failed, errno=%d, count=%d",
               s, save_errno, crb_fails);
            }
         last_crb_err = save_errno;
         if (++crb_fails > MX_CRB_FAILS) {
            if (!restart_interface(s))
               exit(999);  /* Couldn't restart interface; give up now */
            }
         return 0;  /* Try again */
         }
      if (++eagains == MX_EAGAINS) {
         log_msg(LOG_ERR, 0,
            "coral_read_block(%d) too many EAGAINs");
         if (!restart_interface(s))
            exit(911);  /* Couldn't restart interface; give up now */
         eagains = 0;
         }
      last_crb_err = crb_fails = 0;
      coral_to_us = 20000;  /* 20 ms */
      return 0;  /* Try again */
      }
   else {
eagains = 0;
      coral_to_us = 5000;  /* Normal value, 5 ms */
      pci[s]->iface = iface;  
      pci[s]->ncells = ncells = ntohl((binfop)->cell_count);
      pci[s]->cellp = cellp;
      ++pci[s]->nblocks;
      pci[s]->ipackets += ncells;
         /* ???? For now, we're only collecting 1 cell per pkt */
      pci[s]->idrops += ntohl((binfop)->cells_lost);

      last_t = pci[s]->card_ts;
      CORAL_TIMESTAMP_TO_TIMEVAL(iface,
         coral_cell_time(iface, cellp), &t);
#if CORAL_TIME_OFFSET
      tv_sub(t, pci[s]->ts_offset);
#endif
      ts_delta = tv_diff_us(t, last_t);
      if ((ts_delta < 0 || ts_delta > MX_TS_JUMP) && !pci[s]->first_block) {
         ++pci[s]->badtspkts;  /* Bad timestamp from card */
         printf(
            "*** read_live_block(%d): "
               "ncells=%u, last_t=%u.%06u, t=%u.%06u\n",
            s, ncells,
            last_t.tv_sec,last_t.tv_usec, t.tv_sec,t.tv_usec);
#if LOG_BAD_TIMESTAMPS
         log_msg(LOG_ERR, 0, 
            "*** read_live_block(%d): "
               "ncells=%u, last_t=%u.%06u, t=%u.%06u",
            s, ncells,
            last_t.tv_sec,last_t.tv_usec, t.tv_sec,t.tv_usec);
         if (--mxbadt <= 0) { quack(54321); }
#endif
         }
      else pci[s]->card_ts = t;

      if (pci[s]->first_block) {
         printf(
            "+++ read_live_block(%d): first_block, "
               "ncells=%u, t=%u.%06u\n",
            s, ncells, t.tv_sec,t.tv_usec);
#if LOG_BAD_TIMESTAMPS
         log_msg(LOG_ERR, 0, 
            "+++ read_live_block(%d): first_block, "
               "ncells=%u, t=%u.%06u",
            s, ncells, t.tv_sec,t.tv_usec);
         if (--mxbadt <= 0) { quack(54321); }
#endif
         pci[s]->first_block = 0;
         }
#if CORAL_BUFFER_TRACE
      if (coral_logging != 0) {
         --coral_logging;
         for (mp = msg, i = 0; i != n_interfaces; ++i) {
            sprintf(ibuf, "%d: cells=%u, t=%u.%06u, ",
               i, pci[i]->ncells,
               pci[i]->card_ts.tv_sec, pci[i]->card_ts.tv_usec);
            mp = strmov(mp, ibuf);
            }
         mp[-2] = '\0';
         log_msg(LOG_ERR, 0, "=== block(%d): %s", s, msg);
         }
#endif

      return 1;
      }
   }

void init_live_sources(void)
{
   int j;
   for (j = 0; j != n_interfaces; ++j) {  /* Get block for each source */
      pci[j]->first_block = 1;
      while (!read_live_block(j)) ;
      printf("init_live_sources(%d) first block read\n", j);
      fflush(stdout);
      }
   }

void empty_live_buffers(int n)  /* Read n blocks from each interface */
{
   int j, k;
   for (j = 0; j != n_interfaces; ++j) {  /* Get blocks for each source */
      for (k = 0; ; ++k) {
         pci[j]->first_block = 1;
         coral_to_us = 20000;  /* 20 ms */
         if (!read_live_block(j)) break;
         }
      log_msg(LOG_ERR, 0, "--- empty_live_buffers(%d): %d blocks",
         n, k);
      }
#if 0
   for (j = n*n_interfaces, k = 1; j != 0; ++k) {
      for (i = 0; i != n_interfaces; ++i) {
         pci[i]->first_block = 1;
         coral_to_us = 20000;  /* 20 ms */
         if (read_live_block(i)) --j;
         }
      }

   for (n = j = 0; j != n_interfaces; ++j) {  /* Do ONE read on each block */
      pci[j]->first_block = 1;
      coral_to_us = 20000;  /* 20 ms */
      if (read_live_block(j)) ++n;
      }
   log_msg(LOG_ERR, 0, "--- empty_live_buffers(): %d interface skips", n);
#endif
   }

int next_time_ok(int if_n, struct timeval bad_t)
   /* Number of good cell-time pairs (up to 3) */
{
   coral_atm_cell_t *cellp;
   struct timeval t;
   int n, t_delta, r;

   n = pci[if_n]->ncells;  r = 0;
   t = bad_t;  cellp = pci[if_n]->cellp;

   if (--n != 0) {
      bad_t = t;  cellp += coral_cell_size(pci[if_n]->iface);
      CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, 
         coral_cell_time(pci[if_n]->iface, cellp), &t);
      t_delta = tv_diff_us(t, bad_t);
      if (t_delta >= 0 && t_delta < MX_TS_JUMP) ++r;

      if (--n != 0) {
         bad_t = t;  cellp += coral_cell_size(pci[if_n]->iface);
         CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, 
            coral_cell_time(pci[if_n]->iface, cellp), &t);
         t_delta = tv_diff_us(t, bad_t);
         if (t_delta >= 0 && t_delta < MX_TS_JUMP) ++r;

         if (--n != 0) {
            bad_t = t;  cellp += coral_cell_size(pci[if_n]->iface);
            CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, 
               coral_cell_time(pci[if_n]->iface, cellp), &t);
            t_delta = tv_diff_us(t, bad_t);
            if (t_delta >= 0 && t_delta < MX_TS_JUMP) ++r;
            }
         }
      }
   return r;
   }

#define MIN_CELL_BITE  500
   /* Tune this so that meter can catch up without loosing too many cells */

int find_non_bitten_time(int if_n, struct timeval save_t)
     /* Look for a 'good' timestamp succeeding save_t */
{
   coral_atm_cell_t *cellp;
   struct timeval t;
   int n,save_n, t_delta, r;

   save_n = n = pci[if_n]->ncells;  r = 0;
   cellp = pci[if_n]->cellp;

   while (--n != 0) {
      cellp += coral_cell_size(pci[if_n]->iface);
      CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, 
         coral_cell_time(pci[if_n]->iface, cellp), &t);
      t_delta = tv_diff_us(t, save_t);
      if (t_delta >= 0 && t_delta < MX_TS_JUMP) {  /* Good timestamp */
         if (n > MIN_CELL_BITE) {  /* Help Coral/NeTraMet catch up */
            pci[if_n]->ncells = n - MIN_CELL_BITE;
            pci[if_n]->cellp = cellp +
               coral_cell_size(pci[if_n]->iface)*MIN_CELL_BITE;
            CORAL_TIMESTAMP_TO_TIMEVAL(pci[if_n]->iface, 
               coral_cell_time(pci[if_n]->iface, pci[if_n]->cellp), &t);
            pci[if_n]->card_ts = t;
            r = 1;
            }
         break;
         }
      }
#if LOG_BAD_TIMESTAMPS
   quack(54321);  /* Try to see what's happening here */
   log_msg(LOG_ERR, 0, 
      "*** find_non_bitten_time(%d): bad_cells=%u, "
         "cells_left=%u, (CELL_BITE=%u), save_t=%u.06%u, t=%u.%06u",
       if_n, save_n-n, n, MIN_CELL_BITE,
       save_t.tv_sec,save_t.tv_usec, t.tv_sec,t.tv_usec);
#endif
   return r;
   }

void interface_read_live(void)
{
   struct timeval ts, mints, save_t;
   int j, minj, ts_delta, nto;

   coral_pkt_buffer_t outer, hdr;

   union atm_hdr atm_h;
   const Bit8 *pay;
   llcsnap_t *snap_h;
   unsigned int lsap;

   struct pkt pmp;  /* To be passed to pkt_monitor() */
   struct ether_hdr ehdr;

#if CORAL_TIME_OFFSET
   struct timeval t_jump;
#endif

   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) {
            mints = ts;  minj = j;
            }
         else if (ts.tv_sec < mints.tv_sec) {
            mints = ts;  minj = j;
            }
         }
      last_ts = mints;
      if (--pci[minj]->sample_count == 0) {
            /* Process only 1 pkt per SampleRate */
         pci[minj]->sample_count = pci[minj]->SampleRate;

         if (coral_cell_to_pkt(pci[minj]->iface, pci[minj]->cellp, &outer)
               /* ATM data in outer.buf.  Could unpack it ourselves (as
                  we did for RFC1483 to get ethertype and lsap), but we'd
                  have to do it for all ATM encapsulations! */
               && coral_get_payload(&outer, &hdr) >= 0) {
               /* get_payload() strips ATM encapsulation, as per .cfg file */
            if (adj_reqd) {  /* Don't do this unless we have to! */
               atm_h.ui = ntohl(*(Bit32 *)coral_cell_header(
               pci[minj]->iface, pci[minj]->cellp));
               /* 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; 
	       }
            if (handle_pkt(&pmp, 0x0800, 0xAAAA, (Bit8 *)&ehdr,
	       /* Will need to work out what eth_type/lsap should be */
                  hdr.buf, hdr.caplen)) {
               pmp.Low.Interface = pmp.High.Interface = minj+1;  /* 1-org */
               pmp.Low.AdjType = pmp.High.AdjType = AT_AAL5;
               pmp.arrival_time = mints;  /* struct timeval */
               pkt_monitor(&pmp);
	       }
            }
         }

      if (last_ts.tv_sec > last_t_sec) {
         last_t_sec = last_ts.tv_sec;
         one_second_process();  /* NeTraMet housekeeping */
         /* Note this happens at the time seen by the interface, 
            we may get several seconds worth of data in a buffer */
         }

      if (--pci[minj]->ncells == 0) {
         waiting_live_source = minj;
         return;  /* Keep outer block alive */
         }
      else {
         save_t = mints;
         pci[minj]->cellp += coral_cell_size(pci[minj]->iface);
         CORAL_TIMESTAMP_TO_TIMEVAL(pci[minj]->iface, 
            coral_cell_time(pci[minj]->iface, pci[minj]->cellp), &ts);
#if CORAL_TIME_OFFSET
         tv_sub(ts, pci[minj]->ts_offset);
#endif
         ts_delta = tv_diff_us(ts, save_t);
         if (ts_delta < 0 || ts_delta > MX_TS_JUMP) {
            ++pci[minj]->badtspkts;  /* Bad timestamp from card */
            printf("*** interface_read_live(%d): "
                  "ncells=%u, save_t=%u.%06u, ts=%u.%06u\n",
               minj, pci[minj]->ncells,
               save_t.tv_sec,save_t.tv_usec, ts.tv_sec,ts.tv_usec);
            log_msg(LOG_ERR, 0, "*** interface_read_live(%d): "
                  "ncells=%u, save_t=%u.%06u, ts=%u.%06u",
               minj, pci[minj]->ncells,
               save_t.tv_sec,save_t.tv_usec, ts.tv_sec,ts.tv_usec);

            nto = next_time_ok(minj, ts);
            log_msg(LOG_ERR, 0, "!!! interface_read_live(%d): "
                  "Next %d cell-time pairs OK, %s",
               minj, nto,  nto == 3 ? 
                  "time shift detected" : "bad time ignored");
            if (nto != 3) {
               if (--mxbadt <= 0) { quack(54321); }
               }
            else {  /* Dag time shift detected */
#if CORAL_TIME_OFFSET
               t_jump = ts;  tv_sub(t_jump, save_t);
               tv_add(pci[minj]->ts_offset, t_jump);
               tv_sub(ts, pci[minj]->ts_offset);
#endif
#if CORAL_BUFFER_TRACE
               coral_logging = NORMAL_LOGRECS;
#endif
               empty_live_buffers(5);  /* Skip 5 blocks */

#  if 0
               pci[minj]->card_ts = ts;
               /* Try accepting the shift (just to see what happens) */

               log_msg(LOG_ERR, 0, "!!! interface_read_live(%d): "
                  " tail-biting detected, %u cells abandoned",
                  minj, pci[minj]->ncells+1);
               pci[minj]->idrops += pci[minj]->ncells+1;
               pci[minj]->first_block = 1;
               waiting_live_source = minj;
               return;  /* Get the next buffer for this interface */

   /* When this happened, CoralReef got into a resource contention
      argument with Unix, leading to NeTraMet filling disk with
      error messages.  Better to just throw away the rest of this
      buffer. */
               if (!find_non_bitten_time(minj, save_t)) {
                  waiting_live_source = minj;
                  return;  /* Couldn't recover, read next block */
                  }
#  endif
               }
            }
         else pci[minj]->card_ts = ts;
         }
      }
   }

#if 0  /* $$$$$$ */
int interface_read_live(struct interface_info *pi)
   /* Returns 0 if no cells left in buffer */
{
   coral_iface_t *iface;
   coral_blk_info_t *binfop;
   coral_atm_cell_t *cellp;
   struct timeval t;
   int ncells, src_nbr;
   struct timeval this_ts;

   union atm_hdr atm_h;
   const Bit8 *pay;
   llcsnap_t *snap_h;
   unsigned int lsap;

   struct pkt pmp;  /* To be passed to pkt_monitor() */
   struct ether_hdr ehdr;

   int result;

#if 0   /* $$$$$ */
   t.tv_sec = 0;  t.tv_usec = 5000;  /* 5 ms */
   if ((iface = coral_read_block_all(&binfop, &cellp, &t)) == NULL) {
#endif
   if ((iface = coral_read_cell_all(&binfop, &cellp, &t)) == NULL) {
      if (errno != EAGAIN)
         log_msg(LOG_ERR, 0, "coral_read_block_all() failed, errno=%d", errno);
      return 0;
      }
   else {
      src_nbr = coral_source_get_number(coral_interface_get_src(iface));
      ncells = ntohl((binfop)->cell_count);

      ++pci[src_nbr]->cells_read;
      if (pci[src_nbr]->cells_read == 1) {  /* Starting new buffer */
  printf("*%d(%lu) ", src_nbr, ncells);  fflush(stdout);
         pci[src_nbr]->ipackets += ncells;
            /* ???? For now, we're only collecting 1 cell per pkt */
         pci[src_nbr]->idrops += ntohl((binfop)->cells_lost);
         result = 1;
         }
      if (pci[src_nbr]->cells_read == ncells) {  /* Handling last cell */
         pci[src_nbr]->cells_read = 0;
         result = 0;
         }

      if (pci[src_nbr]->card_ts.tv_sec == 0) {
         CORAL_TIMESTAMP_TO_TIMEVAL(iface,
            coral_cell_time(iface, cellp), &last_ts);
         pci[src_nbr]->card_start = last_ts;
         }
      else last_ts = pci[src_nbr]->card_ts;
      /* meter_start_time = pci[src_nbr]->card_start; */

/* $$$$$
      for ( ;  ncells != 0;  --ncells, cellp += coral_cell_size(iface)) {
*/ {
         if (--pci[src_nbr]->sample_count == 0) {  /* Process only 1 pkt per SampleRate */
            pci[src_nbr]->sample_count = pci[src_nbr]->SampleRate;

            CORAL_TIMESTAMP_TO_TIMEVAL(iface, coral_cell_time(iface, cellp), &this_ts);

            if (this_ts.tv_sec >= last_ts.tv_sec && 
                  this_ts.tv_sec - last_ts.tv_sec < 3)
               last_ts = this_ts;  /* Looks OK */
            else {  /* Bad timestamp from point card.  Don't use it! */
               ++pci[src_nbr]->badtspkts;
	       /* printf("!");  fflush(stdout); */
	       /* $$$$$  continue; */   return 1;
               }

	    {  /* RFC 1483 (SNAP over AAL5) */

               if (adj_reqd) {  /* Don't do this unless we have to! */
                  atm_h.ui = ntohl(*(Bit32 *)coral_cell_header(iface, cellp));
                  /* 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; 
	          }

               pay = coral_cell_payload(iface, cellp);
               snap_h = (llcsnap_t *)pay;
               lsap = snap_h->llc_dsap << 8 | snap_h->llc_ssap;
 	       if (lsap == 0xAAAA) {
                  pmp.p_len = 0;  /* use_ip_length will get length from IP header */
                  if (handle_pkt(&pmp, ntohs(snap_h->snap_type), lsap, (Bit8 *)&ehdr,
	                pay+sizeof(llcsnap_t), coral_cell_size(iface)-sizeof(llcsnap_t))) {
                     pmp.Low.Interface = pmp.High.Interface = src_nbr+1;  /* 1-org */
                     pmp.Low.AdjType = pmp.High.AdjType = AT_AAL5;  /* ATM over AAL5 */
                     pmp.arrival_time = last_ts;  /* struct timeval */
                     pkt_monitor(&pmp);

                     }
	          }
               }  /* RFC 1483 */

            if (last_ts.tv_sec > last_t_sec) {
               last_t_sec = last_ts.tv_sec;
               one_second_process();  /* NeTraMet housekeeping */
               /* Note this happens at the time seen by the interface, 
		  we may get several seconds worth of data in a buffer */
               }
	    }
         }
      pci[src_nbr]->card_ts = last_ts;
      }
   return result;
   }
#endif  /* $$$$$$ */

#define CR_PKT_DEBUG  0
#if CR_PKT_DEBUG
int p_count = 0;
#endif

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 */
static int mem_shown = 0;

void interface_read_trace(struct interface_info *pi)
{
   coral_iface_t *iface;
   coral_pkt_result_t pkt_result;
   coral_interval_result_t interval_result;
   coral_pkt_buffer_t payload;
   int src_nbr, if_nbr;
   union atm_hdr atm_h;
   llcsnap_t *snap_h;

   struct pkt pmp;  /* To be passed to pkt_monitor() */
   struct ether_hdr ehdr;

#if 0
   Bit8 *bp;  int j;  char *hp,*pp;
#endif

   if ((iface = coral_read_pkt(&pkt_result, &interval_result)) == NULL) {
      if (errno == 0) {  /* End of trace file  or  stopped device */
         --trace_count;  /* Tell receive() loop we're at interval end */
         if (!mem_shown) show_memory();
         mem_shown = 1;
         trace_state = CT_TRACE_EOF;
         }
      else
         log_msg(LOG_ERR, 0, "coral_read_pkt() failed, errno=%d", errno);
      return;
      }
   else {
      src_nbr = coral_source_get_number(coral_interface_get_src(iface));
      if (pkt_result.packet != NULL) {  /* We have a packet */
#if 0
	 switch (pkt_result.header->protocol) {
         case CORAL_DLT_ATM_RFC1483:
            hp = "RFC1483";
            break;
         case CORAL_DLT_ATM_AAL5:
            hp = "AAL5";
            break;
         case CORAL_DLT_LANE_IEEE8023:
         case CORAL_DLT_EN10MB:
         case CORAL_DLT_IEEE802:
         case CORAL_DLT_GIGX:
            hp = "OTHER";
            break;
         default:
            hp = "unknown";
            break;
	    }
         printf("count=%d, header=%s (%d),", nnbb_count, hp, pkt_result.header->protocol);

         bp = (Bit8 *)pkt_result.header->buf;
         for (j = 0; j != 16; ++j) printf(" %02x", bp[j]);
	 switch (pkt_result.packet->protocol) {
         case CORAL_DLT_ATM_RFC1483:
            pp = "RFC1483";
            break;
         case CORAL_DLT_ATM_AAL5:
            pp = "AAL5";
            break;
         case CORAL_DLT_LANE_IEEE8023:
         case CORAL_DLT_EN10MB:
         case CORAL_DLT_IEEE802:
         case CORAL_DLT_GIGX:
            pp = "OTHER";
            break;
         default:
            pp = "unknown";
            break;
	    }
         printf("\n  packet=%s (%d),", pp, pkt_result.packet->protocol);
         bp = (Bit8 *)pkt_result.packet->buf;
         for (j = 0; j != 16; ++j) printf(" %02x", bp[j]);
         printf("\n");
#endif

         CORAL_TIMESTAMP_TO_TIMEVAL(iface, pkt_result.timestamp, &last_ts);
         if (--pci[src_nbr]->sample_count == 0) {  /* Process only 1 pkt per SampleRate */
            pci[src_nbr]->sample_count = pci[src_nbr]->SampleRate;

            switch (pkt_result.packet->protocol) {
            case CORAL_DLT_ATM_RFC1483:
               atm_h.ui = ntohl(*(Bit32 *)&pkt_result.header->buf[0]);
               snap_h = (llcsnap_t *)pkt_result.packet->buf;
               if (coral_get_payload(pkt_result.packet, &payload) < 0) {
                  log_msg(LOG_INFO, 0, "coral_get_payload failed");
                  return;
	          }
#if CR_PKT_DEBUG
	       if (++p_count <= 10) {
                  if_nbr = coral_interface_get_number(iface);
                  printf("pkt %d, if_nbr=%d, timestamp=%d|%d, atm=%08x, "
                     "vpvc=%06x, type=%04x, len=%d\n",
                     p_count, if_nbr, last_ts.tv_sec,last_ts.tv_usec,
	             atm_h, atm_h.h.vpvc,
                     ntohs(snap_h->snap_type), payload.caplen);
                  bp = (Bit8 *)payload.buf;
                  for (j = 0; j != 16; ++j) printf(" %02x", bp[j]);
                  printf("\n");
                  }
#endif
               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; 
	          }
               pmp.p_len = 0;  /* use_ip_length will get length from IP header */
               if (handle_pkt(&pmp, ntohs(snap_h->snap_type),0xAAAA,
	             (unsigned char *)&ehdr, (Bit8 *)payload.buf, payload.caplen)) {
                  pmp.Low.Interface = pmp.High.Interface = src_nbr+1;  /* 1-org */
                  pmp.Low.AdjType = pmp.High.AdjType = AT_AAL5;  /* ATM over AAL5 */
                  pmp.arrival_time = last_ts;  /* struct timeval */
                  pkt_monitor(&pmp);
                  }
               break;
            case CORAL_DLT_ATM_AAL5:
            case CORAL_DLT_LANE_IEEE8023:
            case CORAL_DLT_EN10MB:
            case CORAL_DLT_IEEE802:
            case CORAL_DLT_GIGX:
               log_msg(LOG_ERR, 0, "unimplemented protocol");
               return;
            default:
               log_msg(LOG_ERR, 0, "unknown protocol");
               break;
	       }

           if (last_ts.tv_sec != last_t_sec) {
               last_t_sec = last_ts.tv_sec;
               one_second_process();  /* NeTraMet housekeeping */
               }
            }
         }
      else if (interval_result.stats == NULL) {  /* A Coral interval has begun */
         last_ts = interval_result.begin;
	 }
      else {  /* A Coral interval has ended */
         last_ts = interval_result.end;

         pci[src_nbr]->ipackets += interval_result.stats->pkts_recv;
         pci[src_nbr]->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);

         --trace_count;  /* Tell receive() loop we're at interval end */
         }         
      }
   }

char *cs_type(int cst)
{
   switch (cst) {
   case CORAL_TYPE_FILE:
      return "CoralReef file";
   case CORAL_TYPE_PCAP:
      return "tcpdump 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";
   default:
      return "Bad cs_type";
      }
   }

int init_interface(struct interface_info *pi)
{
   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;


   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_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 */
      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);
         meter_start_time.tv_sec = min_ct;
         meter_start_time.tv_usec = 0;
         }
 
      if (!live_source) {
         if ((ir = coral_read_pkt_init(NULL, NULL, 1)) != 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;
   }

#else  /* !NF_CISCO_DATA && !CR_DATA*/

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

unsigned long last_t_sec = 0;

struct ether_hdr null_e_hdr = {  /* For PPP callback */
   0,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 = get_short(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)
{
   char *interface, errbuf[PCAP_ERRBUF_SIZE];
   pcap_t *pd;
   int type;

   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, 250, errbuf)) == NULL) {
      log_msg(LOG_ERR, 0, "pcap_open_live(%s): %s\n", pi->name,errbuf);
      return 0;  /* Fail */
      }
   setuid(getuid());
   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)
{
   struct timeval tv;
   gettimeofday(&tv, (struct timezone *)0);
   meter_start_time = tv;
   }

Bit32 uptime_s(void) /* seconds*/
{
   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;
   }

Bit32 uptime_cs(void) /* centiseconds */
{
   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;
   }


double centiseconds(struct timeval nt)
{
   tv_sub(nt, meter_start_time);
   return ((double)nt.tv_sec)*100.0 + ((double)nt.tv_usec)/10000.0;
   }

double microseconds(struct timeval nt)
{
   tv_sub(nt, meter_start_time);
   return ((double)nt.tv_sec)*1000000.0 + (double)nt.tv_usec;
   }

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
      "  -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);
   }

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;

   mxflows = DFMXFLOWS;  mxrules = DFMXRULES;
   mxstreams = DFMXSTREAMS;  mxstr_blocks = DFMXSTRBLOCKS;
   mxdistribs = DFMXDISTRIBS;  mxdistevents = DFMXDISTEVENTS;
   mxpktdatas = DFMXPKTDATS;
   if (argc < 2) {  /* Help Screen when called with no args */
      print_help();
      exit(-1);
      }

#if CR_DATA
   trace_interval = 10;  /* Default 10s */
   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;
	 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';
            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];
	    s_n = atoi(ap);
            if (s_n < 1) s_n = 1;
 	    break;
#if NF_OCX_BGP
	 case 'o':
	    use_owner_asns++;
	    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 '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;
         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
         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;
         case 'T':  /* Collection interval for CoralReef trace file */
            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 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) {
      perror("bind");
      exit(2);
      }
   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;

   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

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

   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
unsigned int mtrlogi = 10;  /* Seconds before next run of meter log process */
#define LOG_INTERVAL  900  /* Seconds (15 min) */
char ifmsg[100];
#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
   int i;
   char *mp, ibuf[40];
#endif

#ifdef UX_TESTING
   printf(".");
#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
   if (--mtrlogi == 0) {  /* Write regular meter log entry */
      for (mp = msg, i = 0; i != n_interfaces; ++i) {
         sprintf(ibuf, "%d: %lu blks, %lu badts, ",
            i, pci[i]->nblocks, pci[i]->badtspkts);
         mp = strmov(mp, ibuf);
         }
      mp[-2] = '\0';
      log_msg(LOG_WARNING, 0, msg);
      mtrlogi = LOG_INTERVAL;
      }
#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
   }

void stop_meter(void)
{
   display_msg(1,"Shutting down");
#if CR_DATA
   coral_stop_all();
   coral_close_all();
#endif
   exit(0);
   }

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)  /* ESC */
      stop_meter();
   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;
      }
   }

receive(int sd,int n_interfaces)
{
   fd_set fdset;
   int j, width, count;
   struct timeval t;
#if defined(NETFLOW) || CR_DATA
   time_t now,prev;
#endif
#if CR_DATA
   int last_state = -1;  /* ### */
   int last_trace_count;
#endif

   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;

   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)
   time(&prev);
#endif

#if CR_DATA
   if (live_source) {
      init_live_sources();
      interface_read_live();  /* Return when ready for next block */
      }
#endif

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

#if CR_DATA
      if (!live_source) {  /* Reading trace file */
         if (trace_state != last_state) {
            printf("Main loop: state=%d, crl_n_count=%d\n",
               trace_state, crl_n_count);
            last_state = trace_state;
   	    }
         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:
            interface_read_trace(pci[0]);  /* Get timestamp for beginning of first interval */
            meter_start_time.tv_sec = last_ts.tv_sec-1;  /* Don't let uptime start at -1 */
            trace_state = CT_WAIT_FLOWS_READ;
            break;
         case CT_PROCESS_FLOWS:
            t.tv_usec = 20000;  /* 20 ms */
            last_trace_count = trace_count;
            do {
               interface_read_trace(pci[0]);  /* Coral tells us what interface it was */
               } while (trace_count == last_trace_count);

            if (trace_count == 0) {  /* Reached -T interval */
               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();
            break;
	    }
         }
      else {
	 if (read_live_block(waiting_live_source))
            interface_read_live();  /* Return when ready for next block */
         t.tv_usec =  2000;  /* 2 ms */
         }

#elif CYGWIN32  /* !CR_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 !!!!!! */

#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 !CYGWIN32 && !CR_DATA
         for (j = 0; j != n_interfaces; ++j) {
            if (FD_ISSET(pci[j]->fd, &fdset))
               interface_read(pci[j]);
	    }
#endif
         if (FD_ISSET(sd, &fdset))
	    snmp_read(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 -1;
      default:
	 log_msg(LOG_ERR, 0, "select returned %d", count);
	 return -1;
	 }

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

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

snmp_read(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) perror("recvfrom");
   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) {
         perror("sendto");
	 return 0;
	 }
      }
   }

