/* 1900, Sat 7 Jul 01 (NZT)

   MET_VARS.C:  AU Internet Accounting Meter snmp agent
        Based on the CMU snmpd version, snmpd.c

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

/*
 * $Log: met_vars.c,v $
 * Revision 1.1.1.2.2.12  2002/02/23 01:57:30  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.10  2001/05/24 02:19:54  nevil
 * LfapMet implemented by Remco Poortinga.
 * MinPDUs implemented by Nevil.
 *
 * Revision 1.1.1.2.2.7  2000/10/26 23:24:09  nevil
 * Write log_msg messages to meter.log file
 *
 * Revision 1.1.1.2.2.6  2000/08/08 19:44:52  nevil
 * 44b8 release
 *
 * Revision 1.1.1.2.2.4  2000/06/06 03:38:20  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2.2.1  2000/01/12 02:57:10  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:24  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.14  1999/09/24 04:09:47  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.13  1999/09/24 02:58:40  nevil
 * Polish up code to get rid of warning messages from Borland (DOS) compiler.
 * Make manager PeerAddress buffers NSAP_ADDR_LEN bytes long.
 * Add asn_lookup variable - only call FindSubnet if ruleset uses ASNs.
 *
 * Revision 1.1.1.1.2.12  1999/09/22 05:38:42  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.11  1999/09/14 00:45:28  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.10  1999/05/26 02:41:40  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.9  1999/05/17 00:11:57  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.8  1999/04/13 05:13:34  nevil
 * Use log_msg for ALL error messages
 *
 * Revision 1.1.1.1.2.7  1999/02/15 21:24:08  nevil
 * Distribution file for 4.3b9
 *
 * Revision 1.1.1.1.2.5  1999/01/20 03:55:50  nevil
 * Implementation of TCP attributes, part 4
 *
 * Revision 1.1.1.1.2.4  1999/01/08 01:31:54  nevil
 * Implementation of TCP attributes, part 3
 *
 * Revision 1.1.1.1.2.3  1998/12/22 23:53:09  nevil
 * Implementation of TCP attributes, part 2
 *
 * Revision 1.1.1.1  1998/11/16 03:57:29  nevil
 * Import of NeTraMet 4.3b3
 *
 * Revision 1.1.1.1  1998/11/16 03:22:01  nevil
 * Import of release 4.3b3
 *
 * Revision 1.1.1.1.2.1  1998/11/11 23:14:45  nevil
 * Only include malloc.h if we HAVE_MALLOC_H
 *
 * Revision 1.1.1.1  1998/10/28 20:31:27  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.3.1.2.2  1998/10/19 22:32:46  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:20  nevil
 * Added Nicolai's patches, some 'tidying up' of the source
 *
 * Revision 1.2  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 noTESTING
#define noPKTESTING
#define noRTTESTING
#define noPSTESTING
#define noGC_TEST
#define noH_TEST
#define DST_TESTING     0
#define TCP_TESTING     0
#define STR_DIST_DEBUG  1  /* Debug: why do stream distribs stop updating? */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <time.h>
#include <sys/types.h>

#include "ausnmp.h"

#if defined(DOS)
#include <alloc.h>
#include <mem.h>

#include "..\wattcp\tcp.h"

#else
#if HAVE_MALLOC_H
# include <malloc.h>
#endif
#include <sys/time.h>
#include <netinet/in.h>
#endif

#include "asn1.h"  /* CMU snmp include files */
#include "snmp.h"
#include "snmpimpl.h"
#include "mib.h"

#if defined(DOS) && !OCX_NTM && !OC3_NTM
#include "..\wattcp\pktdrv.h"
#endif

#include "pktsnap.h"
#include "flowhash.h"
#include "met_vars.h"  /* Constants for snmp oids */

extern Bit32 rsc_n_searches,  /* In flowhash.c */
   rsc_mx_range, rsc_av_range,  /* Flow index range searched */
   rsc_mx_tests, rsc_av_tests;  /* Nbr of TimeFilter tests */

#ifdef DEBUG
void paddr(Bit8 huge *a);
void daddr(Bit8 huge *a);
void pkey(char *msg, struct flow huge *n);
#endif

Bit32 retval;

#if !defined(DOS)
char syslog_name[256] = "";
FILE *log_file = NULL;  /* Local log file for Unix hosts */
char *logname = "meter.log";
#endif

void init_snmp(void)
{
#if defined(LOG_LOCAL) && !defined(DOS)
   if (*syslog_name) openlog(syslog_name, LOG_PID, LOG_LOCAL);
#endif
   }

#define flowMIB     1, 3, 6, 1, 2, 1, 40
#define U_AUCKLAND  1, 3, 6, 1, 4, 1, 411

char version_descr[] =
#if OCX_NTM || OC3_NTM
    "OCxMON: NeTraMet Meter "
#elif NF_CISCO_DATA
# if defined(__FLAT__)
   "NetFlowMet: 32-bit Meter "
# else
   "NetFlowMet: Network Meter "
# endif
#elif CR_DATA
   "NeTraMet: CoralReef Meter "
#elif DAG_DATA
   "NeTraMet: Dag Meter "
#elif defined(__FLAT__)
   "NeTraMet: 32-bit Meter "
#elif defined(DOS)
   "NeTraMet: 16-bit Meter "
#else
   "NeTraMet: Network Meter "
#endif
   ver_str;

oid version_id[] = {U_AUCKLAND, 1, ver_maj,ver_min, 0};  /* NeTraMet maj.min */

extern char interface_name[];  /* Device name for metered interface */
int bad_uptimes = 0;  /* For uptime debugging */
unsigned long pc_noflowpkts = 0;

/* New Algorithm for SNMPv2C:
   Variables now appear in the table below without trailing zeroes or
   subscript markers (MAX_SUBID), and the lengths in the table no
   longer include those trailers.  Each var_* routine must know the
   proper length of all its variables and act accordingly */

#define RSX  255  /* Rule Set Index */
#define IFX  255  /* Interface table index */
#define CTX  255  /* Collector table index */
#define MTX  255  /* Manager table index */
#define FTX  255  /* Flow table index */
#define TMX  255  /* Time index (first or last time) */
#define ANX  255  /* Attribute number index */
#define PSS  255  /* Package selector string */
#define RNX  255  /* Rule number index */

struct variable     variables[] = {
    /* these must be lexicographly ordered by the name field */
   {{MIB, 1, 1},       8, STRING,  VERSION_DESCR, RONLY, var_system},
   {{MIB, 1, 2},       8, OBJID,   VERSION_ID, RONLY, var_system},
   {{MIB, 1, 3},       8, TIMETICKS, UPTIME, RONLY, var_system},
   {{MIB, 2, 1},       8, INTEGER, IFNUMBER, RONLY, var_system},

/* {{flowMIB, 1, 1, 1, 1,  RSX  }, 11, INTEGER, RIINDEX, NOACCESS,var_rinfo}, */
   {{flowMIB, 1, 1, 1, 2/*,RSX*/}, 11, INTEGER, RIRULESIZE, RWRITE, var_rinfo},
   {{flowMIB, 1, 1, 1, 3/*,RSX*/}, 11, STRING, RIOWNER, RWRITE, var_rinfo},
   {{flowMIB, 1, 1, 1, 4/*,RSX*/}, 11, TIMETICKS, RITIMESTAMP, RONLY, var_rinfo},
   {{flowMIB, 1, 1, 1, 5/*,RSX*/}, 11, INTEGER, RISTATUS, RWRITE, var_rinfo},
   {{flowMIB, 1, 1, 1, 6/*,RSX*/}, 11, STRING, RINAME, RWRITE, var_rinfo},
/* {{flowMIB, 1, 1, 1, 7,  RSX  }, 11, INTEGER, RIRULESREADY, RWRITE, var_rinfo}, */
   {{flowMIB, 1, 1, 1, 8/*,RSX*/}, 11, INTEGER, RIFLOWCOUNT, RONLY, var_rinfo},

   {{flowMIB, 1, 2, 1, 1/*,IFX*/}, 11, INTEGER, IFACE_RATE, RWRITE, var_rinfo},
   {{flowMIB, 1, 2, 1, 2/*,IFX*/}, 11, COUNTER, IFACE_LOST, RONLY, var_rinfo},

/* {{flowMIB, 1, 3, 1, 1,  CTX  }, 11, INTEGER, CIINDEX, NOACCESS, var_rinfo}, */
   {{flowMIB, 1, 3, 1, 2/*,CTX*/}, 11, INTEGER, CITIMEOUT, RWRITE, var_rinfo},
   {{flowMIB, 1, 3, 1, 3/*,CTX*/}, 11, STRING, CIOWNER, RWRITE, var_rinfo},
   {{flowMIB, 1, 3, 1, 4/*,CTX*/}, 11, TIMETICKS, CILASTTIME, RWRITE, var_rinfo},
   {{flowMIB, 1, 3, 1, 5/*,CTX*/}, 11, TIMETICKS, CIPREVTIME, RONLY, var_rinfo},
   {{flowMIB, 1, 3, 1, 6/*,CTX*/}, 11, INTEGER, CISTATUS, RWRITE, var_rinfo},
   {{flowMIB, 1, 3, 1, 7/*,CTX*/}, 11, INTEGER, CIRULESET, RWRITE, var_rinfo},
   {{flowMIB, 1, 3, 1, 8/*,CTX*/}, 11, INTEGER, CIMINPDUS, RWRITE, var_rinfo},
   {{flowMIB, 1, 3, 1, 9/*,CTX*/}, 11, INTEGER, CITIMEMARK, RWRITE, var_rinfo},

/* {{flowMIB, 1, 4, 1, 1,  MTX  }, 11, INTEGER, MIINDEX, NOACCESS, var_rinfo}, */
   {{flowMIB, 1, 4, 1, 2/*,MTX*/}, 11, INTEGER, MICURRENTRULESET, RWRITE, var_rinfo},
   {{flowMIB, 1, 4, 1, 3/*,MTX*/}, 11, INTEGER, MISTANDBYRULESET, RWRITE, var_rinfo},
   {{flowMIB, 1, 4, 1, 4/*,MTX*/}, 11, INTEGER, MIHIGHWATERMARK, RWRITE, var_rinfo},
/* {{flowMIB, 1, 4, 1, 5  ,MTX  }, 11, INTEGER, MICOUNTERWRAP, RWRITE, var_rinfo}, */
   {{flowMIB, 1, 4, 1, 6/*,MTX*/}, 11, STRING, MIOWNER, RWRITE, var_rinfo},
   {{flowMIB, 1, 4, 1, 7/*,MTX*/}, 11, TIMETICKS, MITIMESTAMP, RONLY, var_rinfo},
   {{flowMIB, 1, 4, 1, 8/*,MTX*/}, 11, INTEGER, MISTATUS, RWRITE, var_rinfo},
   {{flowMIB, 1, 4, 1, 9/*,MTX*/}, 11, INTEGER, MIRUNNINGSTANDBY, RWRITE, var_rinfo},

   {{flowMIB, 1, 5},   9, INTEGER, FLOODMARK, RWRITE, var_system},
   {{flowMIB, 1, 6},   9, INTEGER, INACT_TIMEOUT, RWRITE, var_system},
   {{flowMIB, 1, 7},   9, INTEGER, ACTIVE_FLOWS, RWRITE, var_system},
   {{flowMIB, 1, 8},   9, INTEGER, MAX_FLOWS, RWRITE, var_system},
   {{flowMIB, 1, 9},   9, INTEGER, FLOOD_MODE, RWRITE, var_system},

/* {{flowMIB, 2, 1, 1, 1,  RSX,TMX,FTX  }, 11,INTEGER, FTFLOWINDEX, NOACCESS, var_ft}, */
/* {{flowMIB, 2, 1, 1, 2,  RSX,TMX,FTX  }, 11,TIMETICKS, FTFLOWTIMEMARK, NOACCESS, var_ft}, */
   {{flowMIB, 2, 1, 1, 3/*,RSX,TMX,FTX*/}, 11,INTEGER, FTFLOWSTATUS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1, 4/*,RSX,TMX,FTX*/}, 11,INTEGER, FTLOWINTERFACE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1, 5/*,RSX,TMX,FTX*/}, 11,INTEGER, FTLOWADJACENTTYPE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1, 6/*,RSX,TMX,FTX*/}, 11,STRING,  FTLOWADJACENTADDRESS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1, 7/*,RSX,TMX,FTX*/}, 11,STRING,  FTLOWADJACENTMASK, RONLY, var_ft},
   {{flowMIB, 2, 1, 1, 8/*,RSX,TMX,FTX*/}, 11,INTEGER, FTLOWPEERTYPE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1, 9/*,RSX,TMX,FTX*/}, 11,STRING,  FTLOWPEERADDRESS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,10/*,RSX,TMX,FTX*/}, 11,STRING,  FTLOWPEERMASK, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,11/*,RSX,TMX,FTX*/}, 11,INTEGER, FTLOWTRANSTYPE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,12/*,RSX,TMX,FTX*/}, 11,STRING,  FTLOWTRANSADDRESS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,13/*,RSX,TMX,FTX*/}, 11,STRING,  FTLOWTRANSMASK, RONLY, var_ft},

   {{flowMIB, 2, 1, 1,14/*,RSX,TMX,FTX*/}, 11,INTEGER, FTHIINTERFACE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,15/*,RSX,TMX,FTX*/}, 11,INTEGER, FTHIADJACENTTYPE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,16/*,RSX,TMX,FTX*/}, 11,STRING,  FTHIADJACENTADDRESS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,17/*,RSX,TMX,FTX*/}, 11,STRING,  FTHIADJACENTMASK, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,18/*,RSX,TMX,FTX*/}, 11,INTEGER, FTHIPEERTYPE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,19/*,RSX,TMX,FTX*/}, 11,STRING,  FTHIPEERADDRESS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,20/*,RSX,TMX,FTX*/}, 11,STRING,  FTHIPEERMASK, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,21/*,RSX,TMX,FTX*/}, 11,INTEGER, FTHITRANSTYPE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,22/*,RSX,TMX,FTX*/}, 11,STRING,  FTHITRANSADDRESS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,23/*,RSX,TMX,FTX*/}, 11,STRING,  FTHITRANSMASK, RONLY, var_ft},

   {{flowMIB, 2, 1, 1,24/*,RSX,TMX,FTX*/}, 11,INTEGER, FTPDUSCALE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,25/*,RSX,TMX,FTX*/}, 11,INTEGER, FTOCTETSCALE, RONLY, var_ft},
