/* 1548, Sun 4 Jun 00

   NMC_SNMP.C:  NMC's SNMP interface with the meter

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

/***********************************************************
	Copyright 1988 by Carnegie Mellon University

                      All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/

/*
 * $Log: nmc_snmp.c,v $
 * Revision 1.1.1.2.2.12  2002/02/23 01:57:22  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.10  2001/05/24 02:19:45  nevil
 * LfapMet implemented by Remco Poortinga.
 * MinPDUs implemented by Nevil.
 *
 * Revision 1.1.1.2.2.7  2000/08/08 19:44:46  nevil
 * 44b8 release
 *
 * Revision 1.1.1.2.2.5  2000/06/06 03:38:13  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2.2.2  2000/01/12 02:57:05  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.2.1  1999/11/29 00:17:22  nevil
 * Make changes to support NetBSD on an Alpha (see version.history for details)
 *
 * Revision 1.1.1.2  1999/10/03 21:06:18  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.10  1999/09/24 02:58:36  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.9  1999/09/22 05:38:37  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.8  1999/09/14 00:46:47  nevil
 * 4.3 Release ..
 *  - Implement -D option to run NeMaC as a daemon
 *  - Tidy up the on-line help displays
 *  - Make IPv4 the default PeerType for sfmt_attribute()
 *
 * Revision 1.1.1.1.2.7  1999/05/25 22:30:50  nevil
 * Make sure IPv6 managers interwork properly with IPv4 meters
 * - Determine meter's RULE_ADDR_SIZE by reading mask from rule 1
 *   of default ruleset.
 * - Print warning if meter rulesize < manager rulesize.
 * - Use smaller of these when downloading rules.
 *
 * Revision 1.1.1.1.2.6  1999/05/13 05:14:50  nevil
 * Bungle in create_table_row().
 *   If meter wasn't responding when manager tried to find a table row,
 *   it could try all possible rows then go into an infinite loop looking
 *   for another one.
 *   Fixed by counting the number of rows tested (n_bits).
 *
 * Revision 1.1.1.1.2.5  1999/02/03 04:41:43  nevil
 * Implementation of TCP attributes, part 5
 *
 * Revision 1.1.1.1.2.4  1999/01/27 04:26:14  nevil
 * Minor corrections to fix compiler warnings
 *
 * Revision 1.1.1.1.2.3  1999/01/20 04:01:34  nevil
 * Implementation of TCP attributes, part 4
 *
 * Revision 1.1.1.1.2.2  1999/01/08 01:38:33  nevil
 * Distribution file for 4.3b7
 *
 * Revision 1.1.1.1.2.1  1998/11/25 03:33:24  nevil
 * Implement tcpdata (part 2)
 *
 * Revision 1.1.1.1  1998/11/16 03:57:27  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:40  nevil
 * Only include malloc.h if we HAVE_MALLOC_H
 *
 * Revision 1.1.1.1  1998/10/28 20:31:25  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.3.2.2.2  1998/10/27 04:39:14  nevil
 * 4.3b1 release
 *
 * Revision 1.1.3.2.2.1  1998/10/22 20:47:23  nevil
 * Incorporate snmp and apps bug fixes.
 *
 * Revision 1.1.3.2  1998/10/18 23:44:11  nevil
 * Added Nicolai's patches, some 'tidying up' of the source
 *
 * Revision 1.1.3.1  1998/10/13 02:48:25  nevil
 * Import of Nicolai's 4.2.2
 *
 * Revision 1.1.1.1  1998/08/24 12:09:29  nguba
 * NetraMet 4.2 Original Distribution
 *
 * Revision 1.12  1998/07/21 00:43:57  rtfm
 * Change attrib numbers for 'New Attribs' I-D
 * First release version of SRL
 *
 * Revision 1.11  1998/06/10 20:51:11  rtfm
 * Use riFlowRecords instead of msNbrFlows for ms->NbrFlows
 * Fix size bug in getting reader_name
 *
 * Revision 1.10  1998/06/03 04:52:11  rtfm
 * Get MaxFlows rather than msMaxFlows
 * Only set snmp_delay here (not in manager outer blocks)
 * Use LastTime instead of sysUptime
 * Use STRING_PTR to process colblobs and packages directly from PDU
 *
 * Revision 1.5  1998/04/23 00:53:22  rtfm
 * Buffer rule downloads, so as to send NDRULES rules in each SNMP PDU.
 * This requires a final call to add_rule with a NULL ri, so as to
 * flush the download buffer.
 *
 * Revision 1.3  1998/04/22 22:42:23  kawai
 * Only print rule download diagnostics if verbose.
 * Improve diagnostics when snmp calls fail.
 *
 * Revision 1.2  1998/03/13 04:25:08  kawai
 * Added log_snmp_err to log error returns from snmp_sync_response.
 * Close snmp session if try_v2_meter fails in start_snmp_session.
 * Lots of improved diagnostics
 */

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

#define TUNING_GETBULK  0

#define NDRULES  10  /* Nbr of rules downloaded per SNMP packet */

#define TESTING          0
#define METER_INFO_TEST  0
#define PKTESTING        0

#include <sys/param.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
#include <errno.h>
#include <syslog.h>

#include <string.h>
#if HAVE_MALLOC_H
# include <malloc.h>
#endif
#if HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif

#include "ausnmp.h"

#include "asn1.h"
#include "snmp.h"
#include "snmpimpl.h"
#include "snmpapi.h"
#include "snmpclnt.h"
#include "mib.h"

#include "nmc.h"
#include "nmc_c64.h"

#ifndef BSD4_3
#define BSD4_2
#endif

#define  mib_2       MIB  /* C can't cope with mib-2! */
#define  flowMIB     mib_2, 40
#define  U_AUCKLAND  1, 3, 6, 1, 4, 1, 411

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

oid o_sysDescr[]	  = {MIB, 1, 1, 0};
oid o_sysVersionId[]	  = {MIB, 1, 2, 0};
oid o_sysUpTime[]	  = {MIB, 1, 3, 0};
oid o_ifNumber[]	  = {MIB, 2, 1, 0};

oid o_riRuleSize[]        = {flowMIB, 1,  1, 1, 2, 0xFF};
oid o_riOwner[]           = {flowMIB, 1,  1, 1, 3, 0xFF};
oid o_riTimestamp[]       = {flowMIB, 1,  1, 1, 4, 0xFF};
oid o_riStatus[]          = {flowMIB, 1,  1, 1, 5, 0xFF};
oid o_riName[]            = {flowMIB, 1,  1, 1, 6, 0xFF};
oid o_riFlowRecords[]     = {flowMIB, 1,  1, 1, 8, 0xFF};

oid o_iiSamplingRate[]    = {flowMIB, 1,  2, 1, 1, 0xFF};
oid o_iiLostPackets[]     = {flowMIB, 1,  2, 1, 2, 0xFF};

oid o_ciTimeout[]         = {flowMIB, 1,  3, 1, 2, 0xFF};
oid o_ciOwner[]           = {flowMIB, 1,  3, 1, 3, 0xFF};
oid o_ciLastTime[]        = {flowMIB, 1,  3, 1, 4, 0xFF};
oid o_ciPreviousTime[]    = {flowMIB, 1,  3, 1, 5, 0xFF};
oid o_ciStatus[]          = {flowMIB, 1,  3, 1, 6, 0xFF};
oid o_ciRuleSet[]         = {flowMIB, 1,  3, 1, 7, 0xFF};
oid o_ciMinPDUs[]         = {flowMIB, 1,  3, 1, 8, 0xFF};  /* For nifty */
oid o_ciTimeMark[]        = {flowMIB, 1,  3, 1, 9, 0xFF};  /* For nifty */

oid o_miCurrentRuleSet[]  = {flowMIB, 1,  4, 1, 2, 0xFF};
oid o_miStandbyRuleSet[]  = {flowMIB, 1,  4, 1, 3, 0xFF};
oid o_miHighWaterMark[]   = {flowMIB, 1,  4, 1, 4, 0xFF};
oid o_miCounterWrap[]     = {flowMIB, 1,  4, 1, 5, 0xFF};
oid o_miOwner[]           = {flowMIB, 1,  4, 1, 6, 0xFF};
oid o_miTimeStamp[]       = {flowMIB, 1,  4, 1, 7, 0xFF};
oid o_miStatus[]          = {flowMIB, 1,  4, 1, 8, 0xFF};
oid o_miRunningStandby[]  = {flowMIB, 1,  4, 1, 9, 0xFF};

int mx_info_rows[5] = {0, MXRIROWS, 0, MXCIROWS, MXMIROWS};
            /* 1.1 = rule, 1.3 = reader, 1.4 = manager info table */

oid o_FloodMark[]         = {flowMIB, 1,  5, 0};
oid o_InactivityTimeout[] = {flowMIB, 1,  6, 0};
oid o_ActiveFlows[]       = {flowMIB, 1,  7, 0};
oid o_MaxFlows[]          = {flowMIB, 1,  8, 0};
oid o_FloodMode[]         = {flowMIB, 1,  9, 0};