/* {{flowMIB, 2, 1, 1,26,  RSX,TMX,FTX  }, 11,INTEGER, FTRULESET, NOACCESS, var_ft}, */
   {{flowMIB, 2, 1, 1,27/*,RSX,TMX,FTX*/}, 11,COUNTER64, FTUPOCTETS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,28/*,RSX,TMX,FTX*/}, 11,COUNTER64, FTUPPDUS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,29/*,RSX,TMX,FTX*/}, 11,COUNTER64, FTDOWNOCTETS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,30/*,RSX,TMX,FTX*/}, 11,COUNTER64, FTDOWNPDUS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,31/*,RSX,TMX,FTX*/}, 11,TIMETICKS, FTFIRSTTIME, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,32/*,RSX,TMX,FTX*/}, 11,TIMETICKS, FTLASTTIME, RONLY, var_ft},

   {{flowMIB, 2, 1, 1,36/*,RSX,TMX,FTX*/}, 11,INTEGER, FTSOURCECLASS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,37/*,RSX,TMX,FTX*/}, 11,INTEGER, FTDESTCLASS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,38/*,RSX,TMX,FTX*/}, 11,INTEGER, FTFLOWCLASS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,39/*,RSX,TMX,FTX*/}, 11,INTEGER, FTSOURCEKIND, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,40/*,RSX,TMX,FTX*/}, 11,INTEGER, FTDESTKIND, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,41/*,RSX,TMX,FTX*/}, 11,INTEGER, FTFLOWKIND, RONLY, var_ft},

#if NEW_ATR
   {{flowMIB, 2, 1, 1,65/*,RSX,TMX,FTX*/}, 11,UINTEGER, FTDISTRIBUTIONS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,66/*,RSX,TMX,FTX*/}, 11,STRING, FTTOPACKETSIZE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,67/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMPACKETSIZE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,68/*,RSX,TMX,FTX*/}, 11,STRING, FTTOINTERARRIVALTIME, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,69/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMINTERARRIVALTIME, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,70/*,RSX,TMX,FTX*/}, 11,STRING, FTTOTURNAROUNDTIME1, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,71/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMTURNAROUNDTIME1, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,72/*,RSX,TMX,FTX*/}, 11,STRING, FTTOBITRATE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,73/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMBITRATE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,74/*,RSX,TMX,FTX*/}, 11,STRING, FTTOPDURATE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,75/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMPDURATE, RONLY, var_ft},

   {{flowMIB, 2, 1, 1,76/*,RSX,TMX,FTX*/}, 11,STRING, FTTOTCPTIME, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,77/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMTCPTIME, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,78/*,RSX,TMX,FTX*/}, 11,STRING, FTTOTCPSIZE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,79/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMTCPSIZE, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,80/*,RSX,TMX,FTX*/}, 11,STRING, FTTOTCPRATE1, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,81/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMTCPRATE1, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,82/*,RSX,TMX,FTX*/}, 11,STRING, FTTOTCPRATE2, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,83/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMTCPRATE2, RONLY, var_ft},

   {{flowMIB, 2, 1, 1,84/*,RSX,TMX,FTX*/}, 11,STRING, FTTOTURNAROUNDTIME2, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,85/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMTURNAROUNDTIME2, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,86/*,RSX,TMX,FTX*/}, 11,STRING, FTTOTURNAROUNDTIME3, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,87/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMTURNAROUNDTIME3, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,88/*,RSX,TMX,FTX*/}, 11,STRING, FTTOTURNAROUNDTIME4, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,89/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMTURNAROUNDTIME4, RONLY, var_ft},

   {{flowMIB, 2, 1, 1,90/*,RSX,TMX,FTX*/}, 11,STRING, FTTOFLOWOCTETS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,91/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMFLOWOCTETS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,92/*,RSX,TMX,FTX*/}, 11,STRING, FTTOFLOWPDUS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,93/*,RSX,TMX,FTX*/}, 11,STRING, FTFROMFLOWPDUS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,94/*,RSX,TMX,FTX*/}, 11,STRING, FTFLOWTIME, RONLY, var_ft},
#endif  /* NEW_ATR */

#if NF_OTHER_ATT
   {{flowMIB, 2, 1, 1,112/*,RSX,TMX,FTX*/}, 11,INTEGER, FTMETERID, RONLY, var_ft},
#endif
#if NF_ASN_ATT
   {{flowMIB, 2, 1, 1,113/*,RSX,TMX,FTX*/}, 11,STRING,  FTLOWROUTEASN, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,114/*,RSX,TMX,FTX*/}, 11,INTEGER, FTLOWROUTEPREFIX, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,115/*,RSX,TMX,FTX*/}, 11,STRING,  FTHIROUTEASN, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,116/*,RSX,TMX,FTX*/}, 11,INTEGER, FTHIROUTEPREFIX, RONLY, var_ft},
#endif

   {{flowMIB, 2, 1, 1,118/*,RSX,TMX,FTX*/}, 11,INTEGER, FTDSCODEPOINT, RONLY, var_ft},
#if NEW_ATR
   {{flowMIB, 2, 1, 1,121/*,RSX,TMX,FTX*/}, 11,INTEGER, FTTOLOSTPDUS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,122/*,RSX,TMX,FTX*/}, 11,INTEGER, FTFROMLOSTPDUS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,123/*,RSX,TMX,FTX*/}, 11,INTEGER, FTTOPQOVERFLOWS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,124/*,RSX,TMX,FTX*/}, 11,INTEGER, FTFROMPQOVERFLOWS, RONLY, var_ft},
   {{flowMIB, 2, 1, 1,125/*,RSX,TMX,FTX*/}, 11,STRING, FTTCPDATA, RONLY, var_ft},
#endif  /* NEW_ATR */

/* {{flowMIB, 2, 3, 1, 1,  PSS,RSX,TMX,FTX  },11, STRING, PTSELECTOR, NOACCESS, var_package}, */
/* {{flowMIB, 2, 3, 1, 2,  PSS,RSX,TMX,FTX  },11, INTEGER, PTRULESET, NOACCESS, var_package}, */
/* {{flowMIB, 2, 3, 1, 3,  PSS,RSX,TMX,FTX  },11, TIMETICKS, PTTIME, NOACCESS, var_package}, */
/* {{flowMIB, 2, 3, 1, 4,  PSS,RSX,TMX,FTX  },11, INTEGER, PTINDEX, NOACCESS, var_package}, */
   {{flowMIB, 2, 3, 1, 5/*,PSS,RSX,TMX,FTX*/},11, STRING, PTPACKAGE, RONLY, var_package},

/* {{flowMIB, 3, 1, 1, 1,  RSX,RNX  }, 11,INTEGER, RTRULESET, NOACCESS, var_rt}, */
/* {{flowMIB, 3, 1, 1, 2,  RSX,RNX  }, 11,INTEGER, RTRULEINDEX, NOACCESS, var_rt}, */
   {{flowMIB, 3, 1, 1, 3/*,RSX,RNX*/}, 11,INTEGER, RTSELECTOR, RWRITE, var_rt},
   {{flowMIB, 3, 1, 1, 4/*,RSX,RNX*/}, 11,STRING,  RTRULEMASK, RWRITE, var_rt},
   {{flowMIB, 3, 1, 1, 5/*,RSX,RNX*/}, 11,STRING,  RTMATCHVALUE, RWRITE, var_rt},
   {{flowMIB, 3, 1, 1, 6/*,RSX,RNX*/}, 11,INTEGER, RTRULEACTION, RWRITE, var_rt},
   {{flowMIB, 3, 1, 1, 7/*,RSX,RNX*/}, 11,INTEGER, RTJUMPINDEX, RWRITE, var_rt},

   {{U_AUCKLAND, 1,  1},  9, INTEGER, MSSTATSRESET, RWRITE, var_system},
   {{U_AUCKLAND, 1,  2},  9, INTEGER, MSSTATSTIME, RONLY, var_system},
   {{U_AUCKLAND, 1,  3},  9, INTEGER, MSNPACKETS, RONLY, var_system},
   {{U_AUCKLAND, 1,  4},  9, INTEGER, MSTBACKLOG, RONLY, var_system},
   {{U_AUCKLAND, 1,  5},  9, INTEGER, MSMXPKTRATE, RONLY, var_system},
   {{U_AUCKLAND, 1,  6},  9, INTEGER, MSMXBACKLOG, RONLY, var_system},
   {{U_AUCKLAND, 1,  7},  9, INTEGER, MSNFLOWS, RONLY, var_system},
   {{U_AUCKLAND, 1,  8},  9, INTEGER, MSFLOWSRCV, RONLY, var_system},
   {{U_AUCKLAND, 1,  9},  9, INTEGER, MSNMATCHES, RONLY, var_system},
   {{U_AUCKLAND, 1, 10},  9, INTEGER, MSHASHSRCHS, RONLY, var_system},
   {{U_AUCKLAND, 1, 11},  9, INTEGER, MSHASHCMPS, RONLY, var_system},
   {{U_AUCKLAND, 1, 12},  9, INTEGER, MSTHASHSZ, RONLY, var_system},
   {{U_AUCKLAND, 1, 13},  9, INTEGER, MSNHASHENTS, RONLY, var_system},
   {{U_AUCKLAND, 1, 14},  9, INTEGER, MSGCINTERVAL, RWRITE, var_system},
   {{U_AUCKLAND, 1, 15},  9, INTEGER, MAX_FLOWS, RONLY, var_system},
   {{U_AUCKLAND, 1, 16},  9, INTEGER, MSAVIDLEPER1000, RONLY, var_system},
   {{U_AUCKLAND, 1, 17},  9, INTEGER, MSMINIDLEPER1000, RONLY, var_system},
   {{U_AUCKLAND, 1, 18},  9, STRING,  INTERFACENAME, RONLY, var_system},

   {{U_AUCKLAND, 2, 1},   9, INTEGER, PCNEARMEM, RONLY, var_system},
   {{U_AUCKLAND, 2, 2},   9, INTEGER, PCFARMEM, RONLY, var_system},
   {{U_AUCKLAND, 2, 3},   9, INTEGER, PCBADPKTS, RONLY, var_system},
   {{U_AUCKLAND, 2, 4},   9, INTEGER, PCNOBUFPKTS, RONLY, var_system},
   {{U_AUCKLAND, 2, 5},   9, INTEGER, PCLOSTPKTS, RONLY, var_system},
   {{U_AUCKLAND, 2, 6},   9, INTEGER, PCBADTIMES, RONLY, var_system},

   {{U_AUCKLAND, 3, 1},   9, INTEGER, TR_STATE, RWRITE, var_system},
   {{U_AUCKLAND, 3, 2},   9, STRING,  TR_INFO, RONLY, var_system},
   {{U_AUCKLAND, 3, 3},   9, INTEGER, TR_TIME, RONLY, var_system}
   };

int compare(
   oid *name1, int len1,
   oid *name2, int len2)
{
   int len = len1 < len2 ? len1 : len2;
   while(len-- > 0) {  /* Find first non-matching byte */
      if (*name1 < *name2) return -1;
      if (*name2++ < *name1++) return 1;
      }
   /* Bytes match up to length of shorter string */
   if (len1 < len2) return -1;  /* name1 shorter, so it is "less" */
   if (len2 < len1) return 1;
   return 0;  /* Both strings are equal */
   }

oid newname[MAX_NAME_LEN];  /* Used in all the var_* functions */
int set_request;  /* True if this is a SET_REQ_MSG pdu */

/*
 * getStatPtr - return a pointer to the named variable, as well as it's
 * type, length, and access control list.
 *
 * If an exact match for the variable name exists, it is returned.  If not,
 * and exact is false, the next variable lexicographically after the
 * requested one is returned.
 *
 * If no appropriate variable can be found, NULL is returned.
 */
u_char *getStatPtr(
   oid *name,     /* IN: name of var, OUT: name matched */
   int *namelen,  /* IN: nbr sub-ids in name, OUT: sub-ids in matched name */
   u_char *type,  /* OUT: type of matched variable */
   int *len,      /* OUT: length of matched variable */
   u_short *acl,  /* OUT: access control list */
   int exact,     /* IN: TRUE if exact match wanted */
   write_fn_type *write_method,  /* OUT: 1 if function, 0 if char * */
   struct packet_info *pi,
   int *noSuchObject)
{
   struct variable *vp;
   u_char *access;
   int x, result, b,t;
   u_short cname[MAX_NAME_LEN];

   t = *namelen <= MAX_NAME_LEN ? *namelen : MAX_NAME_LEN;
   for (x = 0; x != t; ++x)  /* Make u_short copy of vbl name */
      cname[x] = name[x];

   *write_method = NULL;

   /* Find start row for variables[] search */
   b = 0;  t = sizeof(variables)/sizeof(struct variable) - 1;
   do {  /* Binary search averages about 6 compares instead of 22 */
      x = (b + t)/2;
      vp = &variables[x];
      result = memcmp((u_char *)cname, (u_char *)vp->name, sizeof(u_short)*
         (*namelen < (int)vp->namelen ? *namelen : (int)vp->namelen));
      if (result <= 0) t = x-1;  /* Move top down */
      else b = x+1; /* Move bottom up */
      } while (b <= t);
#ifdef PSTESTING
   printf("getStatPtr binary: x=%d, b=%d, t=%d\n", x,b,t);
#endif

   set_request = pi->pdutype == SET_REQ_MSG;
   for (x = t < 0 ? 0 : t, vp = &variables[x];
         x < sizeof(variables)/sizeof(struct variable);  ++vp, ++x) {
      /* Find first vp entry >= requested one */
      result = memcmp((u_char *)vp->name, (u_char *)cname, sizeof(u_short)*
         ((int)vp->namelen < *namelen ? (int)vp->namelen : *namelen));
      if (result < 0) continue;  /* Not there yet */

      for (b = 0; b != (int)vp->namelen; ++b)  /* Make oid name for vp */
         newname[b] = (oid)vp->name[b];
      *len = 0;
      access = (*(vp->findVar))(vp, name, namelen, exact, len, write_method);

#ifdef PSTESTING
      printf("getStatPtr linear: x=%d, result=%d, *len=%d\n", x,result,*len);
#endif
      *type = vp->type;
      *acl = vp->acl;
      if (access != NULL) {  /* access points to object value */
         *noSuchObject = FALSE;
         return access;
         }
      if (exact) {  /* Matched up to vp->namelen */
         *noSuchObject = *write_method == NULL;
            /* If we have a write method, we can create the object */
         return NULL;
         }
      if (*len != 0) {  /* Object found, was too big */
         *noSuchObject = FALSE;
         return NULL;
	 }
      /* Not exact (i.e. GETNEXT or GETBULK); keep looking */
      }
   *noSuchObject = TRUE;
#ifdef TESTING
   printf("getStatPtr failed\n");
#endif
   return NULL;
   }

extern Bit32 snmp_peer_addr;  /* Declared in meter_pc.c */

void display_msg(int timestamp, char *msg)
{
#ifdef DOS
   if (display_enabled) {
      scpos(0,scr_lrow);
      if (timestamp) printf("%02d%02d:%02d  ", tod_h,tod_m,tod_s);
      else printf("  ");
      printf(msg);
      w_roll(0,7, scr_chrtcol-1,scr_lrow, 1);
      }
#else
   time_t t; char *ts;
   if (display_enabled) {
      if (timestamp) {
         time(&t);  ts = ctime(&t);
         printf("%c%c%c%c:%c%c  ",
            ts[11],ts[12],ts[14],ts[15], ts[17],ts[18]);
         }
      else printf("  ");
      printf("%s\n",msg);
      }
#endif
   }

void log_msg(int priority, int die, char *fmt, ...)
{
/* priority = LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG
      (LOG_INFO messages aren't written to local log file)
   die = +1 to exit, 0 normally, -1 to leave log_file open
*/
   char msg[300];  /* Longest log message ~ 130, 13 Mar 01 !!! */
   va_list ap;
#if !defined(DOS)
   time_t t;  char *ts, ftb[32];
#endif

#if defined(LOG_LOCAL) && !defined(DOS)
   va_start(ap, fmt);
   if (*syslog_name) vsyslog(priority, fmt, ap);
#endif

   va_start(ap, fmt);
   vsprintf(msg, fmt, ap);
   va_end(ap);

#if !defined(DOS)  /* Write message to log file */
   if (priority != LOG_INFO) {
      if (log_file == NULL) {
         if ((log_file = fopen(logname,"a")) == NULL) {
            fprintf(stderr,"Failed to open logfile '%s':%s\n",
               logname, strerror(errno));
            exit(321);  /* Don't try to run without log file! */
            }
         }
      if (log_file != NULL) {
         time(&t);  ts = ctime(&t);
         strncpy(&ftb[0],  &ts[11], 9);  /* 17:31:42_ */
         strncpy(&ftb[9],  &ts[0],  4);  /* Thu_ */
         strncpy(&ftb[13], &ts[8],  3);  /* 23_  */
         strncpy(&ftb[16], &ts[4],  4);  /* Sep_ */
         strncpy(&ftb[20], &ts[20], 4);  /* 1997 */
         ftb[24] = '\0';
         fprintf(log_file,"%s (%2d): %s\n", ftb, priority, msg);
         }
      if (die >= 0) {
         fclose(log_file);
         log_file = NULL;
         }
      }
#endif
   display_msg(1,msg);

   if (die > 0) stop_meter(die);
   }

int alloc_rulespace(int rs, int rules, Bit16 size)
{  /* rules=1 to alloc rules space, =0 to alloc rule hash space */

   if (rs > MXRTBLS || rs < 1 || (rs == 1 && rules)) {
      log_msg(LOG_ERR, 0, "Alloc invalid rs ix !!!");
      return 0;
      }

   if (rules) {
      if (inuse_rules+size > mxrules) {
         log_msg(LOG_ERR, 0, "ri[%d]: No room for ruleset", rs);
         return 0;
         }
      ri[rs-1].ri_Size = size;
      ri[rs-1].ri_rule_offset = inuse_rules;
      ri[rs-1].ri_rule_table = &rule_space[inuse_rules];
      inuse_rules += size;
      }
   else {  /* rule hash table */
      if (inuse_rule_hash+size > mxrulehash) {
         log_msg(LOG_ERR, 0, "ri[%d]: No room for rule hash", rs);
         return 0;
         }
      ri[rs-1].ri_rule_hash_size = size;
      ri[rs-1].ri_rule_hash_offset = inuse_rule_hash;
      ri[rs-1].ri_rule_hash = &rule_hash_space[inuse_rule_hash];
      inuse_rule_hash += size;
      }
   return 1;
   }

int free_rulespace(int rs)
{
   struct rtinf_rec *rip;
   Bit32 rule_offset, rule_sz;
   Bit32 hash_offset, hash_sz;
   int j;

   if (rs < 1 || rs > MXRTBLS) {
      log_msg(LOG_ERR, 0, "Free invalid rs ix (%d)", rs);
      return 0;
      }
   if (rs == 1) return 1;

   rip = &ri[rs-1];
   rule_offset = rip->ri_rule_offset;  rule_sz = rip->ri_Size;
   hash_offset = rip->ri_rule_hash_offset;  hash_sz = rip->ri_rule_hash_size;
   for (j = 1; j != MXRTBLS; ++j) {
      if (j == rs-1) continue;
      rip = &ri[j];
      if (rip->ri_rule_offset > rule_offset) {
         rip->ri_rule_offset -= rule_sz;
         rip->ri_rule_table = &rule_space[rip->ri_rule_offset];
         }
      if (rip->ri_rule_hash_offset > hash_offset) {
         rip->ri_rule_hash_offset -= hash_sz;
         rip->ri_rule_hash =
            (Bit16 huge *)&rule_hash_space[rip->ri_rule_hash_offset];
         }
      }
   if (rule_offset+rule_sz != inuse_rules)  /* Not the top ruleset */
      memcpy(&rule_space[rule_offset],
         &rule_space[rule_offset+rule_sz],
         (inuse_rules-rule_offset-rule_sz)*sizeof(struct rule));
   inuse_rules -= rule_sz;
   if (hash_offset+hash_sz != inuse_rule_hash)  /* Not the top rule hash */
      memcpy(&rule_hash_space[hash_offset],
         &rule_hash_space[hash_offset+hash_sz],
         (inuse_rule_hash-hash_offset-hash_sz)*sizeof(Bit16));
   inuse_rule_hash -= hash_sz;
/* free_rulespace() is only called by set_RowStatus/RS_DESTROY,
         so we don't bother to reset these things here ..
   rip->ri_Size = rip->ri_rule_offset =
      rip->ri_rule_hash_size = rip->ri_rule_hash_offset = 0;
         Nevil, 17 May 99 */
   return 1;
   }

u_char *var_system(  /* System (MIB 2 and UA) variables */
   struct variable *vp, oid *name, int *length,
   int exact, int *var_len, write_fn_type *write_method)
{
#ifndef DOS
    struct timeval now, boottime;
#endif
   int result;
   unsigned long ul;

   newname[vp->namelen] = 0;
   result = compare(newname,(int)vp->namelen+1, name,*length);
   if ((exact && (result != 0)) || (!exact && (result <= 0)))
      return NULL;
   memcpy((u_char *)name, (u_char *)newname,
      ((int)vp->namelen+1)*sizeof(oid));
   *length = vp->namelen+1;

   *var_len = sizeof(retval);  /* Default length */
   switch (vp->magic){
   case VERSION_DESCR:
      *var_len = strlen(version_descr);
      return (u_char *)version_descr;
   case VERSION_ID:
      *var_len = sizeof(version_id);
      return (u_char *)version_id;
   case UPTIME:
      retval = uptime_cs();
      return (u_char *)&retval;
   case IFNUMBER:
      retval = 1;  /* 1 ethernet card to start with */
      return (u_char *)&retval;

   case FLOODMARK:
      *var_len = sizeof(FloodMark);
      return (u_char *)&FloodMark;
   case INACT_TIMEOUT:
      *var_len = sizeof(InactivityTimeout);
      return (u_char *)&InactivityTimeout;
   case ACTIVE_FLOWS:
      retval = active_flows();
      return (u_char *)&retval;
   case MAX_FLOWS:
      retval = mxflows;  /* Set at meter startup */
      return (u_char *)&retval;
   case FLOOD_MODE:
      *var_len = sizeof(FloodMode);
      return (u_char *)&FloodMode;

   case MSSTATSRESET:
      *write_method = &writeStatsReset;
      retval = 0;
      return (u_char *)&retval;
   case MSSTATSTIME:
      *var_len = sizeof(stats_time);
      return (u_char *)&stats_time;
   case MSNPACKETS:
      *var_len = sizeof(npackets);
      return (u_char *)&npackets;
   case MSTBACKLOG:
      *var_len = sizeof(t_backlog);
      return (u_char *)&t_backlog;
   case MSMXPKTRATE:
      *var_len = sizeof(max_pkt_rate);
      return (u_char *)&max_pkt_rate;
   case MSMXBACKLOG:
      *var_len = sizeof(max_pkt_backlog);
      return (u_char *)&max_pkt_backlog;
   case MSNFLOWS:
      *var_len = sizeof(nflows);
      return (u_char *)&nflows;
   case MSFLOWSRCV:
      *var_len = sizeof(FlowsRecovered);
      return (u_char *)&FlowsRecovered;
   case MSNMATCHES:
      *var_len = sizeof(n_matches);
      return (u_char *)&n_matches;
   case MSHASHSRCHS:
      *var_len = sizeof(n_hash_searches);
      return (u_char *)&n_hash_searches;
   case MSHASHCMPS:
      *var_len = sizeof(n_hash_compares);
      return (u_char *)&n_hash_compares;
   case MSTHASHSZ:
      *var_len = sizeof(fthashmod);
      return (u_char *)&fthashmod;
   case MSNHASHENTS:
      *var_len = sizeof(n_hash_ents);
      return (u_char *)&n_hash_ents;
   case MSGCINTERVAL:
      *var_len = sizeof(gc_interval);
      return (u_char *)&gc_interval;
   case MSAVIDLEPER1000:
#ifdef DOS
#  if OCX_MON || OC3_MON
      retval = 0;
#  else
      retval = kilodummypackets;
      if (dummypackets >= 500) ++retval;
      ul = retval+npackets/1000L;
      if (ul == 0) retval = 0;  /* Avoid divide by 0 */
      else retval = retval*1000L/ul;
#  endif
#else
      ul = realtime-realtime_org;
      if (ul == 0) retval = 0;
      else retval = 1000 - (proctime-proctime_org)*1000/ul;
#endif
      return (u_char *)&retval;
   case MSMINIDLEPER1000:
#ifdef DOS
#  if OCX_NTM || OC3_NTM
      retval = 0;
#  else
      ul = (mindummyrate+mdpacketrate)*10L;
      if (ul == 0) retval = 0;  /* Avoid divide by 0 */
      else retval = (mindummyrate*10000L+5L)/ul;
#  endif
#else
      retval = min_idle1000;
#endif
      return (u_char *)&retval;

#ifdef DOS
   case PCNEARMEM:
      retval = (Bit32)coreleft();
      return (u_char *)&retval;
   case PCFARMEM:
      retval = (Bit32)farcoreleft();
      return (u_char *)&retval;
   case PCBADPKTS:
      *var_len = sizeof(badpackets);
      return (u_char *)&badpackets;
   case PCNOBUFPKTS:
      *var_len = sizeof(nobufpackets);
      return (u_char *)&nobufpackets;
   case PCLOSTPKTS:
      retval = 
         noflowpackets-pc_noflowpkts +  /* No memory for flow (flowhash.c) */
         nobufpackets +  /* No Waterloo TCP buffer available (src\pcpkt.c) */
         badpackets +    /* Packet length > BUFSIZE (src\pcpkt.c) */
         lostpackets;    /* No pkt buffer available (meter_pc.c) */
      return (u_char *)&retval;
#else
   case PCLOSTPKTS:
      pkt_counts(0);
      retval = lostpackets+noflowpackets;
      return (u_char *)&retval;
#endif
   case PCBADTIMES:
      retval = bad_uptimes;
      return (u_char *)&retval;
   case INTERFACENAME:
      *var_len = strlen(interface_name);
      return (u_char *)interface_name;

#if CR_DATA || DAG_DIRECT
   case TR_STATE:
      *var_len = sizeof(trace_state);     /* If we want the default write routine, */
      return (u_char *)&trace_state;  /* we must refer to the actual variable */
#endif
   case TR_INFO:  /* Null if meter doesn't read trace files */
      *var_len = strlen(crl_info);
      return (u_char *)crl_info;
   case TR_TIME:
      retval = last_t_sec;
      return (u_char *)&retval;

   default:
      *var_len = 0;
      ERROR("");
      }
   return NULL;
   }