oid o_ftFlowIndex[]            = {flowMIB, 2, 1, 1,  1, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftFlowTimeMark[]         = {flowMIB, 2, 1, 1,  2, 0xFF, 0xFFFF, 0xFFFF};
   /* TimeMark added in 'Experimental RTFM' MIB (RFC 2064),
      Implemented in NeTraMet v4;
      shifts rest of flow attributes up by 1 */
oid o_ftFlowStatus[]           = {flowMIB, 2, 1, 1,  3, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowInterface[]         = {flowMIB, 2, 1, 1,  4, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowAdjacentType[]      = {flowMIB, 2, 1, 1,  5, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowAdjacentAddress[]   = {flowMIB, 2, 1, 1,  6, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowAdjacentMask[]      = {flowMIB, 2, 1, 1,  7, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowPeerType[]          = {flowMIB, 2, 1, 1,  8, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowPeerAddress[]       = {flowMIB, 2, 1, 1,  9, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowPeerMask[]          = {flowMIB, 2, 1, 1, 10, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowTransType[]         = {flowMIB, 2, 1, 1, 11, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowTransAddress[]      = {flowMIB, 2, 1, 1, 12, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLowTransMask[]         = {flowMIB, 2, 1, 1, 13, 0xFF, 0xFFFF, 0xFFFF};

oid o_ftHighInterface[]        = {flowMIB, 2, 1, 1, 14, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighAdjacentType[]     = {flowMIB, 2, 1, 1, 15, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighAdjacentAddress[]  = {flowMIB, 2, 1, 1, 16, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighAdjacentMask[]     = {flowMIB, 2, 1, 1, 17, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighPeerType[]         = {flowMIB, 2, 1, 1, 18, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighPeerAddress[]      = {flowMIB, 2, 1, 1, 19, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighPeerMask[]         = {flowMIB, 2, 1, 1, 20, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighTransType[]        = {flowMIB, 2, 1, 1, 21, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighTransAddress[]     = {flowMIB, 2, 1, 1, 22, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftHighTransMask[]        = {flowMIB, 2, 1, 1, 23, 0xFF, 0xFFFF, 0xFFFF};

oid o_ftPDUScale[]             = {flowMIB, 2, 1, 1, 24, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftOctetScale[]           = {flowMIB, 2, 1, 1, 25, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftRuleSet[]              = {flowMIB, 2, 1, 1, 26, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftUpOctets[]             = {flowMIB, 2, 1, 1, 27, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftUpPDUs[]               = {flowMIB, 2, 1, 1, 28, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftDownOctets[]           = {flowMIB, 2, 1, 1, 29, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftDownPDUs[]             = {flowMIB, 2, 1, 1, 30, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftFirstTime[]            = {flowMIB, 2, 1, 1, 31, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftLastTime[]             = {flowMIB, 2, 1, 1, 32, 0xFF, 0xFFFF, 0xFFFF};

oid o_ftSourceClass[]          = {flowMIB, 2, 1, 1, 36, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftDestClass[]            = {flowMIB, 2, 1, 1, 37, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftFlowClass[]            = {flowMIB, 2, 1, 1, 38, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftSourceKind[]           = {flowMIB, 2, 1, 1, 39, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftDestKind[]             = {flowMIB, 2, 1, 1, 40, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftFlowKind[]             = {flowMIB, 2, 1, 1, 41, 0xFF, 0xFFFF, 0xFFFF};

#if defined(NETFLOW)
oid o_ftMeterId[]              = {flowMIB, 2, 1, 1,112, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftSourceASN[]            = {flowMIB, 2, 1, 1,113, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftSourcePrefix[]         = {flowMIB, 2, 1, 1,114, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftDestASN[]              = {flowMIB, 2, 1, 1,115, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftDestPrefix[]           = {flowMIB, 2, 1, 1,116, 0xFF, 0xFFFF, 0xFFFF};
#endif

oid o_ftDSCodePoint[]          = {flowMIB, 2, 1, 1,118, 0xFF, 0xFFFF, 0xFFFF};

oid o_ftToLostPDUs[]           = {flowMIB, 2, 1, 1,121, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftFromLostPDUs[]         = {flowMIB, 2, 1, 1,122, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftToPQOverflows[]        = {flowMIB, 2, 1, 1,123, 0xFF, 0xFFFF, 0xFFFF};
oid o_ftFromPQOverflows[]      = {flowMIB, 2, 1, 1,124, 0xFF, 0xFFFF, 0xFFFF};

oid o_ftTCPData[]              = {flowMIB, 2, 1, 1,125, 0xFF, 0xFFFF, 0xFFFF};

oid o_ftDataPackage[] = {flowMIB, 2, 3, 1, 5,
   0xFF,  /* Selector string goes here */
   0xFF, 0xFFFFFFFF, 0xFFFF};

oid o_rtSelector[]        = {flowMIB, 3, 1, 1, 3, 0xFF, 0xFFFF};
oid o_rtRuleMask[]        = {flowMIB, 3, 1, 1, 4, 0xFF, 0xFFFF};
oid o_rtMatchedValue[]    = {flowMIB, 3, 1, 1, 5, 0xFF, 0xFFFF};
oid o_rtRuleAction[]      = {flowMIB, 3, 1, 1, 6, 0xFF, 0xFFFF};
oid o_rtJumpIndex[]       = {flowMIB, 3, 1, 1, 7, 0xFF, 0xFFFF};

oid o_msStatsReset[]      = {U_AUCKLAND, 1,  1, 0};
oid o_msStatsTime[]       = {U_AUCKLAND, 1,  2, 0};
oid o_msNbrPackets[]      = {U_AUCKLAND, 1,  3, 0};
oid o_msTotPktBacklog[]   = {U_AUCKLAND, 1,  4, 0};
oid o_msMaxPktRate[]      = {U_AUCKLAND, 1,  5, 0};
oid o_msMaxPktBacklog[]   = {U_AUCKLAND, 1,  6, 0};
oid o_msNbrFlows[]        = {U_AUCKLAND, 1,  7, 0};
oid o_msFlowsRecovered[]  = {U_AUCKLAND, 1,  8, 0};
oid o_msRuleMatches[]     = {U_AUCKLAND, 1,  9, 0};
oid o_msHashSearches[]    = {U_AUCKLAND, 1, 10, 0};
oid o_msHashCompares[]    = {U_AUCKLAND, 1, 11, 0};
oid o_msTotalHashSize[]   = {U_AUCKLAND, 1, 12, 0};
oid o_msNbrHashEntries[]  = {U_AUCKLAND, 1, 13, 0};
oid o_msGCInterval[]      = {U_AUCKLAND, 1, 14, 0};
oid o_msMaxFlows[]        = {U_AUCKLAND, 1, 15, 0};
oid o_msAvIdle1000[]      = {U_AUCKLAND, 1, 16, 0};
oid o_msMinIdle1000[]     = {U_AUCKLAND, 1, 17, 0};
oid o_msInterfaceName[]   = {U_AUCKLAND, 1, 18, 0};

oid o_pcNearMem[]         = {U_AUCKLAND, 2, 1, 0};
oid o_pcFarMem[]          = {U_AUCKLAND, 2, 2, 0};
oid o_pcBadPackets[]      = {U_AUCKLAND, 2, 3, 0};
oid o_pcNoBufPackets[]    = {U_AUCKLAND, 2, 4, 0};
oid o_pcLostPackets[]     = {U_AUCKLAND, 2, 5, 0};
oid o_trState[]           = {U_AUCKLAND, 3, 1, 0};
oid o_trInfo[]            = {U_AUCKLAND, 3, 2, 0};
oid o_trTime[]            = {U_AUCKLAND, 3, 3, 0};

oid o_v3SamplingRate[]      = {flowMIB, 1,  2, 1, 2, 0xFF};

oid o_v3CurrentRuleSet[]    = {flowMIB, 1,  3, 0};
oid o_v3StandbyRuleSet[]    = {flowMIB, 1,  4, 0};
oid o_v3HighWaterMark[]     = {flowMIB, 1,  5, 0};
oid o_v3LastCollectTime[]   = {flowMIB, 1,  8, 0};
oid o_v3LostPackets[]       = {flowMIB, 1, 12, 0};

oid o_v3FloodMark[]         = {flowMIB, 1,  6, 0};
oid o_v3InactivityTimeout[] = {flowMIB, 1,  9, 0};
oid o_v3ActiveFlows[]       = {flowMIB, 1, 10, 0};
oid o_v3MaxFlows[]          = {flowMIB, 1, 11, 0};

oid o_v3ftFlowStatus[]           = {flowMIB, 2, 1, 1,  2, 0xFFFF};
oid o_v3ftLowInterface[]         = {flowMIB, 2, 1, 1,  3, 0xFFFF};
oid o_v3ftLowAdjacentType[]      = {flowMIB, 2, 1, 1,  4, 0xFFFF};
oid o_v3ftLowAdjacentAddress[]   = {flowMIB, 2, 1, 1,  5, 0xFFFF};
oid o_v3ftLowAdjacentMask[]      = {flowMIB, 2, 1, 1,  6, 0xFFFF};
oid o_v3ftLowPeerType[]          = {flowMIB, 2, 1, 1,  7, 0xFFFF};
oid o_v3ftLowPeerAddress[]       = {flowMIB, 2, 1, 1,  8, 0xFFFF};
oid o_v3ftLowPeerMask[]          = {flowMIB, 2, 1, 1,  9, 0xFFFF};
oid o_v3ftLowTransType[]        = {flowMIB, 2, 1, 1, 10, 0xFFFF};
oid o_v3ftLowTransAddress[]     = {flowMIB, 2, 1, 1, 11, 0xFFFF};
oid o_v3ftLowTransMask[]        = {flowMIB, 2, 1, 1, 12, 0xFFFF};

oid o_v3ftHighInterface[]        = {flowMIB, 2, 1, 1, 13, 0xFFFF};
oid o_v3ftHighAdjacentType[]     = {flowMIB, 2, 1, 1, 14, 0xFFFF};
oid o_v3ftHighAdjacentAddress[]  = {flowMIB, 2, 1, 1, 15, 0xFFFF};
oid o_v3ftHighAdjacentMask[]     = {flowMIB, 2, 1, 1, 16, 0xFFFF};
oid o_v3ftHighPeerType[]         = {flowMIB, 2, 1, 1, 17, 0xFFFF};
oid o_v3ftHighPeerAddress[]      = {flowMIB, 2, 1, 1, 18, 0xFFFF};
oid o_v3ftHighPeerMask[]         = {flowMIB, 2, 1, 1, 19, 0xFFFF};
oid o_v3ftHighTransType[]       = {flowMIB, 2, 1, 1, 20, 0xFFFF};
oid o_v3ftHighTransAddress[]    = {flowMIB, 2, 1, 1, 21, 0xFFFF};
oid o_v3ftHighTransMask[]       = {flowMIB, 2, 1, 1, 22, 0xFFFF};

oid o_v3ftPDUScale[]             = {flowMIB, 2, 1, 1, 23, 0xFFFF};
oid o_v3ftOctetScale[]           = {flowMIB, 2, 1, 1, 24, 0xFFFF};
oid o_v3ftRuleSet[]              = {flowMIB, 2, 1, 1, 25, 0xFFFF};
oid o_v3ftUpOctets[]             = {flowMIB, 2, 1, 1, 26, 0xFFFF};
oid o_v3ftUpPDUs[]               = {flowMIB, 2, 1, 1, 27, 0xFFFF};
oid o_v3ftDownOctets[]           = {flowMIB, 2, 1, 1, 28, 0xFFFF};
oid o_v3ftDownPDUs[]             = {flowMIB, 2, 1, 1, 29, 0xFFFF};
oid o_v3ftFirstTime[]            = {flowMIB, 2, 1, 1, 30, 0xFFFF};
oid o_v3ftLastTime[]             = {flowMIB, 2, 1, 1, 31, 0xFFFF};

oid o_v3ftSourceClass[]          = {flowMIB, 2, 1, 1, 35, 0xFFFF};
oid o_v3ftDestClass[]            = {flowMIB, 2, 1, 1, 36, 0xFFFF};
oid o_v3ftFlowClass[]            = {flowMIB, 2, 1, 1, 37, 0xFFFF};
oid o_v3ftSourceKind[]           = {flowMIB, 2, 1, 1, 38, 0xFFFF};
oid o_v3ftDestKind[]             = {flowMIB, 2, 1, 1, 39, 0xFFFF};
oid o_v3ftFlowKind[]             = {flowMIB, 2, 1, 1, 40, 0xFFFF};

oid o_v3ColumnBlob[]  = {flowMIB, 2, 4, 1, 4, 0xFF, 0xFFFFFFFF, 0xFFFF};


#define ADD_VAR(v)            snmp_add_null_var(pdu, v, sizeof(v)/sizeof(oid))
#define ADD_X_VAR(v,n1)         { v[sizeof(v)/sizeof(oid) - 1] = n1; \
          ADD_VAR(v); }
#define ADD_X2_VAR(v,n1,n2)     { v[sizeof(v)/sizeof(oid) - 2] = n1; \
          v[sizeof(v)/sizeof(oid) - 1] = n2; \
          ADD_VAR(v); }
#define ADD_X3_VAR(v,n1,n2,n3)  { v[sizeof(v)/sizeof(oid) - 3] = n1; \
          v[sizeof(v)/sizeof(oid) - 2] = n2; \
          v[sizeof(v)/sizeof(oid) - 1] = n3; \
          ADD_VAR(v); }

#define SET_INT(v)            { vars->type = INTEGER; \
  	  vars->val.integer = (Int32 *)malloc(vars->val_len = sizeof(Int32)); \
	  *(vars->val.integer) = (Int32)(v); }
#define SET_TIMETICKS(v)      { vars->type = TIMETICKS; \
  	  vars->val.u_int = (Bit32 *)malloc(vars->val_len = sizeof(Bit32)); \
	  *(vars->val.u_int) = (Bit32)(v); }
#define SET_STRING(v,len)     { vars->type = STRING; \
	  vars->val.string = (u_char *)malloc(len); \
	  memcpy((char *)vars->val.string, (v), \
          vars->val_len = trim((unsigned char *)(v),len)); }

#define STRING_VAL(v)   memcpy(v, vars->val.string, vars->val_len \
                           <= sizeof(v) ? vars->val_len : sizeof(v))
#define STRING_PTR(v,n) v = vars->val.string;  n = vars->val_len

#define INT_VAL(v)      v = *(vars->val.integer)
#define OID_VAL(v)      memcpy(v, (char *)vars->val.objid, vars->val_len)
#define C64_VAL(v)      memcpy(&v, (char *)vars->val.counter64, vars->val_len)


#define TV_TRUE    1
#define TV_FALSE   2

#define RS_ACTIVE          1
#define RS_NOTINSERVICE    2
#define RS_NOTREADY        3
#define RS_CREATEANDGO     4
#define RS_CREATEANDWAIT   5
#define RS_DESTROY         6

struct snmp_session *session_setup(
   struct meter_status *ms, int ver)  /* Creates a new SNMP session */
{
   struct snmp_session session;

   memset((char *)&session, 0, sizeof(struct snmp_session));
   session.peername = ms->name;
   session.remote_port = SNMP_PORT;  /* Uses au_snmp_port */
   session.version = ver;
   session.community = ms->community;
   session.community_len = strlen((char *)ms->community);
   session.retries = SNMP_DEFAULT_RETRIES;
   session.timeout = SNMP_DEFAULT_TIMEOUT;
   session.authenticator = NULL;

   snmp_synch_setup(&session);
   return snmp_open(&session);
   }

void log_snmp_err(char * what, struct snmp_pdu *response) {
   int count;
   struct variable_list *vars;
   char buf[256];

   log_msg(LOG_INFO, FALSE, "%s: Error in packet, reason = %s\n",
	   what ,snmp_errstring(response->errstat));

   for (count=1, vars = response->variables;
      vars && count != response->errindex;
      vars = vars->next_variable, count++) ;
   if (vars) {
      sprint_objid(buf, vars->name, vars->name_length);
      log_msg(LOG_INFO, FALSE, "... %s\n", buf);
      }
   }

int try_v2_meter(struct meter_status *ms)
{
   int  mt = meter_type(ms);  /* Sets MIBver, ver_maj, ver_min, etc. */
   if (mt == 2) {  /* 'New' MIB using SNMPv2 */
      snmp_close(ms->ss);  /* Close v1 session */
      ms->ss = NULL;
      ms->ss = session_setup(ms,SNMP_VERSION_2C);
      if (ms->ss == NULL) {
         fprintf(stderr, "Couldn't open v2C session to %s\n", ms->name);
         return 0;
         }
      ms->flows_per_getbulk = 25;  /* First guess */
        /* nifty gets 23 flows with low FlowIndexes */
      return 1;
      }
   else if (mt == 1)  /* 'Old' MIB using SNMPv1 */
      return 1;
   else return 0;  /* No contact, or couldn't determine meter type */
   }

int start_snmp_session(struct meter_status *ms)
{
   int mt;
   ms->ss = session_setup(ms,SNMP_VERSION_1);
   if (ms->ss == NULL) {
      fprintf(stderr, "Couldn't open snmp to %s\n", ms->name);
      return 0;
      }
   mswait(ms->snmp_delay = 17);  /* Wait 17 ms after an SNMP request */
      /* This imposes a limit of about 50 SNMP transactions per second */
   if(mt = try_v2_meter(ms)) return mt;

   /* Meter failed to respond */
   snmp_close(ms->ss);  /* Close session */
   return 0;
   }

int restart_snmp_session(struct meter_status *ms)
{
   if (ms->ss != NULL)
      snmp_close(ms->ss);  /* Close old session */
   ms->ss = session_setup(ms,SNMP_VERSION_1);
   if (ms->ss == NULL) return 0;
   return try_v2_meter(ms);
   }

int create_table_row(struct meter_status *ms, oid *r_status)
   /* Returns row info table index of new row, 0 if create failed */
{
   int count, status;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   oid rs[12];
   int table,mx_rows,n_bits, len, x, running, m_status;
   Bit32 inuse, mask;
   struct timeval tod;

   if (!ms->write_OK) return 0;  /* No write access */

   mx_rows = mx_info_rows[table = r_status[8]];
   memcpy((char *)rs, (char *)r_status, 11*sizeof(oid));
   if (table == 1) {  /* Rule Info */
      n_bits = inuse = 1;  /* Ruleset 1 is the default */
      }
   else {  /* Reader or Manager info */
      n_bits = inuse = 0;
      }
   running = 1;
   while (running && n_bits != mx_rows) {
      for (;;) {
#define PICK_RANDOM_ROW  1
#if PICK_RANDOM_ROW
         gettimeofday(&tod, (struct timezone *)0);
         x = tod.tv_usec / 1000;  /* ms part of time-of-day */
         x = x % mx_rows;
#else
	 x = 3;  /*e sting: always use the same row (in all tables) */
#endif
         mask = 1 << x;
         if ((inuse & mask) == 0) break;
	 }
      inuse |= mask;  ++n_bits;
      rs[11] = ++x;  len = 12;
      pdu = snmp_pdu_create(SET_REQ_MSG);
      snmp_add_null_var(pdu, rs, len);
      vars = pdu->variables;
      SET_INT(RS_CREATEANDWAIT);

      status = snmp_synch_response(ms->ss, pdu, &response);
      if (status == STAT_SUCCESS) {
	 if (response->errstat == SNMP_ERR_NOERROR)
            running = 0;  /* Created, break the loop */
	 else if (response->errstat != SNMP_ERR_INCONSISTENTVALUE) {
	    running = 0;  /* Error or timeout */
	    log_snmp_err("create_table_row():", response);
	    }
	 }
      if (response)
	 snmp_free_pdu(response);
      }
   mswait(ms->snmp_delay);
   return x;  /* Row index */
   }

int trim(unsigned char *s, int len)
{
   while (len != 0 && s[len-1] == 0) --len;
   return len;
   }

int set_meter_params(struct meter_status *ms)
{
   int i, count, status;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;

   if (ms->GCIntervalReqd == 0 && 
         (ms->HighWaterMark == 0 || ms->MIBver == 4) &&
         ms->FloodMark == 0 &&
         ms->InactivityTime == 0 && ms->SamplingRate == 0) 
      return 1;  /* Nothing to do */
   if (!ms->write_OK) return 1;  /* No write access */

   pdu = snmp_pdu_create(SET_REQ_MSG);
   i = 0;
   if (ms->GCInterval != 0) {
      ADD_VAR(o_msGCInterval);
      vars = pdu->variables;  i = 1;
      SET_INT(ms->GCIntervalReqd);
      }
   if (ms->HighWaterMark != 0) {  /* $$$ Should really delete this */
      if (ms->MIBver != 4) {  /* v4 HWM is a per-task variable */
         ADD_VAR(o_v3HighWaterMark);
         vars = i ? vars->next_variable : pdu->variables;  i = 1;
         SET_INT(ms->HighWaterMark);
	 }
      }
   if (ms->FloodMark != 0) {
      if (ms->MIBver == 4)
         ADD_VAR(o_FloodMark);
      else  /* Use 'old' MIB attribute nbr */
         ADD_VAR(o_v3FloodMark);
      vars = i ? vars->next_variable : pdu->variables;  i = 1;
      SET_INT(ms->FloodMark);
      }
   if (ms->InactivityTime != 0) {
      if (ms->MIBver == 4)
         ADD_VAR(o_InactivityTimeout);
      else  /* Use 'old' MIB attribute nbr */
         ADD_VAR(o_v3InactivityTimeout);
      vars = i ? vars->next_variable : pdu->variables;  i = 1;
      SET_INT(ms->InactivityTime);
      }
   if (ms->SamplingRate != 0) {
      if (ms->MIBver == 4) {
         ADD_X_VAR(o_iiSamplingRate, 1);  /* $$I */
	 }
      else /* Use 'old' MIB attribute nbr */
         ADD_VAR(o_v3SamplingRate);
      vars = i ? vars->next_variable : pdu->variables;
      SET_INT(ms->SamplingRate);
      }

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         if (verbose) fprintf(stderr, 
            "Meter parameters for %s set: GCI=%u, HWM=%u, IAT=%u, SR=%u\n",
            ms->name, ms->GCIntervalReqd,ms->HighWaterMark,
            ms->InactivityTime,ms->SamplingRate);
         log_msg(LOG_INFO, FALSE,
            "Meter parameters for %s set: GCI=%u, HWM=%u, IAT=%u, SR=%u\n",
            ms->name, ms->GCIntervalReqd,ms->HighWaterMark,
            ms->InactivityTime,ms->SamplingRate);
	 }
      else {
	 log_snmp_err("set_meter_params()", response);
         ms->write_OK = 0;
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return 1;
   }

int search_table(struct meter_status *ms, int which,
   void (*row_handler)(struct meter_status *ms, struct row_info *rip))
{
   oid r_status[12];
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   int x, len, result, j;
   int running, status, err, count;
   struct row_info ri;

   if (ms->MIBver != 4) return 1;  /* Didn't need to do this for v3 */

   x = 0;  len = 12;
   result = 0;  running = 1;
   while (running) {
      pdu = snmp_pdu_create(BULK_REQ_MSG);
      pdu->non_repeaters = 0;
      pdu->max_repetitions = mx_info_rows[which];
      switch (which) {
      case ST_RULESET:  /* Ruleset info */
         memcpy(r_status, o_riStatus, len*sizeof(oid));
         ADD_X_VAR(o_riStatus, x);
         ADD_X_VAR(o_riOwner, x);
         ADD_X_VAR(o_riName, x);
         ADD_X_VAR(o_riRuleSize, x);
         break;
      case ST_READER:  /* Meter Reader info */
         memcpy(r_status, o_ciStatus, len*sizeof(oid));
         ADD_X_VAR(o_ciStatus, x);
         ADD_X_VAR(o_ciOwner, x);
         break;
      case ST_MANAGER:  /* Manager info */
         memcpy(r_status, o_miStatus, len*sizeof(oid));
         ADD_X_VAR(o_miStatus, x);
         ADD_X_VAR(o_miOwner, x);
         ADD_X_VAR(o_miCurrentRuleSet, x);
         ADD_X_VAR(o_miStandbyRuleSet, x);
         break;
         }

      status = snmp_synch_response(ms->ss, pdu, &response);
      if (status == STAT_SUCCESS) {
	 if (response->errstat == SNMP_ERR_NOERROR) {
	    for (vars = response->variables; vars;
		  vars = vars->next_variable) {
               if (memcmp(vars->name, r_status, (len-1)*sizeof(oid)) != 0)
                  return 1;  /* End of table */
               x = vars->name[11];
               memset(&ri, 0, sizeof(ri));
               ri.tr_Index = x;
               INT_VAL(ri.tr_Status);
               vars = vars->next_variable;
               if (vars == NULL) break;  /* Get another PDU */
	       if (vars->name_length != len  /* Shorter than requested oid */
                     || vars->name[len-1] != x  /* Wrong row */
                     || vars->name[8] != which)  /* Wrong variable */
		  break;  /* Get another PDU */
               STRING_VAL(ri.tr_Owner);
               if (which == ST_RULESET) {
                  vars = vars->next_variable;
                  if (vars == NULL) break;  /* Get another PDU */
	          if (vars->name_length != len  /* Shorter than reqd oid */
                        || vars->name[len-1] != x  /* Wrong row */
                        || vars->name[8] != which)  /* Wrong variable */
		     break;  /* Get another PDU */
                  STRING_VAL(ri.tr_Name);
                  vars = vars->next_variable;
                  if (vars == NULL) break;  /* Get another PDU */
	          if (vars->name_length != len  /* Shorter than reqd oid */
                        || vars->name[len-1] != x  /* Wrong row */
                        || vars->name[8] != which)  /* Wrong variable */
		     break;  /* Get another PDU */
                  INT_VAL(ri.tr_RuleSize);
		  }
               else if (which == ST_MANAGER) {
                  vars = vars->next_variable;
                  if (vars == NULL) break;  /* Get another PDU */
	          if (vars->name_length != len  /* Shorter than reqd oid */
                        || vars->name[len-1] != x  /* Wrong row */
                        || vars->name[8] != which)  /* Wrong variable */
		     break;  /* Get another PDU */
                  INT_VAL(ri.tr_Current);
                  vars = vars->next_variable;
                  if (vars == NULL) break;  /* Get another PDU */
	          if (vars->name_length != len  /* Shorter than reqd oid */
                        || vars->name[len-1] != x  /* Wrong row */
                        || vars->name[8] != which)  /* Wrong variable */
		     break;  /* Get another PDU */
                  INT_VAL(ri.tr_Standby);
		  }
               row_handler(ms, &ri);
	       }
	    }
	 else {
	    running = 0;  /* Error or timeout */
	    log_snmp_err("search_table()", response);
	    }
	 }
      if (response)
	 snmp_free_pdu(response);
      }
   mswait(ms->snmp_delay);
   return result;
   }

int ruleset_util(struct meter_status *ms,
   int which, struct meter_rule_info *mri)
{
   int result, i, count, status, r;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   char *op;

   if (!ms->write_OK) return 1;
   switch (which) {
   case RU_INIT:  /* Create new rule info row */
      op = "Init";
      if (ms->MIBver == 4) {
         if ((r = create_table_row(ms, o_riStatus)) == 0)
            return 0;  /* Couldn't create row */
         mri->ri_Index = r;
         }
      pdu = snmp_pdu_create(SET_REQ_MSG);
      if (ms->MIBver == 4) {
         ADD_X_VAR(o_riOwner, mri->ri_Index);
         vars = pdu->variables;
         SET_STRING(ms->owner_name, NAMESZ);
         ADD_X_VAR(o_riName, mri->ri_Index);
         vars = vars->next_variable;
         SET_STRING(mri->ri_Name, NAMESZ);
         ADD_X_VAR(o_riRuleSize, mri->ri_Index);  /* Same in old & new MIBs! */
         vars = vars->next_variable;
         SET_INT(ms->nrules);
	 }
      else {
         ADD_X_VAR(o_riRuleSize, mri->ri_Index);  /* Same in old & new MIBs! */
         vars = pdu->variables;
         SET_INT(ms->nrules);
	 }
      break;
   case RU_READY:  /* Set RulesReady */
      op = "Ready";
      if (ms->MIBver != 4) return 1;  /* Didn't need to do this for v3 */
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_X_VAR(o_riStatus, mri->ri_Index);
      vars = pdu->variables;
      SET_INT(RS_ACTIVE);
      break;
   case RU_DESTROY:  /* Destroy ruleset row */
      op = "Destroy";
      if (ms->MIBver != 4) return 1;  /* Didn't need to do this for v3 */
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_X_VAR(o_riStatus, mri->ri_Index);
      vars = pdu->variables;
      SET_INT(RS_DESTROY);
      break;
   default:
      return 0;
      }

   if (testing)fprintf(stderr, 
      "ru(): op=%s, ri_index=%d\n",
      op, mri->ri_Index);

   result = 0;
   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         result = 1;
         switch (which) {
         case RU_INIT:
            mri->ri_Size = ms->nrules;
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, rsu():  Set %s size set to %d rules\n", 
               ms->name, mri->ri_Name, mri->ri_Size);
            break;
         case RU_READY:
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, rsu():  RulesReady for set %s\n",
               ms->name, mri->ri_Name);
            break;
         case RU_DESTROY:
	   if (verbose) log_msg(LOG_INFO, FALSE,
              "Meter %s, rsu():  Row %d destroyed\n",
              ms->name, mri->ri_Index);
            mri->ri_Index = 0;
            break;
            }
	 }
      else {
	 char buf[25];
	 sprintf(buf, "ruleset_util(%s)", op);
	 log_snmp_err(buf, response);
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return result;
   }

int task_util(struct meter_status *ms, int which)
{
   int i, count, status, r;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   char * op;

   switch (which) {
   case TU_INIT:
      op = "Init";
      if (ms->MIBver != 4) return 1;  /* Didn't need to do this for v3 */
      if ((r = create_table_row(ms, o_miStatus)) == 0)
         return 0;  /* Couldn't create row */
      ms->mi_Index = r;
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_X_VAR(o_miOwner, ms->mi_Index);
      vars = pdu->variables;
      SET_STRING(ms->owner_name, NAMESZ);
      if (ms->HighWaterMark != 0) {
         ADD_X_VAR(o_miHighWaterMark, ms->mi_Index);
         vars = vars->next_variable;
         SET_INT(ms->HighWaterMark);
         }
      ADD_X_VAR(o_miStatus, ms->mi_Index);
      vars = vars->next_variable;
      SET_INT(RS_ACTIVE);
      break;
   case TU_CURRENT:  /* Set 'current' ruleset */
      op = "Current";
      pdu = snmp_pdu_create(SET_REQ_MSG);
      if (ms->MIBver == 4) {
         ADD_X_VAR(o_miCurrentRuleSet, ms->mi_Index);
	 }
      else  /* Use 'old' MIB attribute nbr */
         ADD_VAR(o_v3CurrentRuleSet);
      vars = pdu->variables;
      SET_INT(ms->c_ruleset.ri_Index);
      break;
   case TU_STANDBY:  /* Set 'standby' ruleset */
      op = "Standby";
      pdu = snmp_pdu_create(SET_REQ_MSG);
      if (ms->MIBver == 4) {
         ADD_X_VAR(o_miStandbyRuleSet, ms->mi_Index);
	 }
      else  /* Use 'old' MIB attribute nbr */
         ADD_VAR(o_v3StandbyRuleSet);
      vars = pdu->variables;
      SET_INT(ms->s_ruleset.ri_Index);
      break;
   case TU_NOSTAND:  /* Switch back from RunningStandby */
      op = "NoStandby";
      if (ms->MIBver != 4) return 1;  /* Didn't need to do this for v3 */
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_X_VAR(o_miRunningStandby, ms->mi_Index);
      vars = pdu->variables;
      SET_INT(TV_FALSE);
      break;
   case TU_DESTROY:
      op = "Destroy";
      if (ms->MIBver != 4) return 1;  /* Didn't need to do this for v3 */
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_X_VAR(o_miStatus, ms->mi_u_Index);
      vars = pdu->variables;
      SET_INT(RS_DESTROY);
      break;
   default:
      return 0;
      }

   if (testing) fprintf(stderr, 
      "tu(): op=%s, c_index=%d, s_index=%d, u_index=%d\n",
      op, ms->s_ruleset.ri_Index, ms->s_ruleset.ri_Index,  ms->mi_u_Index);

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         switch (which) {
         case TU_INIT:
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, tsku():  Task %d created\n",
               ms->name, ms->mi_Index);
            break;
         case TU_CURRENT:
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, tsku():  Task %d, current=%d\n",
               ms->name, ms->mi_Index,ms->c_ruleset.ri_Index);
            break;
         case TU_STANDBY:
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, tsku():  Task %d, standby=%d\n",
               ms->name, ms->mi_Index,ms->s_ruleset.ri_Index);
            break;
         case TU_NOSTAND:
            if (verbose) log_msg(LOG_INFO, FALSE, 
               "Meter %s, tsku():  Task %d, switched back from standby\n" ,
               ms->name, ms->mi_Index,ms->s_ruleset.ri_Index);
            break;
         case TU_DESTROY:
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, tsku():  Task %d destroyed\n",
               ms->name, ms->mi_u_Index);
            break;
            }
	 }
      else {
	 char buf[25];
	 sprintf(buf, "task_util(%s)", op);
	 log_snmp_err(buf, response);
         ms->write_OK = 0;
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return 1;
   }

int reader_util(struct meter_status *ms, int which, int flowset)
{
   int i, count, status, r;
   int *rowp, *rsnbrp;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   int dummy = -99;
   char *op, *fs;

   if (ms->no_write_meter)
      return 1;  /* Don't want meter to recover memory for collected flows */
   if (!ms->write_OK) return 1;
   switch (flowset) {
   case CU_CURRENT:
      fs = "Current";
      rowp = &ms->ci_c_Index;
      rsnbrp = &ms->c_ruleset.ri_Index;
      break;
   case CU_STANDBY:
      fs = "Standby";
      rowp = &ms->ci_s_Index;
      rsnbrp = &ms->s_ruleset.ri_Index;
      break;
   case CU_DEFAULT:
      fs = "Default";
      rowp = &ms->ci_d_Index;
      rsnbrp = &ms->d_ruleset.ri_Index;
      break;
   case CU_UTIL:
      fs = "Util";
      rowp = &ms->ci_u_Index;
      rsnbrp = &dummy;
      break;
      }

   switch (which) {
   case CU_INIT:
      op = "Init";
      if (ms->MIBver != 4) return 1;  /* Didn't need to do this for v3 */
      if ((r = create_table_row(ms, o_ciStatus)) == 0)
         return 0;  /* Couldn't create row */
      *rowp = r;
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_X_VAR(o_ciOwner, *rowp);
      vars = pdu->variables;
      SET_STRING(ms->owner_name, NAMESZ);
      ADD_X_VAR(o_ciRuleSet, *rowp);
      vars = vars->next_variable;
      SET_INT(*rsnbrp);
      if (ms->ci_Timeout != 0) {
         ADD_X_VAR(o_ciTimeout, *rowp);
         vars = vars->next_variable;
         SET_INT(ms->ci_Timeout);
         }
      ADD_X_VAR(o_ciStatus, *rowp);
      vars = vars->next_variable;
      SET_INT(RS_ACTIVE);
      break;
   case CU_SET_TIME:  /* Set LastTime for flowset */
      op = "Set Time";
      pdu = snmp_pdu_create(SET_REQ_MSG);
      if (ms->MIBver == 4) {
         ADD_X_VAR(o_ciLastTime, *rowp);
         }
      else  /* Use 'old' MIB attribute nbr */
         ADD_VAR(o_v3LastCollectTime);
      vars = pdu->variables;
      SET_TIMETICKS(1);  /* Meter ignores the value we set! */
      if (ms->meter_MinPDUs) {
         ADD_X_VAR(o_ciTimeMark, *rowp);
         vars = vars->next_variable;
         SET_INT(ms->ci_TimeMark);
         }
      break;
   case CU_SET_MINPDUS:  /* Set flowset MinPDUs (for nifty) */
      if (ms->MIBver != 4) return 1;
      if (meter_maj(ms) < 4 || meter_min(ms) < 4) {
         fprintf(stderr,"Meter doesn't support MinPDUs\n");
         log_msg(LOG_INFO, FALSE,
            "Meter doesn't support MinPDUs");
         ms->meter_MinPDUs = 0;
         return 1;
         }
      op = "Set MinPDUs";
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_X_VAR(o_ciMinPDUs, *rowp);
      vars = pdu->variables;
      SET_INT(ms->ci_MinPDUs);
      break;
   case CU_DESTROY:
      op = "Default";
      if (ms->MIBver != 4) return 1;  /* Didn't need to do this for v3 */
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_X_VAR(o_ciStatus, *rowp);
      vars = pdu->variables;
      SET_INT(RS_DESTROY);
      break;
   default:
      return 0;
      }

   if (testing)
      fprintf(stderr, "cu(): which=%s, *rowp=%d, *rsnbrp=%d\n",
      op, *rowp, *rsnbrp);

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         switch (which) {
         case CU_INIT:
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, rdru():  Reader %d created for %d\n",
               ms->name, *rowp, *rsnbrp);
            break;
         case CU_SET_TIME:
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, rdru():  Reader %d TimeMark set to %d\n",
               ms->name, *rowp, ms->ci_TimeMark);
            break;
         case CU_SET_MINPDUS:  /* (for nifty) */
            ms->meter_MinPDUs = 1;
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, rdru():  Reader %d MinPDUs set to %d\n",
               ms->name, *rowp, ms->ci_MinPDUs);
            break;
         case CU_DESTROY:
            if (verbose) log_msg(LOG_INFO, FALSE,
               "Meter %s, rdru():  Reader %d destroyed\n",
               ms->name, *rowp);
            break;
            }
         }
      else {
	 char buf[30];
         switch (which) {
            case CU_SET_MINPDUS:  /* (for nifty) */
            log_msg(LOG_INFO, FALSE,
               "Meter %s, rdru():  Meter doesn't support MinPDUs\n",
               ms->name, *rowp);
            break;
         default:
	    sprintf(buf, "reader_util(%s,%s)", op, fs);
            log_snmp_err(buf, response);
            ms->write_OK = 0;
	    }
         }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return 1;
   }

struct rule_info drules[NDRULES];
int drx = 0;  /* Nbr of rules in drules */

int download_rules(struct meter_status *ms)
{
   int x,i, tb,b, count, status;
   struct snmp_pdu *pdu, *response;
   struct rule_info *ri;
   struct variable_list *vars;
   unsigned char abuf[RULE_ADDR_LEN];

   pdu = snmp_pdu_create(SET_REQ_MSG);
   for (x = 0; x != drx; ++x) {
      ri = &drules[x];
      ADD_X2_VAR(o_rtSelector, ri->RuleSet,ri->RuleNbr);
         vars = x == 0 ? pdu->variables : vars->next_variable;
         i = ri->RuleSelector;
         if (ms->MIBver != 4) {  /* Use 'old' MIB attribute nbr */
            if (i > FTFLOWTIMEMARK && i <= LASTATTRIB) --i;
	    }
         SET_INT(i);
      ADD_X2_VAR(o_rtRuleMask, ri->RuleSet,ri->RuleNbr);
         vars = vars->next_variable;
         SET_STRING(ri->RuleMask, ms->meter_ra_len);
      ADD_X2_VAR(o_rtMatchedValue, ri->RuleSet,ri->RuleNbr);
         vars = vars->next_variable;
         memcpy(abuf, ri->RuleMatchedValue,RULE_ADDR_LEN);
         if (ms->MIBver != 4) {  /* Use 'old' MIB attribute nbr */
	    if (ri->RuleAction == RA_ASSIGN 
                  || ri->RuleAction == RA_ASSIGNACT) {
   	       i = abuf[0];
               if (i > FTFLOWTIMEMARK && i <= LASTATTRIB) --abuf[0];
	       }
   	    }
         SET_STRING(abuf,ms->meter_ra_len);
      ADD_X2_VAR(o_rtRuleAction, ri->RuleSet,ri->RuleNbr);
         vars = vars->next_variable;
         SET_INT(ri->RuleAction);
      ADD_X2_VAR(o_rtJumpIndex, ri->RuleSet,ri->RuleNbr);
         vars = vars->next_variable;
         SET_INT(ri->RuleJumpIndex);
      }
   drx = 0;  /* drules now empty */

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         if (verbose) {
            tb = (ms->nrules+NDRULES-1)/NDRULES;  /* Total blocks */
            b = (ri->RuleNbr+NDRULES-1)/NDRULES;  /* This block */
            i = tb / 10;  if (i < 10) i = 10;  /* 17 Oct 95 */
            if (b % i == 0) {  /* 28 Oct 93 */
               log_msg(LOG_INFO, FALSE,
                  "Rule %d added to set %d on meter %s\n",
                  ri->RuleNbr,ri->RuleSet, ms->name);
               }
	    }
	 }
      else {
	 log_snmp_err("add_rule()", response);
         ms->write_OK = 0;
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return 1;
   }

int add_rule(struct meter_status *ms, struct rule_info *ri)
{
   if (!ms->write_OK) return 1;
   if (ri != NULL) {
      memcpy(&drules[drx++], ri, sizeof(struct rule_info));
      if (drx == NDRULES)
         return download_rules(ms);
      else return 1;  /* OK */
      }
   else {  /* Last call, flush drules */
      if (drx != 0)
         return download_rules(ms);
      else return 1;  /* OK */
      }
   }

int get_rule(struct meter_status *ms, struct rule_info *ri)
{  /* For verifying rulesets by uploading them from a meter */
   int result, status;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;

   result = 0;
   pdu = snmp_pdu_create(GET_REQ_MSG);
   ADD_X2_VAR(o_rtSelector, ri->RuleSet,ri->RuleNbr);
   ADD_X2_VAR(o_rtRuleMask, ri->RuleSet,ri->RuleNbr);
   ADD_X2_VAR(o_rtMatchedValue, ri->RuleSet,ri->RuleNbr);
   ADD_X2_VAR(o_rtRuleAction, ri->RuleSet,ri->RuleNbr);
   ADD_X2_VAR(o_rtJumpIndex, ri->RuleSet,ri->RuleNbr);

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         result = 1;
	 vars = response->variables;
         INT_VAL(ri->RuleSelector);
         vars = vars->next_variable;
         STRING_VAL(ri->RuleMask);
         vars = vars->next_variable;
         STRING_VAL(ri->RuleMatchedValue);
         vars = vars->next_variable;
         INT_VAL(ri->RuleAction);
         vars = vars->next_variable;
         INT_VAL(ri->RuleJumpIndex);
	 }
      else {
	 log_snmp_err("get_rule()", response);
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return 1;
   }

#ifdef NEW_ATR
unsigned char *asn_parse_distribution(
   unsigned char *data, int *d_len, struct distribution *d)
{
   Bit8 type;
   int sz, j;
   Bit32 i_val;

   data += 4;  *d_len -= 4;  /* Skip the enclosing sequence */
   sz = DIST_PARAM_LEN;
   data = asn_parse_string(data, d_len, &type, d->mask_params, &sz);
   if ((d->Transform = d->mask_params[0]) == DS_NULL)
      return data;
   sz = DIST_PARAM_LEN;
   data = asn_parse_string(data, d_len, &type, d->value_params, &sz);
   d->ScaleFactor = d->mask_params[1];
   d->LowerLimit = (d->mask_params[2] << 8) | d->mask_params[3];
   d->UpperLimit = (d->mask_params[4] << 8) | d->mask_params[5];
   d->Buckets = d->value_params[0];
   d->Parameter1 = d->value_params[1];
   d->Parameter2 = (d->value_params[2] << 8) | d->value_params[3];
   d->Parameter3 = (d->value_params[4] << 8) | d->value_params[5];
   for (j = 0; j != d->Buckets; ++j) {
      data = asn_parse_int(data, d_len, &type, &i_val, sizeof(i_val));
      d->counts[j] = i_val;
      }
   if (d->Transform != DS_DYN_PTS) {  /* DS_DYN_PTS (no oflo bucket) */
      data = asn_parse_int(data, d_len, &type, &i_val, sizeof(i_val));
      d->counts[d->Buckets] = i_val;
      }
   return data;
   }

unsigned char *asn_parse_tcp_data(
   unsigned char *data, int *d_len, struct subflow_data *sfd)
{
   Bit8 type;
   int sz, j;
   Bit32 seq_len, i_val;

   asn_parse_length(data+1, &seq_len);
   sfd->valid = seq_len > 3;  /* 3 => integer 0 only */

   data += 4;  *d_len -= 4;  /* Skip the enclosing sequence */
   data = asn_parse_int(data, d_len, &type, 
      &sfd->n_subflows, sizeof(sfd->n_subflows));
   if (!sfd->valid) return data;  /* No TCPdata values */

   data = asn_parse_int(data, d_len, &type, &i_val, sizeof(i_val));
   sfd->mx_active_subflows = i_val;

   data = asn_parse_unsigned_int64(data, d_len, &type,
      &sfd->ToTCPLenOctets, sizeof(sfd->ToTCPLenOctets));
   data = asn_parse_unsigned_int64(data, d_len, &type,
      &sfd->FromTCPLenOctets, sizeof(sfd->FromTCPLenOctets));

   data = asn_parse_unsigned_int64(data, d_len, &type,
      &sfd->ToTCPSeqOctets, sizeof(sfd->ToTCPSeqOctets));
   data = asn_parse_unsigned_int64(data, d_len, &type,
      &sfd->FromTCPSeqOctets, sizeof(sfd->FromTCPSeqOctets));

   data = asn_parse_unsigned_int64(data, d_len, &type,
      &sfd->ToTCPAckOctets, sizeof(sfd->ToTCPAckOctets));
   data = asn_parse_unsigned_int64(data, d_len, &type,
      &sfd->FromTCPAckOctets, sizeof(sfd->FromTCPAckOctets));

   data = asn_parse_int(data, d_len, &type, &i_val, sizeof(i_val));
   sfd->ToTCPDecrSeq = i_val;
   data = asn_parse_int(data, d_len, &type, &i_val, sizeof(i_val));
   sfd->FromTCPDecrSeq = i_val;

   return data;
   }
#endif  /* NEW_ATR */

int unpack_package(struct meter_status *ms, unsigned char *fb,
   int rs, int fi, void (*process_row)(struct flow_info *fp))
{
   struct flow_info fp_data;
   struct flow_info *fp;
   unsigned char *data, type;
   int a, sz, d_len;
   Bit32 u_val;
#ifdef NEW_ATR
   struct distribution distrib[N_DIST_ATRS], *d;
   int nd = 0;
   struct subflow_data sfdat;
#endif  /* NEW_ATR */
#if PKTESTING
   int j;
#endif

   fp = &fp_data;
   memset(fp, 0, sizeof(struct flow_info));

   data = fb+4;  d_len = 2000;  /* Dummy length */
#if PKTESTING
   printf("unpack(): data=%lu, d_len=%u\n", data, d_len);
#endif
   for (a = 1; a != 1+LASTATTRIB; ++a) {
      if (ms->required[a] == 0) continue;
      if (*data == ASN_NULL) {
         if (verbose) printf("Meter returned ASN_NULL for attrib %u\n", a);
         data = asn_parse_null(data, &d_len, &type);
         }
      else {
         switch (a) {
         case FTFLOWINDEX:
            fp->FlowIndex = fi;
            break;
         case FTFLOWSTATUS:
	    data = asn_parse_int(data, &d_len, &type,
                &u_val, sizeof(u_val));
            /* asn_parse_int() only writes into *Bit32s ! */
            fp->FlowStatus = u_val;
            break;
         case FTLOWINTERFACE:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->LowInterface = u_val;
            break;
         case FTLOWADJACENTTYPE:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->LowAdjType = u_val;
            break;
         case FTLOWADJACENTADDRESS:
            sz = sizeof(fp->LowAdjAddress);
            data = asn_parse_string(data, &d_len, &type,
               fp->LowAdjAddress,&sz);
            break;
         case FTLOWADJACENTMASK:
            sz = sizeof(fp->LowAdjMask);
	    data = asn_parse_string(data, &d_len, &type,
               fp->LowAdjMask,&sz);
            break;
         case FTLOWPEERTYPE:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->LowPeerType = u_val;
#if PKTESTING
            printf("   unpack(): a=%d, LowPeerType=%d\n", a, u_val);
#endif
            break;
         case FTLOWPEERADDRESS:
            sz = sizeof(fp->LowPeerAddress);
            data = asn_parse_string(data, &d_len, &type,
               fp->LowPeerAddress,&sz);
#if PKTESTING
            printf("   unpack(): a=%d, sz=%d, LowPeerAddress=%d",
               a, sz, fp->LowPeerAddress[0]);
            for (j = 1; j != sz; ++j) printf(".%d", fp->LowPeerAddress[j]);
            printf("\n");
#endif
            break;
         case FTLOWPEERMASK:
            sz = sizeof(fp->LowPeerMask);
	    data = asn_parse_string(data, &d_len, &type,
               fp->LowPeerMask,&sz);
            break;
         case FTLOWTRANSTYPE:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->LowTransType = u_val;
            break;
         case FTLOWTRANSADDRESS:
            sz = sizeof(fp->LowTransAddress);
	    data = asn_parse_string(data, &d_len, &type,
               fp->LowTransAddress,&sz);
            break;
         case FTLOWTRANSMASK:
            sz = sizeof(fp->LowTransMask);
	    data = asn_parse_string(data, &d_len, &type,
               fp->LowTransMask,&sz);
            break;
         case FTHIINTERFACE:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->HighInterface = u_val;
            break;
         case FTHIADJACENTTYPE:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->HighAdjType = u_val;
            break;
         case FTHIADJACENTADDRESS:
            sz = sizeof(fp->HighAdjAddress);
            data = asn_parse_string(data, &d_len, &type,
               fp->HighAdjAddress,&sz);
            break;
         case FTHIADJACENTMASK:
            sz = sizeof(fp->HighAdjMask);
	    data = asn_parse_string(data, &d_len, &type,
               fp->HighAdjMask,&sz);
            break;
         case FTHIPEERTYPE:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->HighPeerType = u_val;
            break;
         case FTHIPEERADDRESS:
            sz = sizeof(fp->HighPeerAddress);
            data = asn_parse_string(data, &d_len, &type,
               fp->HighPeerAddress,&sz);
#if PKTESTING
            printf("   unpack(): a=%d, sz=%d, HighPeerAddress=%d",
               a, sz, fp->HighPeerAddress[0]);
            for (j = 1; j != sz; ++j) printf(".%d", fp->HighPeerAddress[j]);
            printf("\n");
#endif
            break;
         case FTHIPEERMASK:
            sz = sizeof(fp->HighPeerMask);
	    data = asn_parse_string(data, &d_len, &type,
               fp->HighPeerMask,&sz);
            break;
         case FTHITRANSTYPE:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->HighTransType = u_val;
            break;
         case FTHITRANSADDRESS:
            sz = sizeof(fp->HighTransAddress);
   	    data = asn_parse_string(data, &d_len, &type,
               fp->HighTransAddress,&sz);
            break;
         case FTHITRANSMASK:
            sz = sizeof(fp->HighTransMask);
	    data = asn_parse_string(data, &d_len, &type,
               fp->HighTransMask,&sz);
            break;
         case FTRULESET:
            fp->FlowRuleSet = rs;
            break;
         case FTUPOCTETS: 
	    data = asn_parse_unsigned_int64(data, &d_len, &type,
	       &fp->FwdBytes, sizeof(fp->FwdBytes));
            break;
         case FTUPPDUS:
	    data = asn_parse_unsigned_int64(data, &d_len, &type,
	       &fp->FwdPackets, sizeof(fp->FwdPackets));
            break;
         case FTDOWNOCTETS:
	    data = asn_parse_unsigned_int64(data, &d_len, &type,
	       &fp->BackBytes, sizeof(fp->BackBytes));
            break;
         case FTDOWNPDUS:
	    data = asn_parse_unsigned_int64(data, &d_len, &type,
	       &fp->BackPackets, sizeof(fp->BackPackets));
            break;
         case FTFIRSTTIME:
	    data = asn_parse_unsigned_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->FirstTime = u_val;
            break;
         case FTLASTTIME:
	    data = asn_parse_unsigned_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->LastTime = u_val;
            break;
         case FTSOURCECLASS:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->SourceClass = u_val;
            break;
         case FTDESTCLASS:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->DestClass = u_val;
            break;
         case FTFLOWCLASS:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->FlowClass = u_val;
            break;
         case FTSOURCEKIND:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->SourceKind = u_val;
            break;
         case FTDESTKIND:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->DestKind = u_val;
            break;
         case FTFLOWKIND:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->FlowKind = u_val;
            break;
         case FTDSCODEPOINT:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->DSCodePoint = u_val;
            break;

#if defined(NETFLOW)
         case FTMETERID:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->MeterId = u_val;
            break;
         case FTLOWROUTEASN:
            sz = sizeof(fp->LowRouteASN);
            data = asn_parse_string(data, &d_len, &type,
               fp->LowRouteASN,&sz);
            break;
         case FTLOWROUTEPREFIX:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->LowRoutePrefix = u_val;
            break;
         case FTHIROUTEASN:
            sz = sizeof(fp->HighRouteASN);
	    data = asn_parse_string(data, &d_len, &type,
               fp->HighRouteASN,&sz);
            break;
         case FTHIROUTEPREFIX:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->HighRoutePrefix = u_val;
            break;
#endif

#ifdef NEW_ATR
         case FTDISTRIBUTIONS:
	    data = asn_parse_unsigned_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->distrib_bits = u_val;
            break;
         case FTTOPACKETSIZE:
         case FTFROMPACKETSIZE:
         case FTTOINTERARRIVALTIME:
         case FTFROMINTERARRIVALTIME:
         case FTTOTURNAROUNDTIME:
         case FTFROMTURNAROUNDTIME:
         case FTTOBITRATE:
         case FTFROMBITRATE:
         case FTTOPDURATE:
         case FTFROMPDURATE:

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

/*         case FTTOTURNAROUNDTIME1:
         case FTFROMTURNAROUNDTIME1:  */
         case FTTOTURNAROUNDTIME2:
         case FTFROMTURNAROUNDTIME2:
         case FTTOTURNAROUNDTIME3:
         case FTFROMTURNAROUNDTIME3:
         case FTTOTURNAROUNDTIME4:
         case FTFROMTURNAROUNDTIME4:
         case FTTOFLOWOCTETS:
         case FTFROMFLOWOCTETS:
         case FTTOFLOWPDUS:
         case FTFROMFLOWPDUS:
         case FTFLOWTIME:
            d = &distrib[nd];  d->next = NULL;
            d->selector = a;
            data = asn_parse_distribution(data, &d_len, d);
            if (nd == 0) fp->distrib_list = d;
            else distrib[nd-1].next = d;
            ++nd;
            break;

         case FTTOLOSTPDUS:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->ToLostPDUs = u_val;
            break;
         case FTFROMLOSTPDUS:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->FromLostPDUs = u_val;
            break;
         case FTTOPQOVERFLOWS:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->ToPQOverflows = u_val;
            break;
         case FTFROMPQOVERFLOWS:
	    data = asn_parse_int(data, &d_len, &type,
               &u_val, sizeof(u_val));
            fp->FromPQOverflows = u_val;
            break;

         case FTTCPDATA:
            data = asn_parse_tcp_data(data, &d_len, &sfdat);
            fp->sfd = &sfdat;
            break;
#endif  /* NEW_ATR */
            }
         }
#if PKTESTING > 1
      printf("      unpack() loop: a=%u, data=%lu, d_len=%u\n",
         a, data, d_len);
#endif
      }

   process_row(fp);
   }

int get_package_info(struct meter_status *ms,
   int rs, Bit32 t_time,
   void (*process_row)(struct flow_info *fp))
{
   oid name[MAX_NAME_LEN];
   int a,k, name_length, status;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   int result, running, nflows, lfpg, fpg, err, count;
   unsigned char *fb; int plen;

   memcpy((char *)name, (char *)o_ftDataPackage, 11*sizeof(oid));
   for (a = 1, k = 12; a != 1+LASTATTRIB; ++a) {
      if (ms->required[a] == 0) continue;
      if (a == FTFLOWINDEX ||   /* No access to table indexes */
            a == FTRULESET)
         continue;
      name[k++] = (oid)a;
      }
   name[11] = (oid)k-12;
   name[k++] = (oid)rs;
   name[k++] = (oid)t_time;
   name[k++] = (oid)0;
   name_length = k;

   nflows = lfpg = 0;  fpg = ms->flows_per_getbulk+2;
#if TUNING_GETBULK
  fprintf(stderr, "------- initial fpg:  %d\n", fpg);
#endif
   result = 0;  running = 1;
   while (running) {
      pdu = snmp_pdu_create(BULK_REQ_MSG);
      pdu->non_repeaters = 0;
      pdu->max_repetitions = lfpg = fpg;  fpg = 0;
         /* Min package size is 16+4+3 bytes */
      snmp_add_null_var(pdu, name, name_length);

      status = snmp_synch_response(ms->ss, pdu, &response);
      if (status == STAT_SUCCESS) {
	 if (response->errstat == SNMP_ERR_NOERROR) {
	    for (fpg = 0, vars = response->variables;  vars;
		  vars = vars->next_variable) {
	       if (vars->name_length != k  /* Shorter than requested oid */
                     || vars->name[k-3] != rs  /* Wrong rule set */
                     || vars->name[7] != (oid)2) {  /* Not in flow table */
		  running = 0;
		  break;  /* Past the flow table section */
		  }
	       if (vars->type == ASN_OCTET_STR) {
                  STRING_PTR(fb,plen);  result = 1;
                  unpack_package(ms,  /* Unpack direct from incoming PDU */
                     fb, rs, (unsigned int)vars->name[k-1], process_row);
                  ++fpg;
		  }
               else {
                  fprintf(stderr, "Didn't get ASN_OCTET_STRING for package\n");
		  running = 0;
		  }
	       if (!vars->next_variable) { /* Repeat on last variable */
		  memcpy((char *)name, (char *)vars->name,
			vars->name_length * sizeof(oid));
		  name_length = vars->name_length;
		  }
	       }
            if (fpg == 1 && response->pdu_len > ms->mx_one_pkg_pdu_sz) {
               log_msg(LOG_INFO, FALSE,
                  "Only one 'package' flow in SNMP PDU (%d bytes) for meter %s!\n",
                  ms->mx_one_pkg_pdu_sz = response->pdu_len, ms->name);
	       }
#if TUNING_GETBULK
            fprintf(stderr, "   flows this getbulk: %d\n", fpg);
#endif
            nflows += fpg;
	    }
	 else {
	    running = 0;  /* Error or timeout */
            if (response->errstat == SNMP_ERR_TOOBIG) {
               log_msg(LOG_INFO, FALSE,
                  "'Package' flow too big for SNMP PDU for meter %s!\n",
                  ms->name);
               }
#if TESTING
            fprintf(stderr,
               "get_package_info(): Error in packet, reason = %s\n",
	       snmp_errstring(response->errstat));
            for (count = 1, vars = response->variables;
	       vars && count != response->errindex;
               vars = vars->next_variable, count++) ;
	    if (vars) print_objid(vars->name, vars->name_length);
	    fprintf(stderr, "\n");
#endif
	    }
	 }
      if (response)
	 snmp_free_pdu(response);
      }
   if (nflows > ms->flows_per_getbulk)  /* More than one snmp PDU needed */
      ms->flows_per_getbulk = lfpg;  /* lfpg = flows in a full PDU */
   mswait(ms->snmp_delay);
   return result;
   }

unsigned short getshort(ucp)
unsigned char *ucp;
{
   return ucp[0]<<8 | ucp[1];
   }

Bit32 getBit32(ucp)
unsigned char *ucp;
{
   return ucp[0]<<24 | ucp[1]<<16 | ucp[2]<<8 | ucp[3];
   }

void getcounter64(counter64 *c, unsigned char *ucp)
{
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
   *c = (counter64)ucp[0]<<56 | (counter64)ucp[1]<<48 |
      (counter64)ucp[2]<<40 | (counter64)ucp[3]<<32 |
      (counter64)ucp[4]<<24 | (counter64)ucp[5]<<16 |
      (counter64)ucp[6]<<8 | (counter64)ucp[7];
#else
   c->high = (u_long)ucp[0]<<24 | (u_long)ucp[1]<<16 |
      (u_long)ucp[2]<<8 | (u_long)ucp[3];
   c->low  = (u_long)ucp[4]<<24 | (u_long)ucp[5]<<16 |
      (u_long)ucp[6]<<8 | (u_long)ucp[7];
#endif
   }

int column_info(struct meter_status *ms, unsigned char **fb,
   unsigned char a, Bit32 ft, int *fn, int *len);

unsigned short get_slice(struct meter_status *ms,
   unsigned short first_row, unsigned char col, 
   unsigned char first)
{
   int a, fn, row, len, n,last_n, r, sz;
   struct flow_info *fp;
   unsigned char *ucp;
   fn = first_row;  row = n = 0;
   a = col;
   if (a > FTFLOWTIMEMARK && a <= LASTATTRIB) 
      --a;  /* v4 shifted attributes up by 1 */
   if (column_info(ms,&ucp, a,ms->OurLastCollectTime,&fn,&len)
         && len != 0) {
      for ( ;  row < MX_BLOB_FLOWS;  ) {
         fp = &flows[row];
         last_n = n;  /* Allow flow nbrs to start at 1.  8 Nov 96 */
         n = getshort(ucp);  ucp += 2;  /* Flow nbr */
         sz = attribs[col].len;
         if (n <= last_n) break;  /* No more active flows in blob */
         switch (col) {
	 case FTFLOWINDEX:  /* Used to synchronise column blobs */
	    break;
	 case FTFLOWSTATUS:
            fp->FlowStatus = *ucp;
            break;
	 case FTLOWINTERFACE:
            fp->LowInterface = *ucp;
            break;
	 case FTLOWADJACENTTYPE:
            fp->LowAdjType = *ucp;
            break;
	 case FTLOWADJACENTADDRESS:
	    memcpy(fp->LowAdjAddress,ucp,MAC_ADDR_LEN);
	    break;
	 case FTLOWADJACENTMASK:
	    memcpy(fp->LowAdjMask,ucp,MAC_ADDR_LEN);
	    break;
	 case FTLOWPEERTYPE:
            fp->LowPeerType = new_addr_type[*ucp];
	    break;
	 case FTLOWPEERADDRESS:
            memset((char *)fp->LowPeerAddress,0,PEER_ADDR_LEN);
            sz = *ucp++;
	    memcpy(fp->LowPeerAddress,ucp,sz);
	    break;
	 case FTLOWPEERMASK:
            memset((char *)fp->LowPeerMask,0,PEER_ADDR_LEN);
            sz = *ucp++;
	    memcpy(fp->LowPeerMask,ucp,sz);
	    break;
	 case FTLOWTRANSTYPE:
            fp->LowTransType = *ucp;
	    break;
	 case FTLOWTRANSADDRESS:
	    memcpy(fp->LowTransAddress,ucp,TRANS_ADDR_LEN);
	    break;
	 case FTLOWTRANSMASK:
	    memcpy(fp->LowTransMask,ucp,TRANS_ADDR_LEN);
	    break;
	 case FTHIINTERFACE:
            fp->HighInterface = *ucp;
            break;
	 case FTHIADJACENTTYPE:
            fp->HighAdjType = *ucp;
	    break;
	 case FTHIADJACENTADDRESS:
	    memcpy(fp->HighAdjAddress,ucp,MAC_ADDR_LEN);
	    break;
	 case FTHIADJACENTMASK:
	    memcpy(fp->HighAdjMask,ucp,MAC_ADDR_LEN);
	    break;
	 case FTHIPEERTYPE:
            fp->HighPeerType = new_addr_type[*ucp];
	    break;
	 case FTHIPEERADDRESS:
            memset((char *)fp->HighPeerAddress,0,PEER_ADDR_LEN);
            sz = *ucp++;
	    memcpy(fp->HighPeerAddress,ucp,sz);
	    break;
	 case FTHIPEERMASK:
            memset((char *)fp->HighPeerMask,0,PEER_ADDR_LEN);
            sz = *ucp++;
	    memcpy(fp->HighPeerMask,ucp,sz);
	    break;
	 case FTHITRANSTYPE:
            fp->HighTransType = *ucp;
	    break;
	 case FTHITRANSADDRESS:
	    memcpy(fp->HighTransAddress,ucp,TRANS_ADDR_LEN);
	    break;
	 case FTHITRANSMASK:
	    memcpy(fp->HighTransMask,ucp,TRANS_ADDR_LEN);
	    break;
	 case FTRULESET:
            fp->FlowRuleSet = *ucp;
	    break;
         case FTUPOCTETS:
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
            fp->FwdBytes = getBit32(ucp);
#else
            fp->FwdBytes.high = 0;  fp->FwdBytes.low = getBit32(ucp);
#endif
            sz = 4;
            break;
	 case FTUPPDUS:
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
            fp->FwdPackets = getBit32(ucp);
#else
            fp->FwdPackets.high = 0;  fp->FwdPackets.low = getBit32(ucp);
#endif
            sz = 4;
	    break;
	 case FTDOWNOCTETS:
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
            fp->BackBytes = getBit32(ucp);
#else
            fp->BackBytes.high = 0;  fp->BackBytes.low = getBit32(ucp);
#endif
            sz = 4;
	    break;
	 case FTDOWNPDUS:
#if SIZEOF_LONG_LONG == 8 || SIZEOF_LONG == 8
            fp->BackPackets = getBit32(ucp);
#else
            fp->BackPackets.high = 0;  fp->BackPackets.low = getBit32(ucp);
#endif
            sz = 4;
	    break;
	 case FTFIRSTTIME:
            fp->FirstTime = getBit32(ucp);
	    break;
	 case FTLASTTIME:
            fp->LastTime = getBit32(ucp);
	    break;
	 case FTSOURCECLASS:
            fp->SourceClass = *ucp;
	    break;
	 case FTDESTCLASS:
            fp->DestClass = *ucp;
	    break;
	 case FTFLOWCLASS:
            fp->FlowClass = *ucp;
	    break;
	 case FTSOURCEKIND:
            fp->SourceKind = *ucp;
	    break;
	 case FTDESTKIND:
            fp->DestKind = *ucp;
	    break;
	 case FTFLOWKIND:
            fp->FlowKind = *ucp;
	    break;
         case FTDSCODEPOINT:
            fp->DSCodePoint = *ucp;
	    break;
	    }
         ucp += sz;
         if (first) {
            fp->FlowIndex = n;  ++row;
	    }
         else {
            if (n == fp->FlowIndex) ++row;
            }
         }
      }	

   if (len == 0) return 0;  /* No flows found */
   if (first) flows[row].FlowIndex = n;  /* Return end-of-blob marker */
   return row;  /* Nbr of rows may decrease with later columns */
   }

void get_colblob_data(struct meter_status *ms, int rs, Bit32 t,
   void (*process_row)(struct flow_info *fp))
{
   int first_row, first, a, col, r, nrows;

   if (ms->format[0] != 0) {  /* Collect flow data */
      first_row = 0;
         /* Flow indeces may start at 1.  8 Nov 96 */
      do {  /* Outer loop: get slices for successive bands */
         for (first = 1, a = 0;  (col = col_order[a]) != '\0'; ++a) {
            if (ms->required[col] == 0) continue;
            r = get_slice(ms, first_row,col, first);
            if (first) {
               nrows = r;  first = 0;
               }
            else if (r < nrows) nrows = r;
            if (nrows == 0) break;
            }
         if (nrows == 0) break;
         if (testing && ms->flows != NULL) {
            fprintf(ms->flows,
               "#monitor(): frst_row=%u, nrows=%u, nxt_row=%d, end_mark=%u\n",
               first_row, nrows, 
               nrows != 0 ? flows[nrows-1].FlowIndex : -1,
               flows[nrows].FlowIndex);
	    }
         first_row = flows[nrows-1].FlowIndex;  /* Last data row */
         for (r = 0; r != nrows; ++r)
            process_row(&flows[r]);
         } while (flows[nrows].FlowIndex != 0);
      }
   }

int same_acct_oid(oid *a, oid *b)  
   /* Compare oids for equality within internet-accounting MIB */
{
   int j;
   for (j = 7; j != 11; ++j) {
      if (a[j] != b[j]) return 0;
      }
   return 1;
   }

int column_info(struct meter_status *ms, unsigned char **fb,
   unsigned char a, Bit32 ft, int *fn, int *len)
{
   int lfn, i, count, status;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   int result;

   if (testing)
      fprintf(stderr, "column_info(ms,fb,a=%d,ft=%lu,fn=%d)\n",a,ft,*fn);
   *len = result = 0;
   pdu = snmp_pdu_create(GETNEXT_REQ_MSG);
   lfn = *fn;
   if (lfn < 1) lfn = 1;  /* Old MIB flows started at 2! */
   ADD_X3_VAR(o_v3ColumnBlob,a,ft,lfn)
   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
	 vars = response->variables;
         if (!same_acct_oid(vars->name,o_v3ColumnBlob)) {
            snmp_free_pdu(response);
            return 1;  /* No more flows with last-active time > ft */
	    }
         STRING_PTR(*fb,*len);  result = 1;
	 }
      else {
	 log_snmp_err("column_info()", response);
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return result;
   }

int flow_info(struct meter_status *ms, struct flow_info *fi, unsigned int fn)
{
   int rs, i, count, status, result;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   rs = ms->c_ruleset.ri_Index;
   result = 0;
   pdu = snmp_pdu_create(GET_REQ_MSG);
   if (ms->MIBver == 4) {  /* Use 'new' MIB attribute nbr */
      ADD_X3_VAR(o_ftLowInterface, rs, 1, fn);
      ADD_X3_VAR(o_ftLowAdjacentType, rs, 1, fn);
      ADD_X3_VAR(o_ftLowAdjacentAddress, rs, 1, fn);
      ADD_X3_VAR(o_ftLowPeerType, rs, 1, fn);
      ADD_X3_VAR(o_ftLowPeerAddress, rs, 1, fn);
      ADD_X3_VAR(o_ftLowTransType, rs, 1, fn);
      ADD_X3_VAR(o_ftLowTransAddress, rs, 1, fn);
      ADD_X3_VAR(o_ftSourceClass, rs, 1, fn);
      ADD_X3_VAR(o_ftSourceKind, rs, 1, fn);
      ADD_X3_VAR(o_ftHighInterface, rs, 1, fn);
      ADD_X3_VAR(o_ftHighAdjacentType, rs, 1, fn);
      ADD_X3_VAR(o_ftHighAdjacentAddress, rs, 1, fn);
      ADD_X3_VAR(o_ftHighPeerType, rs, 1, fn);
      ADD_X3_VAR(o_ftHighPeerAddress, rs, 1, fn);
      ADD_X3_VAR(o_ftHighTransType, rs, 1, fn);
      ADD_X3_VAR(o_ftHighTransAddress, rs, 1, fn);
      ADD_X3_VAR(o_ftDestClass, rs, 1, fn);
      ADD_X3_VAR(o_ftDestKind, rs, 1, fn);
      ADD_X3_VAR(o_ftFlowClass, rs, 1, fn);
      ADD_X3_VAR(o_ftFlowKind, rs, 1, fn);
      ADD_X3_VAR(o_ftUpOctets, rs, 1, fn);
      ADD_X3_VAR(o_ftUpPDUs, rs, 1, fn);
      ADD_X3_VAR(o_ftDownOctets, rs, 1, fn);
      ADD_X3_VAR(o_ftDownPDUs, rs, 1, fn);
      ADD_X3_VAR(o_ftFirstTime, rs, 1, fn);
      ADD_X3_VAR(o_ftLastTime, rs, 1, fn);
#if defined(NEW_ATR)
      ADD_X3_VAR(o_ftDSCodePoint, rs, 1, fn);
#endif
#if defined(NETFLOW)
      ADD_X3_VAR(o_ftMeterId, rs, 1, fn);
      ADD_X3_VAR(o_ftSourceASN, rs, 1, fn);
      ADD_X3_VAR(o_ftSourcePrefix, rs, 1, fn);
      ADD_X3_VAR(o_ftDestASN, rs, 1, fn);
      ADD_X3_VAR(o_ftDestPrefix, rs, 1, fn);
#endif
      }
   else {  /* Use 'old' MIB attribute nbr */
      ADD_X_VAR(o_v3ftLowInterface, fn);
      ADD_X_VAR(o_v3ftLowAdjacentType, fn);
      ADD_X_VAR(o_v3ftLowAdjacentAddress, fn);
      ADD_X_VAR(o_v3ftLowPeerType, fn);
      ADD_X_VAR(o_v3ftLowPeerAddress, fn);
      ADD_X_VAR(o_v3ftLowTransType, fn);
      ADD_X_VAR(o_v3ftLowTransAddress, fn);
      ADD_X_VAR(o_v3ftSourceClass, fn);
      ADD_X_VAR(o_v3ftSourceKind, fn);
      ADD_X_VAR(o_v3ftHighInterface, fn);
      ADD_X_VAR(o_v3ftHighAdjacentType, fn);
      ADD_X_VAR(o_v3ftHighAdjacentAddress, fn);
      ADD_X_VAR(o_v3ftHighPeerType, fn);
      ADD_X_VAR(o_v3ftHighPeerAddress, fn);
      ADD_X_VAR(o_v3ftHighTransType, fn);
      ADD_X_VAR(o_v3ftHighTransAddress, fn);
      ADD_X_VAR(o_v3ftDestClass, fn);
      ADD_X_VAR(o_v3ftDestKind, fn);
      ADD_X_VAR(o_v3ftFlowClass, fn);
      ADD_X_VAR(o_v3ftFlowKind, fn);
      ADD_X_VAR(o_v3ftUpOctets, fn);
      ADD_X_VAR(o_v3ftUpPDUs, fn);
      ADD_X_VAR(o_v3ftDownOctets, fn);
      ADD_X_VAR(o_v3ftDownPDUs, fn);
      ADD_X_VAR(o_v3ftFirstTime, fn);
      ADD_X_VAR(o_v3ftLastTime, fn);
      }
   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         if (testing)fprintf(stderr, "   snmp response OK\n");
         result = 1;
	 vars = response->variables;
         if (vars->type != INTEGER)  /* Probably SNMP_NOSUCHINSTANCE; */
            result = 0;  /*   flow table row has been garbage collected */
         else {
   	    INT_VAL(fi->LowInterface);
            vars = vars->next_variable;
	    INT_VAL(fi->LowAdjType);
            vars = vars->next_variable;
   	    STRING_VAL(fi->LowAdjAddress);
            vars = vars->next_variable;
	    INT_VAL(fi->LowPeerType);
            if (ms->MIBver == 3)
               fi->LowPeerType = new_addr_type[fi->LowPeerType];
            vars = vars->next_variable;
	    STRING_VAL(fi->LowPeerAddress);
            vars = vars->next_variable;
	    INT_VAL(fi->LowTransType);
            vars = vars->next_variable;
	    STRING_VAL(fi->LowTransAddress);
            vars = vars->next_variable;
	    INT_VAL(fi->SourceClass);
            vars = vars->next_variable;
	    INT_VAL(fi->SourceKind);
            vars = vars->next_variable;
	    INT_VAL(fi->HighInterface);
            vars = vars->next_variable;
	    INT_VAL(fi->HighAdjType);
            vars = vars->next_variable;
	    STRING_VAL(fi->HighAdjAddress);
            vars = vars->next_variable;
	    INT_VAL(fi->HighPeerType);
            if (ms->MIBver == 3)
               fi->HighPeerType = new_addr_type[fi->HighPeerType];
            vars = vars->next_variable;
	    STRING_VAL(fi->HighPeerAddress);
            vars = vars->next_variable;
	    INT_VAL(fi->HighTransType);
            vars = vars->next_variable;
	    STRING_VAL(fi->HighTransAddress);
            vars = vars->next_variable;
	    INT_VAL(fi->DestClass);
            vars = vars->next_variable;
	    INT_VAL(fi->DestKind);
            vars = vars->next_variable;
	    INT_VAL(fi->FlowClass);
            vars = vars->next_variable;
	    INT_VAL(fi->FlowKind);
            vars = vars->next_variable;
	    C64_VAL(fi->FwdBytes);
            vars = vars->next_variable;
	    C64_VAL(fi->FwdPackets);
            vars = vars->next_variable;
	    C64_VAL(fi->BackBytes);
            vars = vars->next_variable;
	    C64_VAL(fi->BackPackets);
            vars = vars->next_variable;
	    INT_VAL(fi->FirstTime);
            vars = vars->next_variable;
	    INT_VAL(fi->LastTime);
#if defined(NEW_ATR)
            vars = vars->next_variable;
	    INT_VAL(fi->DSCodePoint);
#endif
#if defined(NETFLOW)
            vars = vars->next_variable;
            if (vars && vars->val.integer != (Int32 *)NULL) {
                  /* Meter has NetFlow attributes */
	       INT_VAL(fi->MeterId);
               vars = vars->next_variable;
               STRING_VAL(fi->LowRouteASN);
               vars = vars->next_variable;
	       INT_VAL(fi->LowRoutePrefix);
               vars = vars->next_variable;
               STRING_VAL(fi->HighRouteASN);
               vars = vars->next_variable;
	       INT_VAL(fi->HighRoutePrefix);
               }
#endif
            }
	 }
      else {
	 log_snmp_err("flow_info()", response);
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return result;
   }

int meter_is_current(struct meter_status *ms)
{
   int j;
   if (standard == 0) {
      sprintf(ms->version,"%lu.%lu", meter_maj(ms),meter_min(ms));
      for (j = 0;  ;  ++j) {
         if (ms->versionid[j] != CurrentVersion[j]) return 0;
         if (CurrentVersion[j] == 0) return 1;
         }
      }
   else return 1;
   }

int reset_FloodMode(struct meter_status *ms)
{
   int i, count, status;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;

   pdu = snmp_pdu_create(SET_REQ_MSG);
   ADD_VAR(o_FloodMode);
   vars = pdu->variables;
   SET_INT(TV_FALSE);

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         if (testing)fprintf(stderr, "Meter %s: FloodMode reset\n", ms->name);
         log_msg(LOG_INFO, FALSE,"Meter %s: FloodMode reset\n", ms->name);
	 }
      else {
	 log_snmp_err("reset_FloodMode()", response);
         ms->write_OK = 0;
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return 1;
   }

int meter_info(struct meter_status *ms)
{
   int count, status, result, j;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   Bit32 uptime, last_uptime;
   int FloodMark, FloodMode;
   char reader_name[NAMESZ+1];
   int crl_info_len;

   result = 0;
   last_uptime = ms->uptime;
   if (testing)
      fprintf(stderr, "meter_info(ms), statsreqd=%d\n", ms->statsreqd);
   pdu = snmp_pdu_create(GET_REQ_MSG);
   if (standard == 0) {  /* OK to use Auckland Enterprise MIB variables */
      if (ms->statsreqd) {
         ADD_VAR(o_msStatsTime);
         ADD_VAR(o_msNbrPackets);
         ADD_VAR(o_msTotPktBacklog);
         ADD_VAR(o_msMaxPktRate);
         ADD_VAR(o_msMaxPktBacklog);
         ADD_VAR(o_msNbrFlows);
         ADD_VAR(o_msFlowsRecovered);
         ADD_VAR(o_msRuleMatches);
         ADD_VAR(o_msHashSearches);
         ADD_VAR(o_msHashCompares);
         ADD_VAR(o_msTotalHashSize);
         ADD_VAR(o_msNbrHashEntries);
         ADD_VAR(o_msGCInterval);
         ADD_VAR(o_msAvIdle1000);
         ADD_VAR(o_msMinIdle1000);
         ADD_VAR(o_pcLostPackets);
         }
      }
   if (ms->MIBver == 4) {
      ADD_VAR(o_MaxFlows);
      ADD_VAR(o_FloodMark);
      ADD_VAR(o_ActiveFlows);
      ADD_VAR(o_FloodMode);
      if (ms->ci_c_Index != 0) {
         ADD_X_VAR(o_ciOwner, ms->ci_c_Index);
         ADD_X_VAR(o_ciLastTime, ms->ci_c_Index);
         }
      if (ms->c_ruleset.ri_Index != 0)
         ADD_X_VAR(o_riFlowRecords, ms->c_ruleset.ri_Index);
      if (ms->s_ruleset.ri_Index != 0) {
         ADD_X_VAR(o_riFlowRecords, ms->s_ruleset.ri_Index);
         ADD_X_VAR(o_miRunningStandby, ms->mi_Index);
	 }
      }
   if (ms->MIBver == 3) {  /* Need this for changing current ruleset! */
      ADD_VAR(o_v3MaxFlows);
      ADD_VAR(o_v3LastCollectTime);
      ADD_VAR(o_v3CurrentRuleSet);
      ADD_VAR(o_v3StandbyRuleSet);
      }

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status != STAT_SUCCESS) {  /* Network error */
      return 0;  /* Error (status=1) or timeout (status=2) */
      }
   else {
      if (response->errstat != SNMP_ERR_NOERROR) { /* Error from meter */
	 log_snmp_err("meter_info()", response);
	 }
      else {
         if (testing)fprintf(stderr, "   snmp response OK\n");
         result = 1;
	 vars = response->variables;
         if (standard == 0) {  /* OK to use UA Enterprise MIB variables */
            if (ms->statsreqd) {
               INT_VAL(ms->StatsTime);
               vars = vars->next_variable;
               INT_VAL(ms->NbrPackets);
               vars = vars->next_variable;
               INT_VAL(ms->TotPktBacklog);
               vars = vars->next_variable;
               INT_VAL(ms->MaxPktRate);
               vars = vars->next_variable;
               INT_VAL(ms->MaxPktBacklog);
               vars = vars->next_variable;
               INT_VAL(ms->NbrFlows);
               vars = vars->next_variable;
               INT_VAL(ms->FlowsRecovered);
               vars = vars->next_variable;
               INT_VAL(ms->RuleMatches);
               vars = vars->next_variable;
               INT_VAL(ms->HashSearches);
               vars = vars->next_variable;
               INT_VAL(ms->HashCompares);
               vars = vars->next_variable;
               INT_VAL(ms->TotalHashSize);
               vars = vars->next_variable;
               INT_VAL(ms->NbrHashEntries);
               vars = vars->next_variable;
               INT_VAL(ms->GCInterval);
               vars = vars->next_variable;
               INT_VAL(ms->AvIdle1000);
               vars = vars->next_variable;
               INT_VAL(ms->MinIdle1000);
               vars = vars->next_variable;
               INT_VAL(ms->LostPackets);
               if (testing) fprintf(stderr,
                  "  stats read: StatsTime=%lu\n",ms->StatsTime);
               vars = vars->next_variable;
               }
            }
         if (ms->MIBver == 4) {
	    INT_VAL(ms->MaxFlows);
            vars = vars->next_variable;
            INT_VAL(FloodMark);
            vars = vars->next_variable;
            INT_VAL(ms->ActiveFlows);
            vars = vars->next_variable;
            INT_VAL(FloodMode);
            if (FloodMode == TV_TRUE) {
               if (testing) fprintf(stderr,
                  "Meter %s in Flood mode!\n", ms->name);
               log_msg(LOG_INFO, FALSE, "Meter %s in Flood mode!\n", ms->name);
               ms->ActivePercent = (ms->ActiveFlows*100)/ms->MaxFlows;
               j = FloodMark-5;
               if (j < 0) j = 0;
               if (ms->ActivePercent <= j && ms->download_level != 2)
                  reset_FloodMode(ms);
               }
            if (ms->ci_c_Index != 0) {
               vars = vars->next_variable;
               memset(reader_name,0,sizeof(reader_name));
               STRING_VAL(reader_name);
               reader_name[sizeof(reader_name)-1] = '\0';
               vars = vars->next_variable;
               if (strcmp(reader_name,ms->owner_name) != 0)
                  ms->uptime = 0;  /* Owner has changed! */
               else INT_VAL(ms->uptime);  /* Reader LastTime */
               }
            if (ms->uptime >= last_uptime) {  /* Meter hasn't restarted */
               if (ms->c_ruleset.ri_Index != 0) {
                  vars = vars->next_variable;
                  if (vars->val.integer != NULL)
                     INT_VAL(ms->c_ruleset.ri_FlowRecords);
                  else ms->c_ruleset.ri_FlowRecords = 0;
	          }
               if (ms->s_ruleset.ri_Index != 0) {
                  vars = vars->next_variable;
                  if (vars->val.integer != NULL)
                     INT_VAL(ms->s_ruleset.ri_FlowRecords);
                  else ms->s_ruleset.ri_FlowRecords = 0;
                  vars = vars->next_variable;
                  if (vars->val.integer != NULL)
                     INT_VAL(j);
                  ms->mi_RunningStandby = j == TV_TRUE;
	          }
	       }
            }
         ms->NbrFlows =  /* Don't use UA variable */
            ms->c_ruleset.ri_FlowRecords + ms->s_ruleset.ri_FlowRecords;
         if (ms->MIBver == 3) {
            vars = vars->next_variable;
	    INT_VAL(ms->MaxFlows);
            vars = vars->next_variable;
	    INT_VAL(ms->uptime);
            vars = vars->next_variable;
	    INT_VAL(ms->CurrentRuleSet);
            vars = vars->next_variable;
	    INT_VAL(ms->StandbyRuleSet);
	    }
#if METER_INFO_TEST
         printf("meter_info() values received:\n  descr=%s, version=%d",
            ms->descr, ms->versionid[0]);
         for (j = 1; ms->versionid[j] != 0; ++j)
            printf(".%d", ms->versionid[j]);
         printf("\n  ra_len=%d, interface=%s\n",
            ms->meter_ra_len, ms->interface);
         if (ms->statsreqd) {
            printf("    StatsTime=%u, NbrPackets=%u, TotPktBacklog=%u\n",
               ms->StatsTime, ms->NbrPackets, ms->TotPktBacklog);
            printf("    MaxPktRate=%u, MaxPktBacklog=%u, NbrFlows=%u\n",
               ms->MaxPktRate, ms->MaxPktBacklog, ms->NbrFlows);
            printf("    FlowsRecovd=%u, RuleMatches=%u, HashSearches=%u\n",
               ms->FlowsRecovered, ms->RuleMatches, ms->HashSearches);
            printf("    HashCompares=%u, TotalHashSize=%u, NbrHashEnts=%u\n",
               ms->HashCompares, ms->TotalHashSize, ms->NbrHashEntries);
            printf("    GCInterval=%u, AvIdle1000=%u, MinIdle1000=%u\n",
               ms->GCInterval, ms->AvIdle1000, ms->MinIdle1000);
            printf("    LostPackets=%u, StatsTime=%u\n",
               ms->LostPackets, ms->StatsTime);
            }
         printf("  MaxFlows=%u, FloodMark=%u, ActiveFlows=%u, FloodMode=%u\n", 
            ms->MaxFlows, FloodMark, ms->ActiveFlows, FloodMode);
         printf("  reader_name=%s, uptime=%u\n",
            reader_name, ms->uptime);
#endif  /* METER_INFO_TEST */
         }
      snmp_free_pdu(response);
      }
   mswait(ms->snmp_delay);
   if (result != 1) return 0;  /* Failed to get meter info */

   if (standard != 0)  /* Don't use Auckland Enterprise MIB variables */
      return result;
   if (ms->statsreqd && ms->write_OK) {  /* Zero meter statistics */
      if (testing) fprintf(stderr, "   about to zero meter stats\n");
      result = 0;
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_VAR(o_msStatsReset);
      vars = pdu->variables;
      SET_INT(1);
      status = snmp_synch_response(ms->ss, pdu, &response);
      if (status == STAT_SUCCESS) {
         if (testing)fprintf(stderr, "  stats zeroed\n");
         if (response->errstat != SNMP_ERR_NOERROR) {
	    log_snmp_err("meter_info(Zero stats)", response);
            ms->write_OK = 0;  /* Set failed, don't try again */
	    }
         else result = 1;  /* No errors */
         snmp_free_pdu(response);
         }
      else {
         return 0;  /* Error or timeout */
         }
      mswait(ms->snmp_delay);
      }
   return result;
   }

int meter_type(struct meter_status *ms)  /* 0 = unknown, 1 = V3, 2 = V4 */
{
   int i, count, status, mtype, result;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;

   ms->MIBver = 3;  /* Old (Experimental) Meter MIB */
   mtype = 0;  /* Determine meter type .. */
   pdu = snmp_pdu_create(GET_REQ_MSG);
   ADD_VAR(o_sysVersionId);  /* Want this for ver_min */
   ADD_VAR(o_MaxFlows);  /* OID changed between MIBs 3 and 4! */
   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         if (testing)fprintf(stderr, "   snmp response OK\n");
	 vars = response->variables;
	 OID_VAL(ms->versionid);
         ms->versionid[vars->val_len / sizeof(oid)] = 0;
         vars = vars->next_variable;
         if (vars->type == INTEGER) {
            ms->MIBver = 4;  /* RTFM (Proposed Standard) Meter MIB */
            mtype = 2;
            }
         else if (vars->type == TIMETICKS)
            mtype = 1;  /* Old (v3) MIB */
	 else mtype = 0;  /* Unknown */
         }
      else {
	 log_snmp_err("meter_type()", response);
	 mtype = 0;
	 }
      snmp_free_pdu(response);
      }
   else {
       return 0;  /* Error or timeout */
       }
   mswait(ms->snmp_delay);

   result = 1;  /* Get meter 'fixed info' objects .. */
   pdu = snmp_pdu_create(GET_REQ_MSG);
   ADD_VAR(o_sysDescr);
   ADD_X2_VAR(o_rtRuleMask, 1,1);  /* First rule in default ruleset */
   if (ms->MIBver == 4) {
      ADD_VAR(o_msInterfaceName);
      if (meter_min(ms) >= 4) {
         ADD_VAR(o_trInfo);  /* Objects for Trace Files */
         }
      }
   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
         if (testing)fprintf(stderr, "   snmp response OK\n");
	 vars = response->variables;
	 STRING_VAL(ms->descr);
         ms->descr[sizeof(ms->descr)-1] = '\0';
         vars = vars->next_variable;
         ms->meter_ra_len = vars->val_len >= RULE_ADDR_LEN ? RULE_ADDR_LEN
	    : vars->val_len;  /* Truncate for non-V6 meters */
         if (ms->MIBver == 4) {
            vars = vars->next_variable;
	    STRING_VAL(ms->interface);
            ms->interface[sizeof(ms->interface)-1] = '\0';
            if (!isprint(ms->interface[0])) 
               /* Early 3.2 PC meters returned \B0 ! */
               strcpy(ms->interface,"eth0");
            if (meter_min(ms) >= 4) {
               vars = vars->next_variable;
               if (vars->val_len != 0) {  /* Meter has a trace info string */
	          if (ms->crl_info == NULL)  /* First time seen */
                     ms->crl_info = (char *)malloc(vars->val_len + 1);
                  else if(vars->val_len > strlen(ms->crl_info)) {
	             free(ms->crl_info);  /* len increased */
                     ms->crl_info = (char *)malloc(vars->val_len + 1);
                     }
                  memcpy(ms->crl_info, vars->val.string, vars->val_len);
                  ms->crl_info[vars->val_len] = '\0';
                  }
               }
            }
         }
      else {
	 log_snmp_err("meter_type()", response);
	 result = 0;
	 }
      snmp_free_pdu(response);
      }
   else {
       return 0;  /* Error or timeout */
       }
   mswait(ms->snmp_delay);
   return result == 0 ? 0 : /* Failed to get 'fixed info' */
      mtype;  /* OK, return meter type */
   }

int set_rule_info(ms,which)  /* which = 0 to set rule table size */
struct meter_status *ms;     /*         1 to set meter's CurrentRuleSet */
int which;                   /*         2 to set meter's StandbyRuleSet */
{
   int i, count, status, rc = 1;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;
   char *op;

   if (ms->MIBver == 4) return 0;  /* $$$ */
   if (!ms->write_OK) return 1;
   pdu = snmp_pdu_create(SET_REQ_MSG);
   switch (which) {
   case 0:
      op = "table size";
      ADD_X_VAR(o_riRuleSize, ms->ruleset);
      vars = pdu->variables;
      SET_INT(ms->nrules);
      break;
   case 1:
      op = "CurrentRS";
      ADD_VAR(o_v3CurrentRuleSet);
      vars = pdu->variables;
      SET_INT(ms->c_ruleset.ri_Index);
      break;
   case 2:
      op = "StandbyRS";
      ADD_VAR(o_v3StandbyRuleSet);
      vars = pdu->variables;
      SET_INT(ms->s_ruleset.ri_Index);
      break;
      }

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {

         switch (which) {
         case 0:
            log_msg(LOG_INFO, FALSE,
		"Meter %s: sri(): set %d sizes set to %d rules + %d counts\n", 
                 ms->name, ms->ruleset,ms->nrules,ms->ncounts);
            break;
         case 1:
            log_msg(LOG_INFO, FALSE,
		    "Meter %s: sri(): using rule set %d\n",
		    ms->name, ms->c_ruleset.ri_Index);
            break;
         case 2:
            log_msg(LOG_INFO, FALSE,
		    "Meter %s: sri(): standby rule set is %d\n",
		    ms->name, ms->s_ruleset.ri_Index);
            break;
            }
	 }
      else {
	 char buf[30];
	 sprintf(buf, "set_rule_info(%s)", op);
	 log_snmp_err(buf, response);
         ms->write_OK = rc = 0;
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return rc;
   }

void quack() { }  /* Debugger breakpoint */

int TrState_util(struct meter_status *ms,
   int which, int *what)
{
   int i, count, status;
   struct snmp_pdu *pdu, *response;
   struct variable_list *vars;

   if (which == CSU_READ) {
      pdu = snmp_pdu_create(GET_REQ_MSG);
      ADD_VAR(o_trState);
      ADD_VAR(o_trTime);
      }
   else {
      pdu = snmp_pdu_create(SET_REQ_MSG);
      ADD_VAR(o_trState);
      vars = pdu->variables;
      SET_INT(*what);
      }

   status = snmp_synch_response(ms->ss, pdu, &response);
   if (status == STAT_SUCCESS) {
      if (response->errstat == SNMP_ERR_NOERROR) {
	 vars = response->variables;
         INT_VAL(*what);
         if (which == CSU_READ) {
            vars = vars->next_variable;
            INT_VAL(ms->trt);
            }
	 }
      else {
	 log_snmp_err("CrlState_util()", response);
         ms->write_OK = 0;
	 }
      snmp_free_pdu(response);
      }
   else return 0;  /* Error or timeout */
   mswait(ms->snmp_delay);
   return 1;
   }