int string_OK(int t)
{
   if (t != STRING) {
      log_msg(LOG_ERR, 0, "Not string");  return FALSE;
      }
   return TRUE;
   }

int int_OK(int t)
{
   if (t != INTEGER) {
      log_msg(LOG_ERR, 0, "Not integer");  return FALSE;
      }
   return TRUE;
   }

int tr_status(int tbl, int x)
{
   switch (tbl) {
   case 1:  /* Rule Info table */
      return ri[x-1].ri_Status;
   case 3:  /* Reader Info table */
      return ci[x-1].ci_Status;
   case 4:  /* Manager Info table */
      return mi[x-1].mi_Status;
      }
   return RS_UNUSED;
   }

void set_tr_timestamp(int tbl, int x)
{
   switch (tbl) {
   case 1:  /* Rule Info table */
      ri[x-1].ri_TimeStamp = uptime_cs();
      break;
   case 4:  /* Manager Info table */
      mi[x-1].mi_TimeStamp = uptime_cs();
      break;
      }
   }

void recover_flows(int rs)
{
   struct flow huge *fp;
   int k;
   for (k = 1; k <= mxflows; ++k) {
      if (flow_idle(k)) continue;
#if FLOW_INDEX
      fp = flow_ix[k-1];
#else
      fp = &fa[k-1];
#endif
      if (fp->FlowRuleSet != rs) continue;  /* Not this ruleset */
      mark_idle(k);
      free_flow(fp);
      --ri[rs-1].ri_FlowRecords;
      --nflows;  ++FlowsRecovered;
      }
/* recover_flows() is only called by set_RowStatus/RS_DESTROY,
      ri[rs-1].ri_flow_chain is reset there.
   (It wasn't being reset at all, which caused strange problems
      when a ruleset info row was reused!)  Nevil, 17 May 99 */         
   }

void build_rdr_chains(char p)  /* Chain reader rows using same ruleset */
{  /* For nifty */
   int j, r;
   struct rdr_rec *cip;
   int rs_rdr_tail[MXRTBLS];

   memset(rs_rdr, 0, sizeof(rs_rdr));
   memset(rs_rdr_tail, 0, sizeof(rs_rdr_tail));
   for (j = 0; j != MXCLCTRS; ++j) {
      cip = &ci[j];
      if (cip->ci_RuleSet != 0) {
         if (rs_rdr[r = cip->ci_RuleSet-1] == NULL)
            rs_rdr[r] = cip;
         else ci[rs_rdr_tail[r]].ci_rs_next = cip;
         rs_rdr_tail[r] = j;  cip->ci_rs_next = NULL;
         }
      }

#if 0
   printf("B_R_C(%c): Checking rdr info table ..\n", p);
   for (j = 0; j != MXRTBLS; ++j) {
      if (rs_rdr[j] != NULL) {
         printf(
            "  j=%d, rs_rdr[%d]=%x, rs=%d, owner=%s, MinPDUs=%d, ci_next=%x\n",
            j, j,rs_rdr[j], rs_rdr[j]->ci_RuleSet, rs_rdr[j]->ci_Owner,
            rs_rdr[j]->ci_MinPDUs, rs_rdr[j]->ci_rs_next);
         for (cip = rs_rdr[j]; ; ) {
            r = cip - ci;  /* GNU cc gives ptr diff in elemnts! */
            printf("    row=%d, owner=%s, MinPDUs=%d, ci_next=%x\n",
               r, cip->ci_Owner, cip->ci_MinPDUs, cip->ci_rs_next);
            if ((cip = cip->ci_rs_next) == NULL) break;
	    }
         }
      }
   fflush(stdout);
#endif
   }

/* RowStatus actions are ..
      RESERVE1: Check types, lengths and values (GETs and SETs)
      RESERVE2: Ditto (only performed for SETs)
      COMMIT:   Do the set
      ACTION:   Execute any actions
      FREE:     Undo anything done by RESERVE1 and RESERVE2 */

int status_change_OK(int s_new, int s_old)
{
   switch (s_new) {
   case RS_ACTIVE:
      if (s_old == RS_UNUSED)
         return SNMP_ERR_NOCREATION;
      if (s_old != RS_NOTINSERVICE && s_old != RS_NOTREADY &&
            s_old != RS_SET_CREATE)
         return SNMP_ERR_INCONSISTENTVALUE;
      break;
   case RS_NOTINSERVICE:
      if (s_old == RS_UNUSED)
         return SNMP_ERR_NOCREATION;
      if (s_old != RS_ACTIVE && s_old != RS_NOTINSERVICE)
         return SNMP_ERR_INCONSISTENTVALUE;
      break;
   case RS_NOTREADY:
      return SNMP_ERR_INCONSISTENTVALUE;
   case RS_CREATEANDGO:
   case RS_CREATEANDWAIT:
      if (s_old != RS_UNUSED)
         return SNMP_ERR_INCONSISTENTVALUE;
   case RS_DESTROY:
      break;
      }
   return SNMP_ERR_NOERROR;
   }

int set_RowStatus(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int bigsize = 1000, which, row, r;
   Int32 reqval;
   struct rtinf_rec *rip;
   struct rdr_rec *cip;
   struct mgr_rec *mip;

   which = name[8];  row = name[11];
   if (!int_OK(var_val_type)) return SNMP_ERR_WRONGTYPE;
   asn_parse_int(var_val, &bigsize, &var_val_type,
      (Bit32 *)&reqval, sizeof(reqval));
   if (reqval < RS_UNUSED || reqval > RS_DESTROY)
      return SNMP_ERR_INCONSISTENTVALUE;
   switch(which) {
   case 1:  /* Rule info table */
      rip = &ri[row-1];
      break;
   case 3:  /* Reader info table */
      cip = &ci[row-1];
      break;
   case 4:  /* Manager info table */
      mip = &mi[row-1];
      break;
      }
   if (action == RESERVE2) {
      switch(which) {
      case 1:  /* Rule info table */
         rip->ri_old_status = rip->ri_Status;
         if (reqval == RS_DESTROY) {
            if (row == 1)  /* Can't destroy default ruleset! */
               return SNMP_ERR_INCONSISTENTVALUE;
            return rip->ri_running != 0 ? SNMP_ERR_INCONSISTENTVALUE
               : SNMP_ERR_NOERROR;
            }
         else {
            rip->ri_Status = RS_SET_CREATE;
            r = status_change_OK(reqval, rip->ri_old_status);
            if (r == SNMP_ERR_NOERROR && reqval == RS_ACTIVE &&
                  rip->ri_old_status == RS_NOTINSERVICE) {
               if (rip->ri_Size == 0)  /* No rules downloaded! */
                  return SNMP_ERR_INCONSISTENTVALUE;
               if (!optimise_rule_table(row))  /* Build rule hash tables */
                  return SNMP_ERR_RESOURCEUNAVAILABLE;
               else {
                  log_msg(LOG_WARNING, 0, "ri[%d]: '%s', rhss = %u", 
                     row, rip->ri_Name,rip->ri_rule_hash_size);
                  }
               }
            return r;   
            }
      case 3:  /* Reader info table */
         cip->ci_old_status = cip->ci_Status;
         cip->ci_Status = RS_SET_CREATE;
         return status_change_OK(reqval, cip->ci_old_status);
      case 4:  /* Manager info table */
         mip->mi_old_status = mip->mi_Status;
         mip->mi_Status = RS_SET_CREATE;
         return status_change_OK(reqval, mip->mi_old_status);
         }
      }
   else if (action == FREE) {
      switch(which) {
      case 1:  /* Rule info table */
         rip->ri_Status = rip->ri_old_status;
         break;
      case 3:  /* Reader Info table */
         cip->ci_Status = cip->ci_old_status;
         break;
      case 4:  /* Manager Info table */
         mip->mi_Status = mip->mi_old_status;
         break;
         }
      return SNMP_ERR_NOERROR;
      }
   else if (action == COMMIT) {
      switch(which) {
      case 1:  /* Rule info table */
         switch (reqval) {
         case RS_ACTIVE:
            rip->ri_Status = RS_ACTIVE;
            break;
         case RS_NOTINSERVICE:
            rip->ri_Status = RS_NOTINSERVICE;
            break;
         case RS_CREATEANDGO:
         case RS_CREATEANDWAIT:
            rip->ri_Status =
               reqval == RS_CREATEANDGO ? RS_ACTIVE : RS_NOTINSERVICE;
               /* notready = missing info needed by agent */
            /* Don't recover rules or hash tables */
            break;
         case RS_DESTROY:
            if (rip->ri_FlowRecords != 0)
               recover_flows(row);  /* 1-org */
            free_rulespace(row);  /* Recover rule space */
            /* Reset row info variables.  Nevil, 17 May 99 */
            memset(rip, 0, sizeof(struct rtinf_rec));
            rip->ri_Status = RS_UNUSED;
            break;
            }
         break;
      case 3:  /* Reader info table */
         switch (reqval) {
         case RS_ACTIVE:
            cip->ci_Status = RS_ACTIVE;
            break;
         case RS_NOTINSERVICE:
            cip->ci_Status = RS_NOTINSERVICE;
            break;
         case RS_CREATEANDGO:
         case RS_CREATEANDWAIT:
            cip->ci_Status =
               reqval == RS_CREATEANDGO ? RS_ACTIVE : RS_NOTINSERVICE;
            break;
         case RS_DESTROY:
            memset(cip, 0, sizeof(struct rdr_rec));
            cip->ci_Status = RS_UNUSED;
            build_rdr_chains('D');  /* For nifty */
            break;
            }
         break;
      case 4:  /* Manager info table */
         switch (reqval) {
         case RS_ACTIVE:
            mip->mi_Status = RS_ACTIVE;
            break;
         case RS_NOTINSERVICE:
            mip->mi_Status = RS_NOTINSERVICE;
            break;
         case RS_CREATEANDGO:
         case RS_CREATEANDWAIT:
            mip->mi_Status =
               reqval == RS_CREATEANDGO ? RS_ACTIVE : RS_NOTINSERVICE;
            break;
         case RS_DESTROY:
            if (mip->mi_RunningStandby == TV_FALSE)
               open_rule_set(mip->mi_CurrentRuleSet, 0);
            else open_rule_set(mip->mi_StandbyRuleSet, 0);
            memset(mip, 0, sizeof(struct mgr_rec));
            mip->mi_RunningStandby = TV_FALSE;
            mip->mi_Status = RS_UNUSED;
            break;
            }
         break;
         }
      set_tr_timestamp(which, row);
      }
   return SNMP_ERR_NOERROR;
   }

int set_Name(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int size, bigsize = 1000, which, row;
   u_char buf[30];

   if (!string_OK(var_val_type)) return SNMP_ERR_WRONGTYPE;
   size = sizeof(buf);
   asn_parse_string(var_val, &bigsize, &var_val_type, buf, &size);
   if (size > NAMESZ) {
      log_msg(LOG_ERR, 0, "Name too long");
      return SNMP_ERR_WRONGLENGTH;
      }
   which = name[8];  row = name[11];
   if (action == RESERVE2) {
      return tr_status(which, row) == RS_UNUSED
         ? SNMP_ERR_NOCREATION : SNMP_ERR_NOERROR;
      }
   else if (action == COMMIT) {
      memset(statP, 0, NAMESZ+1);
      memcpy(statP, buf, size);
      set_tr_timestamp(which, row);
      }
   return SNMP_ERR_NOERROR;
   }

int set_Integer(int action, u_char *var_val,  /* Sets a value in an info row */
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int bigsize = 1000, which, row;
   Int32 reqval;

   if (!int_OK(var_val_type)) return SNMP_ERR_WRONGTYPE;
   asn_parse_int(var_val, &bigsize, &var_val_type,
      (Bit32 *)&reqval, sizeof(reqval));
   which = name[8];  row = name[11];
   if (action == RESERVE2) {
      return tr_status(which, row) == RS_UNUSED
         ? SNMP_ERR_NOCREATION : SNMP_ERR_NOERROR;
      }
   else if (action == COMMIT) {
      *((int *)statP) = (int)reqval;
      set_tr_timestamp(which, row);
      }
   return SNMP_ERR_NOERROR;
   }

int set_RuleSize(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int bigsize = 1000, j, ri_row;
   Bit32 intval = 0, memory_size;
   struct rtinf_rec *rip;
   struct rule huge *rt;

   if (!int_OK(var_val_type)) return SNMP_ERR_WRONGTYPE;
   ri_row = name[11];
   if (ri_row == 1) {
      log_msg(LOG_ERR, 0, "Can't modify default rules");
      return SNMP_ERR_INCONSISTENTVALUE;
      }
   rip = &ri[ri_row-1];
   if (rip->ri_running != 0) {
      log_msg(LOG_ERR, 0, "Can't modify running ruleset");
      return SNMP_ERR_INCONSISTENTVALUE;
      }
   asn_parse_int(var_val, &bigsize, &var_val_type, &intval, sizeof(intval));
   if (action == RESERVE2) {
      if (rip->ri_Status == RS_UNUSED)
         return SNMP_ERR_NOCREATION;
      if (intval > 0xFFFF)  /* Won't fit in a Bit16 */
         return SNMP_ERR_INCONSISTENTVALUE;
      if (!alloc_rulespace(ri_row, 1, intval))  /* Space for rules */
         return SNMP_ERR_RESOURCEUNAVAILABLE;
      rip->ri_Size = intval;
      memset(rip->ri_rule_table, 0, sizeof(struct rule)*rip->ri_Size);
      }
   else if (action == COMMIT) {
      set_tr_timestamp(1, ri_row);  /* RuleInfo TimeStamp */
      log_msg(LOG_WARNING, 0, "ri[%d]: '%s', %d rules", 
         ri_row, rip->ri_Name,rip->ri_Size);
      }
   return SNMP_ERR_NOERROR;
   }

int set_RuleSet(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int bigsize = 1000, row, which, s,c;
   Bit32 intval = 0;
   struct mgr_rec *mip;

   if (!int_OK(var_val_type)) return SNMP_ERR_WRONGTYPE;
   asn_parse_int(var_val, &bigsize, &var_val_type, &intval, sizeof(intval));
   which = name[10];  row = name[11];
   mip = &mi[row-1];

   if (action == RESERVE2) {
      if (mip->mi_Status == RS_UNUSED)
         return SNMP_ERR_NOCREATION;
      if (which == 9) {  /* RunningStandby */
         if (intval != TV_TRUE && intval != TV_FALSE)
            return SNMP_ERR_INCONSISTENTVALUE;
         }
      else {
         if (intval < 1 || intval > MXRTBLS) {
            log_msg(LOG_ERR, 0, "Bad RuleSet index (%d)", intval);
            return SNMP_ERR_INCONSISTENTVALUE;
            }
         if (ri[intval-1].ri_Status != RS_ACTIVE) {
            log_msg(LOG_ERR, 0, "Rules not ready");
            return SNMP_ERR_RESOURCEUNAVAILABLE;
            }
         }
      }
   if (action == COMMIT) {
      if (which == 2) {  /* CurrentRuleSet */
         if ((c = mip->mi_CurrentRuleSet) == intval)
            return SNMP_ERR_NOERROR;  /* No change needed */
         if (mip->mi_RunningStandby == TV_FALSE) {
            open_rule_set(c,0);  /* Close old current ruleset */
            open_rule_set(intval, 1);  /* Open new one */
            }
         /* else running standby; just set CurrentRuleSet */
         mip->mi_CurrentRuleSet = intval;
         log_msg(LOG_WARNING, 0, 
            "Manager %d, Current set %d", row,(int)intval);
#if CR_DATA || DAG_DIRECT  /* Can read a trace file */
         if (trace_state == CT_WAIT_RULES)
            trace_state = CT_RULES_READY;
#endif
         }
      else if (which == 3) {  /* StandbyRuleSet */
         if ((s = mip->mi_StandbyRuleSet) == intval)
            return SNMP_ERR_NOERROR;  /* No change needed */
         if (mip->mi_RunningStandby == TV_TRUE) {
            open_rule_set(s,0);  /* Close old standby ruleset */
            open_rule_set(intval, 1);  /* Open new one */
            }
         /* else running current; just set StandbyRuleSet */
         mip->mi_StandbyRuleSet = intval;
         log_msg(LOG_WARNING, 0, 
            "Manager %d, Standby set %d", row,(int)intval);
         }
      else {  /* 9 -> RunningStandby */
         if (intval != mip->mi_RunningStandby) {
            c = mip->mi_CurrentRuleSet;
            s = mip->mi_StandbyRuleSet;
            if (mip->mi_RunningStandby == TV_TRUE) {  /* Switch to current */
               open_rule_set(s,0);
               open_rule_set(c,1);
               }
            else {  /* Running current, switch to standby */
               open_rule_set(c,0);
               open_rule_set(s,1);
               }
            mip->mi_RunningStandby = intval;
            log_msg(LOG_WARNING, 0, "Manager %d running %s", row,
               intval == TV_FALSE ? "Current" : "Standby");
            }
         }
      }
   return SNMP_ERR_NOERROR;
   }

int set_ReaderSet(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int bigsize = 1000, row;
   Bit32 intval = 0;
   struct rdr_rec *cip;

   if (!int_OK(var_val_type)) return SNMP_ERR_WRONGTYPE;
   asn_parse_int(var_val, &bigsize, &var_val_type, &intval, sizeof(intval));
   row = name[11];
   cip = &ci[row-1];

   if (action == RESERVE2) {
      if (cip->ci_Status == RS_UNUSED)
         return SNMP_ERR_NOCREATION;
      if (intval < 1 || intval > MXRTBLS)
         return SNMP_ERR_INCONSISTENTVALUE;
      }
   if (action == COMMIT) {
      cip->ci_RuleSet = intval;
      build_rdr_chains('R');  /* For nifty */
      }
   return SNMP_ERR_NOERROR;
   }

int set_LastTime(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int row, j, rsx;
   struct rdr_rec *cip, *cixp;
   Bit32 min_time;

   row = name[11];
   if (var_val_type != TIMETICKS)
      return SNMP_ERR_WRONGTYPE;
   cip = &ci[row-1];
   if (cip->ci_Status == RS_UNUSED) 
      return SNMP_ERR_NOCREATION;
   if (cip->ci_Status != RS_ACTIVE || cip->ci_RuleSet == 0)
      return SNMP_ERR_RESOURCEUNAVAILABLE;
   if (action == COMMIT) {
      cip->ci_PrevTime = cip->ci_LastTime;
      cip->ci_LastTime = uptime_cs();
      rsx = cip->ci_RuleSet;
      min_time = cip->ci_LastTime < InactivityTimeout ? 0
         : cip->ci_LastTime-InactivityTimeout;  /* Must be >= 0 */
      for (j = 0; j != MXCLCTRS; ++j) {
         cixp = &ci[j];
         if (cixp->ci_Status != RS_ACTIVE || cixp->ci_RuleSet != rsx)
            continue;  /* Not this rule set */
         if (cixp->ci_PrevTime < min_time)
            min_time = cixp->ci_PrevTime;
         }
      ri[rsx-1].ri_gc_time = min_time;
      log_msg(LOG_INFO, 0, 
         "'%s' flows read by %s", ri[rsx-1].ri_Name, cip->ci_Owner);
      already_complained = 0;  /* Let garbage collector run again */
      /* build_rdr_chains('L');  /* For nifty testing */
      }
   return SNMP_ERR_NOERROR;
   }

int writeMask(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int size, bigsize = 1000, j;
   Bit8 buf[RULE_ADDR_LEN];
   struct rule huge *rt, huge *r;

   if (!string_OK(var_val_type)) return SNMP_ERR_WRONGTYPE;
   for (j = 0; j != RULE_ADDR_LEN; ++j) buf[j] = 0;
   size = sizeof(buf);
   asn_parse_string(var_val, &bigsize, &var_val_type, buf, &size);
   if (size > var_val_len) {
      log_msg(LOG_ERR, 0, "Address too big");
      return SNMP_ERR_WRONGLENGTH;
      }
   for (j = 0; j != n_masks; ++j) {
      if (memcmp((u_char *)buf,(u_char *)&Masks[j].Val,
         RULE_ADDR_LEN) == 0) break;
      }
   if (j == n_masks && n_masks == MXMASKS) {
      log_msg(LOG_ERR, 0, "Too many masks");
      return SNMP_ERR_RESOURCEUNAVAILABLE;
      }
   if (action == COMMIT) {
#ifdef H_TEST
printf("wrAddr: sz=%d, statP=%Fp, addr=", size, statP);
daddr(statP);  printf("\n");
#endif
      if (j == n_masks) {
         memcpy((u_char *)&Masks[j].Val, (u_char *)buf,
            RULE_ADDR_LEN);
         ++n_masks;
         }
      rt = ri[name[11]-1].ri_rule_table;
      r = &rt[name[12]-1];
      r->RuleMaskVal = j;
      }
   return SNMP_ERR_NOERROR;
   }

int writeStatsReset(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int bigsize = 1000;
   Bit32 intval = 0;
   if (!int_OK(var_val_type)) return SNMP_ERR_WRONGTYPE;
   asn_parse_int(var_val, &bigsize, &var_val_type, &intval, sizeof(intval));
   if (action == COMMIT) zero_stats(1);
   return SNMP_ERR_NOERROR;
   }

int set_Bit16(int action, u_char *var_val,
   u_char var_val_type, int var_val_len,
   u_char *statP, oid *name, int name_len)
{
   int bigsize = 1000;
   Bit32 intval = 0;
   if (!int_OK(var_val_type)) return SNMP_ERR_WRONGLENGTH;
   asn_parse_int(var_val, &bigsize, &var_val_type, &intval, sizeof(intval));
   if (intval > 0xFFFF) return SNMP_ERR_INCONSISTENTVALUE;
   if (action == COMMIT) *((Bit16 *)statP) = (Bit16)intval;
   return SNMP_ERR_NOERROR;
   }

u_char *var_rinfo(struct variable *vp, oid *name, int *length,
   int exact, int *var_len, write_fn_type *write_method)
{
   int tblsz, t, x;
#ifdef TESTING
   int j, cmp;
#endif
   *write_method = NULL;
   t = (int)vp->name[8];
   if (t == 0 || t >= sizeof(tbl_size)) return NULL;
   tblsz = tbl_size[t];
   x = (int)name[11];
#ifdef TESTING
   scpos(0,scr_lrow);
   printf("\nvar_rinfo(): exact=%d, control=%d, x=%d, tblsz=%d, magic=%d\n   name=",
      exact,(int)name[8],x,tblsz,vp->magic);
   for (j = 0; j != *length; ++j) printf(".%d",name[j]);
   printf("\n");
#endif
   if (exact) {
      if (*length < 12) return NULL;
      if (x < 1 || x > tblsz) return NULL;
      newname[11] = (oid)x;
      if (compare(newname,(int)vp->namelen+1, name,*length) != 0) return NULL;
      if (tr_status(t,x) == RS_UNUSED) {  /* Row not created yet */
         if (!set_request) return NULL;  /* Read requests fail */
         }  /* Write requests checked in set_ routines */
      }
   else {
      if (compare(newname,11, name,11) != 0 || *length < 12) x = 0;
      for (++x;  x <= tblsz;  ++x) {
         newname[11] = (oid)x;
#ifdef TESTING
         cmp = compare(newname,(int)vp->namelen+1, name,*length);
         printf("x=%d, cmp=%d ", x,cmp);
         for (j = 0; j != (int)vp->namelen+1; ++j) printf(".%d",newname[j]);
         printf("\n");
         if (cmp > 0) {
#else
         if (compare(newname,(int)vp->namelen+1, name,*length) > 0) {
#endif
            if (tr_status(t,x) != RS_UNUSED) break;
            }
         }
      if (x > tblsz) return NULL;
      }
   memcpy((u_char *)name, (u_char *)newname, ((int)vp->namelen+1)*sizeof(oid));
   *length = (int)vp->namelen+1;

   *var_len = sizeof(retval);
   switch (vp->magic) {
   case RISTATUS:
      *write_method = &set_RowStatus;
      *var_len = sizeof(ri[x-1].ri_Status);
      return (u_char *)&ri[x-1].ri_Status;
   case RIRULESIZE:
      *write_method = &set_RuleSize;
      *var_len = sizeof(ri[x-1].ri_Size);
      return (u_char *)&ri[x-1].ri_Size;
   case RIOWNER:
      *write_method = &set_Name;
      *var_len = strlen(ri[x-1].ri_Owner);
      return (u_char *)ri[x-1].ri_Owner;
   case RITIMESTAMP:
      *var_len = sizeof(ri[x-1].ri_TimeStamp);
      return (u_char *)&ri[x-1].ri_TimeStamp;
   case RINAME:
      *write_method = &set_Name;
      *var_len = strlen(ri[x-1].ri_Name);
      return (u_char *)ri[x-1].ri_Name;
   case RIFLOWCOUNT:
      *var_len = sizeof(ri[x-1].ri_FlowRecords);
      return (u_char *)&ri[x-1].ri_FlowRecords;

   case IFACE_RATE:
#if defined(DOS)
# if OCX_NTM || OC3_NTM
      retval = 0L;
# else         
      retval = en_table[x-1].SampleRate;
# endif
#else  /* Unix */
      retval = iface_info(x,IFACE_RATE);
#endif
      return (u_char *)&retval;
   case IFACE_LOST:
#if defined(DOS)
# if OCX_NTM || OC3_NTM
      retval = 0L;  /* Don't know how to get these for OCxMON $$$ */
# else         
      retval = 
         en_table[x-1].lostpackets + en_table[x-1].noflowpackets;
# endif
#else  /* Unix */
      retval = iface_info(x,IFACE_LOST);
#endif
      return (u_char *)&retval;

   case CISTATUS:
      *write_method = &set_RowStatus;
      *var_len = sizeof(ci[x-1].ci_Status);
      return (u_char *)&ci[x-1].ci_Status;
   case CITIMEOUT:
      *write_method = &set_Integer;
      *var_len = sizeof(ci[x-1].ci_Timeout);
      return (u_char *)&ci[x-1].ci_Timeout;
   case CIOWNER:
      *write_method = &set_Name;
      *var_len = strlen(ci[x-1].ci_Owner);
      return (u_char *)ci[x-1].ci_Owner;
   case CILASTTIME:
      *write_method = &set_LastTime;
      *var_len = sizeof(ci[x-1].ci_LastTime);
      return (u_char *)&ci[x-1].ci_LastTime;
   case CIPREVTIME:
      *var_len = sizeof(ci[x-1].ci_PrevTime);
      return (u_char *)&ci[x-1].ci_PrevTime;
   case CIRULESET:
      *write_method = &set_ReaderSet;
      *var_len = sizeof(ci[x-1].ci_RuleSet);
      return (u_char *)&ci[x-1].ci_RuleSet;
   case CIMINPDUS:  /* For nifty */
      *write_method = &set_Integer;
      *var_len = sizeof(ci[x-1].ci_MinPDUs);
      return (u_char *)&ci[x-1].ci_MinPDUs;
   case CITIMEMARK:
      *write_method = &set_Integer;
      *var_len = sizeof(ci[x-1].ci_TimeMark);
      return (u_char *)&ci[x-1].ci_TimeMark;

   case MISTATUS:
      *write_method = &set_RowStatus;
      *var_len = sizeof(mi[x-1].mi_Status);
      return (u_char *)&mi[x-1].mi_Status;
   case MICURRENTRULESET:
      *write_method = &set_RuleSet;
      *var_len = sizeof(mi[x-1].mi_CurrentRuleSet);
      return (u_char *)&mi[x-1].mi_CurrentRuleSet;
   case MISTANDBYRULESET:
      *write_method = &set_RuleSet;
      *var_len = sizeof(mi[x-1].mi_StandbyRuleSet);
      return (u_char *)&mi[x-1].mi_StandbyRuleSet;
   case MIHIGHWATERMARK:
      *write_method = &set_Integer;
      *var_len = sizeof(mi[x-1].mi_HighWaterMark);
      return (u_char *)&mi[x-1].mi_HighWaterMark;
   case MIOWNER:
      *write_method = &set_Name;
      *var_len = strlen(mi[x-1].mi_Owner);
      return (u_char *)mi[x-1].mi_Owner;
   case MITIMESTAMP:
      *var_len = sizeof(mi[x-1].mi_TimeStamp);
      return (u_char *)&mi[x-1].mi_TimeStamp;
   case MIRUNNINGSTANDBY:
      *write_method = &set_RuleSet;
      *var_len = sizeof(mi[x-1].mi_RunningStandby);
      return (u_char *)&mi[x-1].mi_RunningStandby;

   default:
      *var_len = 0;
      ERROR("");
      }
   return NULL;
   }

#if NEW_ATR
Bit8 *asn_build_distribution(Bit8 *data, int *seq_len, 
   struct flow huge *f, int attrib)
{
   Bit8 *save_data, nul;
   int j, a, var_len, dummy_len;
   struct distribution *d;
   int nb;  /* Variables to handle dynamic distributions */
   Bit8 *mask_r, *value_r;
   Bit8 mask_b[DISTRIB_PARAM_LEN], value_b[DISTRIB_PARAM_LEN];

   save_data = data;
   data += 4;  *seq_len -= 4;
      /* asn_build_sequence() always takes 4 bytes */

   if ((f->distrib_bits & (1 << attrib-FTTOPACKETSIZE)) == 0) {
      nul = '\0';  /* Flow isn't building this distribution */
      data = asn_build_string(data, seq_len, ASN_OCTET_STR, 
         (u_char *)&nul, 1);
      }
   else {
      for (d = f->distrib_list; d != NULL; d = d->next) {
         if (d->selector == attrib) break;
         }
      if (d == NULL) {
         log_msg(LOG_ERR, 0, "Couldn't find distribution <<");
         return NULL;
         }
      if (d->non_empty) {
#if STR_DIST_DEBUG
         if (attrib == FTFLOWTIME) {
	   if (f->stdata == NULL)
              log_msg(LOG_WARNING, 0, "build_dist(): f->stdata==NULL,"
                 " flownbr=%lu, flowruleset=%u, flowkind=%u",
                 flow_nbr(f), f->FlowRuleSet, f->fk.FlowKind);
            }
#endif
         if (d->Transform == DS_DYN_REQ) {
            nb = d->n_values;
            if (d->n_values > d->Buckets) {  /* Distribution */
               mask_b[0] = DS_DYN_BKT;
               mask_b[1] = d->ScaleFactor;
               mask_b[2] = d->LowerLimit >> 8;
               mask_b[3] = d->LowerLimit & 0xFF;
               mask_b[4] = d->UpperLimit >> 8;
               mask_b[5] = d->UpperLimit & 0xFF;
               mask_r = mask_b;  value_r = d->value_params;;
               }
            else {  /* Actual values */
               memcpy(mask_b, d->mask_params, DISTRIB_PARAM_LEN);
               mask_b[0] = DS_DYN_PTS;
               memcpy(value_b, d->value_params, DISTRIB_PARAM_LEN);
               value_b[0] = nb;
               mask_r = mask_b;  value_r = value_b;
               }
            d->non_empty = d->n_values = 0;  /* Reset distrib values */
            d->max_val = d->lowerlim;  d->min_val = d->upperlim;
            }
         else {  /* Linear or log */
            nb = d->Buckets+1;
            mask_r = d->mask_params;  value_r = d->value_params;
            }

         data = asn_build_string(data, seq_len, ASN_OCTET_STR,
            (u_char *)mask_r, DISTRIB_PARAM_LEN);
         if (data == NULL) return NULL;
         data = asn_build_string(data, seq_len, ASN_OCTET_STR,
            (u_char *)value_r, DISTRIB_PARAM_LEN);
         if (data == NULL) return NULL;
         for (j = 0; j != nb; ++j) {
            data = asn_build_int(data, seq_len, ASN_INTEGER,
               (Bit32 *)&d->counts[j], sizeof(Bit32));
            if (data == NULL) return NULL;
            }
         }
      else {  /* No counts in this distribution */
         nul = '\0';
         data = asn_build_string(data, seq_len, ASN_OCTET_STR, 
            (u_char *)&nul, 1);
         }
      }

   var_len = data-save_data;
   dummy_len = 100;
   asn_build_sequence(save_data, &dummy_len,
      (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), var_len-4);

#if DST_TESTING
   for (j = 0; j != var_len; ++j) printf(" %02x", save_data[j]);
   printf("\n");
#endif
   return data;
   }

Bit8 *asn_build_tcp_data(Bit8 *data, int *seq_len, 
   struct flow huge *f)
{
   Bit8 *save_data;
   Bit32 zero;
   int j, a, var_len, dummy_len;
   struct stream_data *std;

   save_data = data;
   data += 4;  *seq_len -= 4;
      /* asn_build_sequence() always takes 4 bytes */

   if ((std = f->stdata) == NULL) {
      zero = 0;  /* Flow doesn't have stream data */
      data = asn_build_int(data, seq_len, ASN_INTEGER,
         &zero, sizeof(Bit32));
      }
   else {
      data = asn_build_int(data, seq_len, ASN_INTEGER,
         &std->n_streams, sizeof(Bit32));
      if (data == NULL) return NULL;
      data = asn_build_int(data, seq_len, ASN_INTEGER,
         &std->mx_active_streams, sizeof(Bit32));
      if (data == NULL) return NULL;

      data = asn_build_unsigned_int64(data, seq_len, COUNTER64,
         &std->ToTCPLenOctets, sizeof(counter64));
      if (data == NULL) return NULL;
      data = asn_build_unsigned_int64(data, seq_len, COUNTER64,
         &std->FromTCPLenOctets, sizeof(counter64));
      if (data == NULL) return NULL;

      data = asn_build_unsigned_int64(data, seq_len, COUNTER64,
         &std->ToTCPSeqOctets, sizeof(counter64));
      if (data == NULL) return NULL;
      data = asn_build_unsigned_int64(data, seq_len, COUNTER64,
         &std->FromTCPSeqOctets, sizeof(counter64));
      if (data == NULL) return NULL;

      data = asn_build_unsigned_int64(data, seq_len, COUNTER64,
         &std->ToTCPAckOctets, sizeof(counter64));
      if (data == NULL) return NULL;
      data = asn_build_unsigned_int64(data, seq_len, COUNTER64,
         &std->FromTCPAckOctets, sizeof(counter64));
      if (data == NULL) return NULL;

      data = asn_build_int(data, seq_len, ASN_INTEGER,
         &std->ToTCPDecrSeq, sizeof(Bit32));
      if (data == NULL) return NULL;
      data = asn_build_int(data, seq_len, ASN_INTEGER,
         &std->FromTCPDecrSeq, sizeof(Bit32));
      if (data == NULL) return NULL;
      }

   var_len = data-save_data;
   dummy_len = 100;
   asn_build_sequence(save_data, &dummy_len,
      (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), var_len-4);

#if TCP_TESTING
   for (j = 0; j != var_len; ++j) printf(" %02x", save_data[j]);
   printf("\n");
#endif
   return data;
   }
#endif  /* NEW_ATR */

Bit8 seq_buf[1500];  /* For data package return (must be global) */

u_char *var_ft(  /*  Flow Table */
   struct variable *vp,oid *name, int *length,
   int exact, int *var_len, write_fn_type *write_method)
{
   int rs;  /* Flow ruleset */
   Bit32 t_time;  /* Flow 'target' time */
   Bit32 x;  /* Flow index */
   struct flow huge *t;
   int pal;
#if NEW_ATR
   int distr_len;
   Bit8 *bd_result;
   int tcp_len;
   Bit8 *tcp_result;
#endif  /* NEW_ATR */
   struct rdr_rec *cip;  /* For nifty */
   int min_pdus;
#ifdef TESTING
   int j;
#endif
   rs = (int)name[11];
   t_time = (Bit32)name[12];
   x = (Bit32)name[13];

#ifdef TESTING
   scpos(0,scr_lrow);
   printf("var_ft(1): exact=%d,  name=", exact);
   for (j = 0; j != *length; ++j) printf(".%d",name[j]);
   printf("\n");
#endif
   if (exact) {
      if (*length < 14) return NULL;  /* 7 + 4 + 3 */
      if (compare(newname,11, name,11) != 0 /* No match on OID */
           || (t = find_flow(x)) == NULL  /* Doesn't exist */
           || t->FlowRuleSet != rs)  /* Not in the reqd ruleset */
         return NULL;
      }
   else {  /* Does oid match up to indexes? */
      if (compare(name,11, newname,11) != 0 ||  /* OID < flow attribute */
            *length < 12) {  /* No indeces given */
         rs = 1;  t_time = 0;  x = 0;
         }
      else {
         if (*length == 12) {  /* Ruleset given  */
            t_time = 0;  x = 0;
            }
         else if (*length == 13)  /* Ruleset and time value given */
            x = 0;
            }
      for ( ; ; ++rs, x = 0) {
         if (rs > MXRTBLS) return NULL;
         if (ri[rs-1].ri_FlowRecords == 0) continue;
            /* No flows in table */

         min_pdus = 0;  /* Find MinPDUs for this reader (for nifty) */
         for (cip = rs_rdr[rs-1]; cip != NULL; cip = cip->ci_rs_next) {
            if (cip->ci_TimeMark == t_time) {
               min_pdus = cip->ci_MinPDUs;
               break;
               }
            }

         if (x == 0) {
            x = ri[rs-1].ri_flow_chain;  /* Test first flow of rs */
            }
         else {
            t = find_flow(x);
            if (t->FlowRuleSet == rs)  /* Test next flow of rs */
               x = t->rs_next;
            else {  /* Linear search */
               for (++x;  x <= mxflows;  ++x) {
                  t = find_flow(x);
                  if (flow_idle(x))  /* Ignore deallocated flows */
                     continue;
                  if (t->FlowRuleSet == rs)
                     break;  /* Found the next flow of rs */
                  }
               if (x > mxflows) x = 0;
               }
            }
         for (;;) {
            if (x == 0) break;  /* No more flows for this rs */
            t = &fa[x-1];

            if (scmp32_ge(t->ntm_LastTime, t_time)) {
                  /* Was >, but TimeFilter requires >=.  Nevil, 10 Mar 98 */
                  /* Force signed copy to handle Bit32 wraparound */
               if (min_pdus == 0 ||
                  ( c64geint(t->UpOctets, min_pdus) ||  /* For nifty */
	            c64geint(t->DownOctets, min_pdus) ) )
                  break;  /* Active at or since target time */
	       }

            x = t->rs_next;
            }
         if (x != 0) break;
         }
      }
   newname[11] = (oid)rs;
   newname[12] = (oid)t_time;
   newname[13] = (oid)x;
   memcpy((u_char *)name, (u_char *)newname, ((int)vp->namelen+3)*sizeof(oid));
   *length = (int)vp->namelen+3;

   *write_method = 0;
   *var_len = sizeof(retval);
   pal = addr_len[t->fk.PeerAddrType];
#ifdef TESTING
   scpos(0,scr_lrow);
   printf("var_ft(2): x=%d, t=%Fp, pal=%d, lowpeer=", x,t,pal);
   for (j = 0; j != 4; ++j) printf(".%d",t->fk.Low.PeerAddress[j]);
   printf("\n");
#endif
   switch (vp->magic) {
   case FTFLOWSTATUS:
      retval = 2;  /* current(2) */
      return (u_char *)&retval;
   case FTLOWINTERFACE:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.Low.Interface;
   case FTHIINTERFACE:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.High.Interface;
   case FTLOWADJACENTTYPE:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.Low.AdjAddrType;
   case FTHIADJACENTTYPE:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.High.AdjAddrType;
   case FTLOWADJACENTADDRESS:
      *var_len = MAC_ADDR_LEN;
      return (u_char *)&t->fk.Low.AdjAddr_ms4;
   case FTLOWADJACENTMASK:
      *var_len = MAC_ADDR_LEN;
      return (u_char *)&Masks[t->fk.Low.AdjMaskVal].Val;
   case FTLOWPEERTYPE:
   case FTHIPEERTYPE:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.PeerAddrType;
   case FTLOWPEERADDRESS:
      *var_len = pal;
      return (u_char *)&t->fk.Low.PeerAddress;
   case FTLOWPEERMASK:
      *var_len = pal;
      return (u_char *)&Masks[t->fk.Low.PeerMaskVal].Val;
   case FTLOWTRANSTYPE:
   case FTHITRANSTYPE:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.TransAddrType;
   case FTLOWTRANSADDRESS:
      *var_len = TRANS_ADDR_LEN;
      return (u_char *)&t->fk.Low.TransAddress;
   case FTLOWTRANSMASK:
      *var_len = TRANS_ADDR_LEN;
      return (u_char *)&Masks[t->fk.Low.TransMaskVal].Val;
   case FTHIADJACENTADDRESS:
      *var_len = MAC_ADDR_LEN;
      return (u_char *)&t->fk.High.AdjAddr_ms4;
   case FTHIADJACENTMASK:
      *var_len = MAC_ADDR_LEN;
      return (u_char *)&Masks[t->fk.High.AdjMaskVal].Val;
   case FTHIPEERADDRESS:
      *var_len = pal;
      return (u_char *)&t->fk.High.PeerAddress;
   case FTHIPEERMASK:
      *var_len = pal;
      return (u_char *)&Masks[t->fk.High.PeerMaskVal].Val;
   case FTHITRANSADDRESS:
      *var_len = TRANS_ADDR_LEN;
      return (u_char *)&t->fk.High.TransAddress;
   case FTHITRANSMASK:
      *var_len = TRANS_ADDR_LEN;
      return (u_char *)&Masks[t->fk.High.TransMaskVal].Val;
   case FTUPOCTETS:
      *var_len = sizeof(counter64);
      return (u_char *)&t->UpOctets;
   case FTUPPDUS:
      *var_len = sizeof(counter64);
      return (u_char *)&t->UpPDUs;
   case FTDOWNOCTETS:
      *var_len = sizeof(counter64);
      return (u_char *)&t->DownOctets;
   case FTDOWNPDUS:
      *var_len = sizeof(counter64);
      return (u_char *)&t->DownPDUs;
   case FTFIRSTTIME:
      return (u_char *)&t->FirstTime;
   case FTLASTTIME:
      return (u_char *)&t->LastTime;
   case FTSOURCECLASS:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.Low.Class;
   case FTDESTCLASS:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.High.Class;
   case FTFLOWCLASS:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.FlowClass;
   case FTSOURCEKIND:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.Low.Kind;
   case FTDESTKIND:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.High.Kind;
   case FTFLOWKIND:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.FlowKind;
   case FTDSCODEPOINT:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.DSCodePoint;

#if NF_ASN_ATT
   case FTLOWROUTEASN:
      *var_len = TRANS_ADDR_LEN;
      return (u_char *)&t->fk.Low.RouteASN;
   case FTHIROUTEASN:
      *var_len = TRANS_ADDR_LEN;
      return (u_char *)&t->fk.High.RouteASN;
   case FTLOWROUTEPREFIX:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.Low.RoutePrefix;
   case FTHIROUTEPREFIX:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.High.RoutePrefix;
#endif
#if NF_OTHER_ATT
   case FTMETERID:
      *var_len = sizeof(Bit8);
      return (u_char *)&t->fk.MeterId;
#endif

#if NEW_ATR
   case FTDISTRIBUTIONS:
      *var_len = sizeof(Bit32);
      return (u_char *)&t->distrib_bits;
   case FTTOPACKETSIZE:
   case FTFROMPACKETSIZE:
   case FTTOINTERARRIVALTIME:
   case FTFROMINTERARRIVALTIME:
   case FTTOBITRATE:
   case FTFROMBITRATE:
   case FTTOPDURATE:
   case FTFROMPDURATE:

   case FTTOTURNAROUNDTIME1:
   case FTFROMTURNAROUNDTIME1:
   case FTTOTURNAROUNDTIME2:
   case FTFROMTURNAROUNDTIME2:
   case FTTOTURNAROUNDTIME3:
   case FTFROMTURNAROUNDTIME3:
   case FTTOTURNAROUNDTIME4:
   case FTFROMTURNAROUNDTIME4:

   case FTTOTCPSIZE:
   case FTFROMTCPSIZE:
   case FTTOTCPTIME:
   case FTFROMTCPTIME:
   case FTTOTCPRATE1:
   case FTFROMTCPRATE1:
   case FTTOTCPRATE2:
   case FTFROMTCPRATE2:

   case FTTOFLOWOCTETS:
   case FTFROMFLOWOCTETS:
   case FTTOFLOWPDUS:
   case FTFROMFLOWPDUS:
   case FTFLOWTIME:

      distr_len = sizeof(seq_buf)-100;
      bd_result = asn_build_distribution(seq_buf, &distr_len, t, vp->magic);
      *var_len = sizeof(seq_buf)-100 - distr_len;
      if (bd_result == NULL) return NULL;
      else return (u_char *)seq_buf;
#endif  /* NEW_ATR */

#if NEW_ATR
   case FTTOLOSTPDUS:
      retval = t->stdata != NULL ? t->stdata->ToLostPDUs : 0;
      return (u_char *)&retval;
   case FTFROMLOSTPDUS:
      retval = t->stdata != NULL ? t->stdata->FromLostPDUs : 0;
      return (u_char *)&retval;
   case FTTOPQOVERFLOWS:  /* PQOF */
      retval = t->stdata != NULL ? t->stdata->ToPQOverflows : 0;
      return (u_char *)&retval;
   case FTFROMPQOVERFLOWS:
      retval = t->stdata != NULL ? t->stdata->FromPQOverflows : 0;
      return (u_char *)&retval;

   case FTTCPDATA:
      tcp_len = sizeof(seq_buf)-100;
      tcp_result = asn_build_tcp_data(seq_buf, &tcp_len, t);
      *var_len = sizeof(seq_buf)-100 - tcp_len;
      if (tcp_result == NULL) return NULL;
      else return (u_char *)seq_buf;
#endif  /* NEW_ATR */

   default:
      *var_len = 0;
      ERROR("");
      }
   return NULL;
   }

Bit32 last_t_time = 0;  /* <> */
u_char *var_package(  /* Data Package table */
   struct variable *vp, oid *name, int *length,
   int exact, int *var_len, write_fn_type *write_method)
{
   Bit8 attribs[MAX_NAME_LEN];  /* 'Selector' index */
   int a_len, j,k, pal, seq_len, dummy_len;
   int rs;  /* Flow ruleset */
   Bit32 t_time;  /* Flow 'target' time */
   Bit32 x;  /* Flow index */
   struct flow huge *t;
   struct rdr_rec *cip;  /* For nifty */
   int min_pdus;
   Bit8 *data;
   int x0, nt;

#ifdef PKTESTING
   scpos(0,scr_lrow);
   printf("var_package(1): exact=%d,  name=", exact);
   for (j = 0; j != *length; ++j) printf(".%d",name[j]);
   printf("\n");
#endif
   if (*length < 13) return NULL;  /* 7 + 4 + 2 */
      /* Must have a selector with at least one attribute requested */
   a_len = (int)name[11];
   if (*length < 11+1+a_len) return NULL;  /* Bad selector length */
   newname[11] = (oid)a_len;
   for (j = 0, k = 12; j != a_len; ++j, ++k) {
      attribs[j] = (Bit8)name[k];
      newname[k] = (oid)attribs[j];
      }
   rs = (int)name[k];
   t_time = (Bit32)name[k+1];
   x = (Bit32)name[k+2];
#ifdef PKTESTING
   printf("%d attribs =", a_len);
   for (j = 0; j != a_len; ++j) printf(" %u", attribs[j]);
   printf("\nrs=%u, t_time=%d(%d), x=%u\n", rs,t_time,sizeof(t_time),x);
#endif
   if (exact) {
      if (*length != k+3) return NULL;
      if (compare(newname,11, name,11) != 0 /* No match on OID */
           || (t = find_flow(x)) == NULL  /* Doesn't exist */
           || t->FlowRuleSet != rs)  /* Not in the reqd ruleset */
         return NULL;
      }
   else {  /* Does oid match up to indexes? */
      if (compare(name,11, newname,11) != 0 ||  /* OID < flow attribute */
            *length < k+1) {  /* No indeces given */
         rs = 1;  t_time = 0;  x = 0;
         }
      else {
         if (*length == k+1) {  /* Ruleset given  */
            t_time = 0;  x = 0;
            }
         else if (*length == k+2)  /* Ruleset and time value given */
            x = 0;
         }

      for ( ; ; ++rs, x = 0) {
         if (rs > MXRTBLS) return NULL;
         if (ri[rs-1].ri_FlowRecords == 0) continue;
            /* No flows in table */

         min_pdus = 0;  /* Find MinPDUs for this reader (for nifty) */
         for (cip = rs_rdr[rs-1]; cip != NULL; cip = cip->ci_rs_next) {
            if (cip->ci_TimeMark == t_time) {
               min_pdus = cip->ci_MinPDUs;
               break;
               }
            }

         x0 = x;  nt = 0;
         if (x == 0) {
            x = ri[rs-1].ri_flow_chain;  /* Test first flow of rs */
            }
         else {
            t = find_flow(x);
            if (t->FlowRuleSet == rs)  /* Test next flow of rs */
               x = t->rs_next;
            else {  /* Linear search */
               for (++x;  x <= mxflows;  ++x) {
                  t = find_flow(x);
                  if (flow_idle(x))  /* Ignore deallocated flows */
                     continue;
                  if (t->FlowRuleSet == rs)
                     break;  /* Found the next flow of rs */
                  }
               if (x > mxflows) x = 0;
               }
            }
         for (;;) {
            if (x == 0) break;  /* No more flows for this rs */
            t = find_flow(x);
            if (t == NULL) {
               log_msg(LOG_ERR, 0, 
                  "ri[%d]: Couldn't find flow %lu !!!", rs, x);
               x = 0;  /* Assume no more flows for this ruleset */
               break;
               }
            ++nt;

            if (scmp32_ge(t->ntm_LastTime, t_time)) {
                  /* Was >, but TimeFilter requires >=.  Nevil, 10 Mar 98 */
                  /* Force signed copy to handle Bit32 wraparound */
               if (min_pdus == 0 ||
                  ( c64geint(t->UpPDUs, min_pdus) ||  /* For nifty */
                    c64geint(t->DownPDUs, min_pdus) ) )
                  break;  /* Active at or since target time */
	       }

            x = t->rs_next;
            }

         if (x != 0) {
            if ((x0 = x-x0) > rsc_mx_range) rsc_mx_range = x0;
            rsc_av_range += x0;
            if (nt > rsc_mx_tests) rsc_mx_tests = nt;
            rsc_av_tests += nt;
            ++rsc_n_searches;
	    break;  /* Break outer loop (flow is in this ruleset) */
            }

         }
      }

   newname[k] = (oid)rs;
   newname[k+1] = (oid)t_time;
   newname[k+2] = (oid)x;
   memcpy((u_char *)name, (u_char *)newname, (k+3)*sizeof(oid));
   *length = (int)k+3;

   pal = addr_len[t->fk.PeerAddrType];
   switch (vp->magic) {
   case PTPACKAGE:
      data = seq_buf;  
      seq_len = sizeof(seq_buf)-100;  /* Leave room for header etc! */
      data += 4;  seq_len -= 4;
         /* asn_build_sequence() always takes 4 bytes */
      for (j = 0; j != a_len; ++j) {
         switch (attribs[j]) {
         case FTFLOWSTATUS:
            retval = 2;  /* current(2) */
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               &retval, sizeof(retval));
            break;
         case FTLOWINTERFACE:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.Low.Interface, sizeof(Bit8));
            break;
         case FTHIINTERFACE:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.High.Interface, sizeof(Bit8));
            break;
         case FTLOWADJACENTTYPE:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.Low.AdjAddrType, sizeof(Bit8));
            break;
         case FTHIADJACENTTYPE:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.High.AdjAddrType, sizeof(Bit8));
            break;
         case FTLOWADJACENTADDRESS:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&t->fk.Low.AdjAddr_ms4, MAC_ADDR_LEN);
            break;
         case FTLOWADJACENTMASK:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&Masks[t->fk.Low.AdjMaskVal].Val, MAC_ADDR_LEN);
            break;
         case FTLOWPEERTYPE:
         case FTHIPEERTYPE:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.PeerAddrType, sizeof(Bit8));
            break;
         case FTLOWPEERADDRESS:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&t->fk.Low.PeerAddress, pal);
            break;
         case FTLOWPEERMASK:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&Masks[t->fk.Low.PeerMaskVal].Val, pal);
            break;
         case FTLOWTRANSTYPE:
         case FTHITRANSTYPE:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.TransAddrType, sizeof(Bit8));
            break;
         case FTLOWTRANSADDRESS:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&t->fk.Low.TransAddress, TRANS_ADDR_LEN);
            break;
         case FTLOWTRANSMASK:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&Masks[t->fk.Low.TransMaskVal].Val, TRANS_ADDR_LEN);
            break;
         case FTHIADJACENTADDRESS:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&t->fk.High.AdjAddr_ms4, MAC_ADDR_LEN);
            break;
         case FTHIADJACENTMASK:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&Masks[t->fk.High.AdjMaskVal].Val, MAC_ADDR_LEN);
            break;
         case FTHIPEERADDRESS:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&t->fk.High.PeerAddress, pal);
            break;
         case FTHIPEERMASK:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&Masks[t->fk.High.PeerMaskVal].Val, pal);
            break;
         case FTHITRANSADDRESS:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&t->fk.High.TransAddress, TRANS_ADDR_LEN);
            break;
         case FTHITRANSMASK:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&Masks[t->fk.High.TransMaskVal].Val, TRANS_ADDR_LEN);
            break;
         case FTRULESET:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->FlowRuleSet, sizeof(Bit8));
            break;
         case FTUPOCTETS:
            data = asn_build_unsigned_int64(data, &seq_len, COUNTER64,
               &t->UpOctets, sizeof(counter64));
            break;
         case FTUPPDUS:
            data = asn_build_unsigned_int64(data, &seq_len, COUNTER64,
               &t->UpPDUs, sizeof(counter64));
            break;
         case FTDOWNOCTETS:
            data = asn_build_unsigned_int64(data, &seq_len, COUNTER64,
               &t->DownOctets, sizeof(counter64));
            break;
         case FTDOWNPDUS:
            data = asn_build_unsigned_int64(data, &seq_len, COUNTER64,
               &t->DownPDUs, sizeof(counter64));
            break;
         case FTFIRSTTIME:
            data = asn_build_int(data, &seq_len, TIMETICKS,
               &t->FirstTime, sizeof(Bit32));
            break;
         case FTLASTTIME:
            data = asn_build_int(data, &seq_len, TIMETICKS,
               &t->LastTime, sizeof(Bit32));
            break;
         case FTSOURCECLASS:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.Low.Class, sizeof(Bit8));
            break;
         case FTDESTCLASS:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.High.Class, sizeof(Bit8));
            break;
         case FTFLOWCLASS:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.FlowClass, sizeof(Bit8));
            break;
         case FTSOURCEKIND:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.Low.Kind, sizeof(Bit8));
            break;
         case FTDESTKIND:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.High.Kind, sizeof(Bit8));
            break;
         case FTFLOWKIND:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.FlowKind, sizeof(Bit8));
            break;
         case FTDSCODEPOINT:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.DSCodePoint, sizeof(Bit8));
            break;

#if NF_ASN_ATT
         case FTLOWROUTEASN:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&t->fk.Low.RouteASN, TRANS_ADDR_LEN);
            break;
         case FTHIROUTEASN:
            data = asn_build_string(data, &seq_len, ASN_OCTET_STR,
               (u_char *)&t->fk.High.RouteASN, TRANS_ADDR_LEN);
            break;
         case FTLOWROUTEPREFIX:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.Low.RoutePrefix, sizeof(Bit8));
            break;
         case FTHIROUTEPREFIX:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.High.RoutePrefix, sizeof(Bit8));
            break;
#endif
#if NF_OTHER_ATT
         case FTMETERID:
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               (Bit32 *)&t->fk.MeterId, sizeof(Bit8));
            break;
#endif
#if NEW_ATR
         case FTDISTRIBUTIONS:
            data = asn_build_unsigned_int(data, &seq_len, UINTEGER,
               (Bit32 *)&t->distrib_bits, sizeof(Bit32));
            break;
         case FTTOPACKETSIZE:
         case FTFROMPACKETSIZE:
         case FTTOINTERARRIVALTIME:
         case FTFROMINTERARRIVALTIME:
         case FTTOBITRATE:
         case FTFROMBITRATE:
         case FTTOPDURATE:
         case FTFROMPDURATE:

         case FTTOTURNAROUNDTIME1:
         case FTFROMTURNAROUNDTIME1:
         case FTTOTURNAROUNDTIME2:
         case FTFROMTURNAROUNDTIME2:
         case FTTOTURNAROUNDTIME3:
         case FTFROMTURNAROUNDTIME3:
         case FTTOTURNAROUNDTIME4:
         case FTFROMTURNAROUNDTIME4:

         case FTTOTCPSIZE:
         case FTFROMTCPSIZE:
         case FTTOTCPTIME:
         case FTFROMTCPTIME:
         case FTTOTCPRATE1:
         case FTFROMTCPRATE1:
         case FTTOTCPRATE2:
         case FTFROMTCPRATE2:

         case FTTOFLOWOCTETS:
         case FTFROMFLOWOCTETS:
         case FTTOFLOWPDUS:
         case FTFROMFLOWPDUS:
         case FTFLOWTIME:

            data = asn_build_distribution(data, &seq_len, t, attribs[j]);
            break;

         case FTTOLOSTPDUS:
            retval = t->stdata != NULL ? t->stdata->ToLostPDUs : 0;
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               &retval, sizeof(retval));
            break;
         case FTFROMLOSTPDUS:
            retval = t->stdata != NULL ? t->stdata->FromLostPDUs : 0;
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               &retval, sizeof(retval));
            break;
         case FTTOPQOVERFLOWS:  /* PQOF */
            retval = t->stdata != NULL ? t->stdata->ToPQOverflows : 0;
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               &retval, sizeof(retval));
            break;
         case FTFROMPQOVERFLOWS:
            retval = t->stdata != NULL ? t->stdata->FromPQOverflows : 0;
            data = asn_build_int(data, &seq_len, ASN_INTEGER,
               &retval, sizeof(retval));
            break;

         case FTTCPDATA:
            data = asn_build_tcp_data(data, &seq_len, t);
            break;
#endif  /* NEW_ATR */

         default:
            ERROR("");
            data = asn_build_null(data, &seq_len, ASN_NULL);
            }
         if (data == NULL) {  /* Package too big! */ 
            *var_len = 1;  /* TOOBIG rather than ENDOFMIB */
            return NULL;
	    }
         }
      *var_len = data-seq_buf;
      data = seq_buf;
      dummy_len = sizeof(seq_buf);
      asn_build_sequence(data, &dummy_len,
         (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), *var_len-4);
#ifdef PKTESTING
      for (j = 0; j != *var_len; ++j) printf(" %02x", seq_buf[j]);
      printf("\n");
#endif
      return (u_char *)seq_buf;

   default:
      *length = 0;
      ERROR("");
      }
   return NULL;
   }

u_char *var_rt(  /* Rule table */
   struct variable *vp, oid *name, int *length,
   int exact, int *var_len, write_fn_type *write_method)
{
   int rule_set;
   Bit16 x, sz;
   struct rule huge *r, huge *rt;
#ifdef RTTESTING
   int j, cmp;
   rule_set = (int)name[11];
   x = (Bit16)name[12];
   scpos(0,scr_lrow);
   printf("var_rt(): exact=%d, set=%d, x=%d\n   name=",
      exact,rule_set,x);
   for (j = 0; j != *length; ++j) printf(".%d",name[j]);
   printf("\n");
#endif
   if (exact) {
      if (*length < 13) return NULL;
      if ((rule_set = (int)name[11]) < 1 || rule_set > MXRTBLS) return NULL;
      x = (Bit16)name[12];
      if ((rt = ri[rule_set-1].ri_rule_table) == NULL) return NULL;
      if (x > (sz = ri[rule_set-1].ri_Size)) return NULL;
      newname[11] = (oid)rule_set;  newname[12] = (oid)x;
      if (compare(newname,(int)vp->namelen+2, name,*length) != 0) return NULL;
      if ((r = &rt[x-1]) == NULL) return NULL;
      }
   else {  /* Does oid match up to indexes? */
      if (compare(name,11, newname,11) != 0  ||
            *length < 13) {  /* No: find first entry */
         rule_set = 1;  x = 0;  /* Have to search whole 2-D rule table */
         }
      else {
         if ((rule_set = name[11]) < 1 || rule_set > MXRTBLS) return NULL;
         x = (Bit16)name[12];
         }
      for ( ; ; ++rule_set, x = 0) {
         if (rule_set > MXRTBLS) return NULL;
         if ((rt = ri[rule_set-1].ri_rule_table) == NULL) continue;
         if (x >= (sz = ri[rule_set-1].ri_Size)) continue;
         newname[11] = (oid)rule_set;
         for (++x; x <= sz; ++x) {
            if ((r = &rt[x-1]) == NULL) continue;
            newname[12] = (oid)x;
#ifdef RTTESTING
            cmp = compare(newname,(int)vp->namelen+2, name,*length);
            printf("x=%d, cmp=%d ", x,cmp);
            for (j = 0; j != (int)vp->namelen+2; ++j) printf(".%d",newname[j]);
            printf("\n");
            if (cmp > 0) break;
#else
            if (compare(newname,(int)vp->namelen+2, name,*length) > 0) break;
#endif
            }
         if (x <= sz) break;  /* Found the next one! */
         }
      }
   memcpy((u_char *)name, (u_char *)newname, ((int)vp->namelen+2)*sizeof(oid));
   *length = (int)vp->namelen+2;

#ifdef RTTESTING
#ifdef DOS
printf("   rt[%d], address=%Fp, x=%d\n", rule_set, r, x);
#else
printf("   rt[%d], address=%lu, x=%d\n", rule_set, r, x);
fflush(stdout);
#endif
#endif
   *write_method = 0;
   switch (vp->magic) {
   case RTSELECTOR:
      *var_len = sizeof(r->RuleSelector);
      return (u_char *)&r->RuleSelector;
   case RTRULEMASK:
      *write_method = &writeMask;
      *var_len = RULE_ADDR_LEN;
      return (u_char *)&Masks[r->RuleMaskVal].Val;
   case RTMATCHVALUE:
      *var_len = RULE_ADDR_LEN;
      return (u_char *)r->RuleMatchedValue.rule;
   case RTRULEACTION:
      *var_len = sizeof(r->RuleAction);
      return (u_char *)&r->RuleAction;
   case RTJUMPINDEX:
      *var_len = sizeof(r->RuleJumpIndex);
      *write_method = &set_Bit16;
      return (u_char *)&r->RuleJumpIndex;

   default:
      *var_len = 0;
      ERROR("");
      }
   return NULL;
   }
