/*
 * Copyright (c) 2001 Mark Fullmer and The Ohio State University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      $Id: ftfil.c,v 1.20 2004/01/05 17:55:23 maf Exp $
 */

#include "ftconfig.h"
#include "ftlib.h"

#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <ctype.h>
#include <syslog.h>
#include <dirent.h>
#include <limits.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <zlib.h>

#if HAVE_STRINGS_H
 #include <strings.h>
#endif
#if HAVE_STRING_H
  #include <string.h>
#endif

#if !HAVE_STRSEP
  char    *strsep (char **, const char *);
#endif

#define PARSE_PRIMITIVE_TYPE_INIT(A)\
  if (!(A = malloc (sizeof *A))) {\
    fterr_warn("malloc()");\
    return -1;\
  }\
  bzero(A, sizeof *A);\
  A->init = 1;\
  A->default_mode = FT_FIL_MODE_DENY;\
  lp->cur_primitive->lookup = A;\

#define RADIX_TRIE_INIT\
  if (!rn_init_called) {\
    max_keylen = sizeof(struct radix_sockaddr_in);\
    rn_init();\
    rn_init_called = 1;\
  }\

extern int max_keylen;
extern u_int32 mask_lookup[];
static int rn_init_called;
static struct radix_node_head *rhead;


enum ftfil_op { FT_FIL_OP_UNSET, FT_FIL_OP_LT, FT_FIL_OP_GT,
                   FT_FIL_OP_EQ, FT_FIL_OP_NE, FT_FIL_OP_GE,
                   FT_FIL_OP_LE };
    
enum ftfil_parse_state { PARSE_STATE_UNSET, PARSE_STATE_PRIMITIVE,
                          PARSE_STATE_DEFINITION };
    

enum ftfil_def_match { FT_FIL_DEFINITION_MATCH_UNSET,
                               FT_FIL_DEFINITION_MATCH_SRC_AS,
                               FT_FIL_DEFINITION_MATCH_DST_AS,
                               FT_FIL_DEFINITION_MATCH_IP_SRC_ADDR,
                               FT_FIL_DEFINITION_MATCH_IP_DST_ADDR,
                               FT_FIL_DEFINITION_MATCH_IP_EXPORTER_ADDR,
                               FT_FIL_DEFINITION_MATCH_IP_NEXT_HOP_ADDR,
                               FT_FIL_DEFINITION_MATCH_IP_SC_ADDR,
                               FT_FIL_DEFINITION_MATCH_IP_PROTOCOL,
                               FT_FIL_DEFINITION_MATCH_IP_SRC_PREFIX_LEN,
                               FT_FIL_DEFINITION_MATCH_IP_DST_PREFIX_LEN,
                               FT_FIL_DEFINITION_MATCH_IP_TOS,
                               FT_FIL_DEFINITION_MATCH_IP_MARKED_TOS,
                               FT_FIL_DEFINITION_MATCH_IP_TCP_FLAGS,
                               FT_FIL_DEFINITION_MATCH_IP_SRC_PORT,
                               FT_FIL_DEFINITION_MATCH_IP_DST_PORT,
                               FT_FIL_DEFINITION_MATCH_INPUT_IF,
                               FT_FIL_DEFINITION_MATCH_OUTPUT_IF,
                               FT_FIL_DEFINITION_MATCH_START_TIME,
                               FT_FIL_DEFINITION_MATCH_END_TIME,
                               FT_FIL_DEFINITION_MATCH_FLOWS,
                               FT_FIL_DEFINITION_MATCH_OCTETS,
                               FT_FIL_DEFINITION_MATCH_PACKETS,
                               FT_FIL_DEFINITION_MATCH_XTRA_PACKETS,
                               FT_FIL_DEFINITION_MATCH_DURATION,
                               FT_FIL_DEFINITION_MATCH_ENGINE_ID,
                               FT_FIL_DEFINITION_MATCH_ENGINE_TYPE,
                               FT_FIL_DEFINITION_MATCH_SRC_TAG,
                               FT_FIL_DEFINITION_MATCH_DST_TAG,
                               FT_FIL_DEFINITION_MATCH_PPS,
                               FT_FIL_DEFINITION_MATCH_BPS,
                               FT_FIL_DEFINITION_MATCH_RANDOM_SAMPLE };

char *op_name_lookup[] = {"Unset", "lt", "gt", "eq", "ne", "ge", "le"};
char *mode_name_lookup[] = {"Unset", "permit", "deny"};


struct line_parser {
  enum ftfil_parse_state state;
  enum ftfil_primitive_type type;
  enum ftfil_mode mode;
  struct ftfil_primitive *cur_primitive;
  struct ftfil_def *cur_def;
  struct ftfil_match *cur_def_match;
  struct ftsym *sym_ip_prot;
  struct ftsym *sym_ip_tcp_port;
  struct ftsym *sym_asn;
  struct ftsym *sym_tag;
  struct ftsym *sym_cur;
  int lineno;
  char *buf, *fname, *word;
  
};

struct ftfil_lookup_ip_address {
  struct ftchash *ftch;
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_ip_mask {
  FT_STAILQ_HEAD(filipmhead, ftfil_lookup_ip_mask_rec) list;
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_ip_mask_rec {
  FT_STAILQ_ENTRY(ftfil_lookup_ip_mask_rec) chain;
  u_int32 ip, mask;
  int mode; /* FT_FIL_MODE_PERMIT/DENY */
};

struct ftfil_lookup_ip_prefix_rec {
  struct radix_node rt_nodes[2]; /* radix tree glue */
  struct radix_sockaddr_in addr;
  u_int8 masklen;
  int mode; /* FT_FIL_MODE_PERMIT/DENY */
};

struct ftfil_lookup_ip_prefix {
  struct radix_node_head *rhead;
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_as {
  u_int8 mode[65536];
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_ip_prot {
  u_int8 mode[256];
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_ip_port {
  u_int8 mode[65536];
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_ip_tos {
  u_int8 mask;
  u_int8 mode[256];
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_ip_tcp_flags {
  u_int8 mask;
  u_int8 mode[256];
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_if_index {
  u_int8 mode[65536];
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_engine {
  u_int8 mode[256];
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_ip_prefix_len {
  u_int8 mode[33];
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_counter_rec {
  FT_STAILQ_ENTRY(ftfil_lookup_counter_rec) chain;
  u_int32 val;
  enum ftfil_op op; /* FT_FIL_OP */
  int mode;
};

struct ftfil_lookup_counter {
  FT_STAILQ_HEAD(fillchead, ftfil_lookup_counter_rec) list;
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_double_rec {
  FT_STAILQ_ENTRY(ftfil_lookup_double_rec) chain;
  double val;
  enum ftfil_op op; /* FT_FIL_OP */
  int mode;
};

struct ftfil_lookup_double {
  FT_STAILQ_HEAD(filldhead, ftfil_lookup_double_rec) list;
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_tag {
  struct ftchash *ftch;
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_tag_mask_rec {
  FT_STAILQ_ENTRY(ftfil_lookup_tag_mask_rec) chain;
  u_int32 tag, mask;
  int mode; /* FT_FIL_MODE_PERMIT/DENY */
};

struct ftfil_lookup_tag_mask {
  FT_STAILQ_HEAD(filtmhead, ftfil_lookup_tag_mask_rec) list;
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_time_rec {
  FT_STAILQ_ENTRY(ftfil_lookup_time_rec) chain;
  int hour, min, sec;
  enum ftfil_op op; /* FT_FIL_OP */
  int mode;
};

struct ftfil_lookup_time {
  FT_STAILQ_HEAD(filltmehead, ftfil_lookup_time_rec) list;
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_lookup_rate {
  int rate;
  enum ftfil_mode mode; /* FT_FIL_MODE_PERMIT/DENY */
  enum ftfil_mode default_mode; /* FT_FIL_MODE_PERMIT/DENY */
  int init; /* initialized? */
};

struct ftfil_match_item_cache {
  FT_SLIST_ENTRY(ftfil_match_item_cache) chain;
  u_int32 time; /* cache time -- only valid for flow == curflow+1 */
  void *flow; /* address of flow evaluating -- used to invalidate cache */
  enum ftfil_mode mode; /* result FT_FIL_MODE_* */
  void *lookup; /* data for evaluator */
  int (*eval)(void *lookup, void *rec, struct fts3rec_offsets *fo);
};

static int walk_free(struct radix_node *rn, struct walkarg *UNUSED);

static int ftfil_load_lookup(struct line_parser *lp, char *s, int size,
  char *list, int mode);

static int parse_definition(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_definition_match(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_definition_or(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_definition_invert(struct line_parser *lp,
  struct ftfil *ftfil);

static int parse_primitive(struct line_parser *lp, struct ftfil *ftfil);
static int parse_primitive_type(struct line_parser *lp, struct ftfil *ftfil);
static int parse_primitive_deny(struct line_parser *lp, struct ftfil *ftfil);
static int parse_primitive_permit(struct line_parser *lp, struct ftfil *ftfil);
static int parse2_primitive_permitdeny(struct line_parser *lp,
  struct ftfil *ftfil, int flag);
static int parse_primitive_default(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_mask(struct line_parser *lp, struct ftfil *ftfil);

static int parse_primitive_type_asn(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_ip_prot(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_ip_port(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_ip_prefix_len(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_if_index(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_ip_tos(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_ip_tcp_flags(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_engine(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_ip_address(struct line_parser *lp,
  struct ftfil *ftfil);
int parse_primitive_type_ip_mask(struct line_parser *lp,
  struct ftfil *ftfil);
int parse_primitive_type_ip_prefix(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_tag(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_tag_mask(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_counter(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_time_date(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_time(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_double(struct line_parser *lp,
  struct ftfil *ftfil);
static int parse_primitive_type_rate(struct line_parser *lp,
  struct ftfil *ftfil);

inline int eval_match_src_as(struct ftfil_lookup_as *lookup, char *rec,
  struct fts3rec_offsets *fo);
inline int eval_match_dst_as(struct ftfil_lookup_as *lookup, char *rec,
  struct fts3rec_offsets *fo);
inline int eval_match_engine_type(struct ftfil_lookup_engine *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_engine_id(struct ftfil_lookup_engine *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_dst_if_index(struct ftfil_lookup_if_index *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_src_if_index(struct ftfil_lookup_if_index *lookup,
  char *rec, struct fts3rec_offsets *fo); 
inline int eval_match_ip_dst_port(struct ftfil_lookup_ip_port *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_src_port(struct ftfil_lookup_ip_port *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_tcp_flags(struct ftfil_lookup_ip_tcp_flags *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_marked_tos(struct ftfil_lookup_ip_tos *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_tos(struct ftfil_lookup_ip_tos *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_dst_prefix_len(struct ftfil_lookup_ip_prefix_len
  *lookup, char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_src_prefix_len(struct ftfil_lookup_ip_prefix_len
  *lookup, char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_prot(struct ftfil_lookup_ip_prot *lookup, char *rec,
  struct fts3rec_offsets *fo);
inline int eval_match_flows(struct ftfil_lookup_counter *lookup, char *rec,
  struct fts3rec_offsets *fo);
inline int eval_match_octets(struct ftfil_lookup_counter *lookup, char *rec,
  struct fts3rec_offsets *fo);
inline int eval_match_packets(struct ftfil_lookup_counter *lookup, char *rec,
  struct fts3rec_offsets *fo);
inline int eval_match_xtra_packets(struct ftfil_lookup_counter *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_duration(struct ftfil_lookup_counter *lookup, char *rec,
  struct fts3rec_offsets *fo);

inline int eval_match_start_time_date(struct ftfil_lookup_counter *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_end_time_date(struct ftfil_lookup_counter *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_start_time(struct ftfil_lookup_time *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_end_time(struct ftfil_lookup_time *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_src_tag_l(struct ftfil_lookup_tag_mask *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_src_tag_h(struct ftfil_lookup_tag *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_dst_tag_l(struct ftfil_lookup_tag_mask *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_dst_tag_h(struct ftfil_lookup_tag *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_ip_sc_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_sc_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_sc_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_ip_nexthop_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_nexthop_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_nexthop_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_ip_src_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_src_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_src_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_ip_dst_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_dst_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_dst_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_ip_exporter_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_exporter_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_ip_exporter_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_pps(struct ftfil_lookup_double *lookup,
  char *rec, struct fts3rec_offsets *fo);
inline int eval_match_bps(struct ftfil_lookup_double *lookup,
  char *rec, struct fts3rec_offsets *fo);

inline int eval_match_random_sample(struct ftfil_lookup_rate *lookup,
  char *rec, struct fts3rec_offsets *fo);

static int resolve_primitives(struct ftfil *ftfil);

#define NEXT_WORD(A,B)\
  for (;;) {\
    B = strsep(A, " \t");\
    if ((B && *B != 0) || (!B))\
      break;\
  }\
 
struct jump {
  char *name;
  enum ftfil_parse_state state;
  int (*func)(struct line_parser *lp, struct ftfil *ftfil);
};

static struct jump pjump[] = {
          {"filter-primitive", 0, parse_primitive},
          {"type", PARSE_STATE_PRIMITIVE, parse_primitive_type},
          {"permit", PARSE_STATE_PRIMITIVE, parse_primitive_permit},
          {"deny", PARSE_STATE_PRIMITIVE, parse_primitive_deny},
          {"default", PARSE_STATE_PRIMITIVE, parse_primitive_default},
          {"mask", PARSE_STATE_PRIMITIVE, parse_primitive_mask},
          {"filter-definition", 0, parse_definition},
          {"match", PARSE_STATE_DEFINITION, parse_definition_match},
          {"or", PARSE_STATE_DEFINITION, parse_definition_or},
          {"invert", PARSE_STATE_DEFINITION, parse_definition_invert},
          {0, 0, 0},
          };
/*
 * data structures:
 *
 * Each primitive is stored in a linked list of struct ftfil_primitive.  The
 * head is ftfil.primitives.  Each primitive has a lookup entry which 
 * points to a struct ftfil_lookup_* based on the enum ftfil_primitive_type
 * stored as type.  Some lookup entries allocate further storage (ie
 * a hash, radix tree, or linked list for the linear lookups).
 *
 * Each definition is stored in a linked list of struct ftfil_def.
 * The head is ftfil.defs.  Each definition has a list of
 * ftfil_match matches.  The matches have a list of ftfil_match_items.
 * For a match to be satisfied each each item in the match must evaluate
 * true (AND).  For a definition to be satisfied (permitted) one of the
 * entries in the match list must be satisfied (OR).
 *
 * Each primitive (enum ftfil_primitive_type) has an associated
 * parse_primitive_type_xxx().
 *
 * Each match type (enum ftfil_def_match) has an associated
 * eval_match_xxx().
 *
 * ftfil_eval() walks the matches and the match items for the definition
 * passed to it looking for a match.  If every match item in a match
 * evaluated to permit (enum ftfil_mode) a permit is returned, else
 * a deny.
 *
 * The current evaluator code does not cache results.  For example
 *
 * match src-ip-addr test1
 * match ip-protocol test2
 * or
 * match src-ip-addr test1
 * match ip-port test3
 *
 * If the first two matches fail the result from "match src-ip-addr test1"
 * will need to be calculated a second time.  In practice this doesn't
 * seem to be a problem -- the performance impact is minimal for real
 * world definitions.  Adding a cache would most likely reduce performance
 * for most cases due to overhead of the cache test/update code.
 *
 * Also note that in the above example if the first two matches pass
 * the the next two will not be evaluated at all - short circuit.
 */

/*
 *************************************************************************
                                eval_*
 *************************************************************************
 */

/*
 * function: eval_match_src_as
 *
 * Evalute src_as
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_src_as(struct ftfil_lookup_as *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  u_int16 *src_as;
  int val;

  src_as = ((u_int16*)(rec+fo->src_as));

  val = lookup->mode[*src_as];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_src_as */

/*
 * function: eval_match_dst_as
 *
 * Evalute dst_as
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_dst_as(struct ftfil_lookup_as *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  u_int16 *dst_as;
  int val;

  dst_as = ((u_int16*)(rec+fo->dst_as));

  val = lookup->mode[*dst_as];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_dst_as */


/*
 * function: eval_match_ip_prot
 *
 * Evalute ip_prot
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_prot(struct ftfil_lookup_ip_prot *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  u_int8 *ip_prot;
  int val;

  ip_prot = ((u_int8*)(rec+fo->prot));

  val = lookup->mode[*ip_prot];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_ip_prot */


/*
 * function: eval_match_ip_src_prefix_len
 *
 * Evalute ip_src_prefix_len
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_src_prefix_len(struct ftfil_lookup_ip_prefix_len *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int8 *src_mask;
  int val;

  src_mask = ((u_int8*)(rec+fo->src_mask));

  val = lookup->mode[*src_mask];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_ip_src_prefix_len */


/*
 * function: eval_match_ip_dst_prefix_len
 *
 * Evalute ip_dst_prefix_len
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_dst_prefix_len(struct ftfil_lookup_ip_prefix_len *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int8 *dst_mask;
  int val;

  dst_mask = ((u_int8*)(rec+fo->dst_mask));

  val = lookup->mode[*dst_mask];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_ip_dst_prefix_len */


/*
 * function: eval_match_ip_tos
 *
 * Evalute ip_tos
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_tos(struct ftfil_lookup_ip_tos *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int8 tos;
  int val;

  tos = *((u_int8*)(rec+fo->tos));
  tos &= lookup->mask;

  val = lookup->mode[tos];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_ip_tos */

/*
 * function: eval_match_marked_ip_tos
 *
 * Evalute marked_ip_tos
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_marked_tos(struct ftfil_lookup_ip_tos *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int8 marked_tos;
  int val;

  marked_tos = *((u_int8*)(rec+fo->marked_tos));
  marked_tos &= lookup->mask;

  val = lookup->mode[marked_tos];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_ip_marked_tos */


/*
 * function: eval_match_ip_tcp_flags
 *
 * Evalute ip_tcp_flags
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_tcp_flags(struct ftfil_lookup_ip_tcp_flags *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int8 tcp_flags;
  int val;

  tcp_flags = *((u_int8*)(rec+fo->tcp_flags));
  tcp_flags &= lookup->mask;

  val = lookup->mode[tcp_flags];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_ip_tcp_flags */


/*
 * function: eval_match_ip_src_port
 *
 * Evalute ip_src_port
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_src_port(struct ftfil_lookup_ip_port *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int16 *src_port;
  int val;

  src_port = ((u_int16*)(rec+fo->srcport));

  val = lookup->mode[*src_port];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_ip_src_port */

/*
 * function: eval_match_ip_dst_port
 *
 * Evalute ip_dst_port
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_dst_port(struct ftfil_lookup_ip_port *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int16 *dst_port;
  int val;

  dst_port = ((u_int16*)(rec+fo->dstport));

  val = lookup->mode[*dst_port];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_ip_dst_port */


/*
 * function: eval_match_src_if_index
 *
 * Evalute src_if_index
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_src_if_index(struct ftfil_lookup_if_index *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int16 *src_if_index;
  int val;

  src_if_index = ((u_int16*)(rec+fo->input));

  val = lookup->mode[*src_if_index];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_src_if_index */

/*
 * function: eval_match_dst_if_index
 *
 * Evalute dst_if_index
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_dst_if_index(struct ftfil_lookup_if_index *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int16 *dst_if_index;
  int val;

  dst_if_index = ((u_int16*)(rec+fo->output));

  val = lookup->mode[*dst_if_index];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_dst_if_index */


/*
 * function: eval_match_engine_id
 *
 * Evalute engine_id
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_engine_id(struct ftfil_lookup_engine *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int8 *engine_id;
  int val;

  engine_id = ((u_int8*)(rec+fo->engine_id));

  val = lookup->mode[*engine_id];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_engine_id */


/*
 * function: eval_match_engine_type
 *
 * Evalute engine_type
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_engine_type(struct ftfil_lookup_engine *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  u_int8 *engine_type;
  int val;

  engine_type = ((u_int8*)(rec+fo->engine_type));

  val = lookup->mode[*engine_type];

  if (val == FT_FIL_MODE_PERMIT)
    return FT_FIL_MODE_PERMIT;
  else if (val == FT_FIL_MODE_DENY)
    return FT_FIL_MODE_DENY;
  else
    return lookup->default_mode;

} /* eval_match_engine_type */

/*
 * function: eval_match_flows
 *
 * Evalute flows
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_flows(struct ftfil_lookup_counter *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_counter_rec *ftflcr;
  u_int32 *flows;
  int t, match;

  flows = ((u_int32*)(rec+fo->dFlows));

  match = 0;

  FT_STAILQ_FOREACH(ftflcr, &lookup->list, chain) {

    switch (ftflcr->op) {

      case FT_FIL_OP_LT:
        t = (*flows < ftflcr->val);
        break;
      case FT_FIL_OP_GT:
        t = (*flows > ftflcr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (*flows == ftflcr->val);
        break;
      case FT_FIL_OP_NE:
        t = (*flows != ftflcr->val);
        break;
      case FT_FIL_OP_GE:
        t = (*flows >= ftflcr->val);
        break;
      case FT_FIL_OP_LE:
        t = (*flows <= ftflcr->val);
        break;
      default:
        fterr_warnx("eval_match_flows: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflcr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_flows */

/*
 * function: eval_match_octets
 *
 * Evalute octets
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_octets(struct ftfil_lookup_counter *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_counter_rec *ftflcr;
  u_int32 *octets;
  int t, match;

  octets = ((u_int32*)(rec+fo->dOctets));

  match = 0;

  FT_STAILQ_FOREACH(ftflcr, &lookup->list, chain) {

    switch (ftflcr->op) {

      case FT_FIL_OP_LT:
        t = (*octets < ftflcr->val);
        break;
      case FT_FIL_OP_GT:
        t = (*octets > ftflcr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (*octets == ftflcr->val);
        break;
      case FT_FIL_OP_NE:
        t = (*octets != ftflcr->val);
        break;
      case FT_FIL_OP_GE:
        t = (*octets >= ftflcr->val);
        break;
      case FT_FIL_OP_LE:
        t = (*octets <= ftflcr->val);
        break;
      default:
        fterr_warnx("eval_match_octets: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflcr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_octets */

/*
 * function: eval_match_packets
 *
 * Evalute packets
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_packets(struct ftfil_lookup_counter *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_counter_rec *ftflcr;
  u_int32 *packets;
  int t, match;

  packets = ((u_int32*)(rec+fo->dPkts));

  match = 0;

  FT_STAILQ_FOREACH(ftflcr, &lookup->list, chain) {

    switch (ftflcr->op) {

      case FT_FIL_OP_LT:
        t = (*packets < ftflcr->val);
        break;
      case FT_FIL_OP_GT:
        t = (*packets > ftflcr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (*packets == ftflcr->val);
        break;
      case FT_FIL_OP_NE:
        t = (*packets != ftflcr->val);
        break;
      case FT_FIL_OP_GE:
        t = (*packets >= ftflcr->val);
        break;
      case FT_FIL_OP_LE:
        t = (*packets <= ftflcr->val);
        break;
      default:
        fterr_warnx("eval_match_packets: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflcr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_packets */

/*
 * function: eval_match_xtra_packets
 *
 * Evalute xtra_packets
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_xtra_packets(struct ftfil_lookup_counter *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_counter_rec *ftflcr;
  u_int32 *xtra_packets;
  int t, match;

  xtra_packets = ((u_int32*)(rec+fo->extra_pkts));

  match = 0;

  FT_STAILQ_FOREACH(ftflcr, &lookup->list, chain) {

    switch (ftflcr->op) {

      case FT_FIL_OP_LT:
        t = (*xtra_packets < ftflcr->val);
        break;
      case FT_FIL_OP_GT:
        t = (*xtra_packets > ftflcr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (*xtra_packets == ftflcr->val);
        break;
      case FT_FIL_OP_NE:
        t = (*xtra_packets != ftflcr->val);
        break;
      case FT_FIL_OP_GE:
        t = (*xtra_packets >= ftflcr->val);
        break;
      case FT_FIL_OP_LE:
        t = (*xtra_packets <= ftflcr->val);
        break;
      default:
        fterr_warnx("eval_match_xtra_packets: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflcr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_xtra_packets */

/*
 * function: eval_match_duration
 *
 * Evalute duration
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_duration(struct ftfil_lookup_counter *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_counter_rec *ftflcr;
  u_int32 duration, *first, *last;
  int t, match;

  first = ((u_int32*)(rec+fo->First));
  last = ((u_int32*)(rec+fo->Last));
  duration = *last - *first;

  match = 0;

  FT_STAILQ_FOREACH(ftflcr, &lookup->list, chain) {

    switch (ftflcr->op) {

      case FT_FIL_OP_LT:
        t = (duration < ftflcr->val);
        break;
      case FT_FIL_OP_GT:
        t = (duration > ftflcr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (duration == ftflcr->val);
        break;
      case FT_FIL_OP_NE:
        t = (duration != ftflcr->val);
        break;
      case FT_FIL_OP_GE:
        t = (duration >= ftflcr->val);
        break;
      case FT_FIL_OP_LE:
        t = (duration <= ftflcr->val);
        break;
      default:
        fterr_warnx("eval_match_duration: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflcr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_duration */

/*
 * function: eval_match_start_time_date
 *
 * Evalute start_time_date
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_start_time_date(struct ftfil_lookup_counter *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_counter_rec *ftflcr;
  struct fttime ftt;
  u_int32 *sysUpTime, *unix_secs, *unix_nsecs, *First;
  int t, match;

  sysUpTime = ((u_int32*)(rec+fo->sysUpTime));
  unix_secs = ((u_int32*)(rec+fo->unix_secs));
  unix_nsecs = ((u_int32*)(rec+fo->unix_nsecs));
  First = ((u_int32*)(rec+fo->First));

  ftt = ftltime(*sysUpTime, *unix_secs, *unix_nsecs, *First);

  match = 0;

  FT_STAILQ_FOREACH(ftflcr, &lookup->list, chain) {

    switch (ftflcr->op) {

      case FT_FIL_OP_LT:
        t = (ftt.secs < ftflcr->val);
        break;
      case FT_FIL_OP_GT:
        t = (ftt.secs > ftflcr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (ftt.secs == ftflcr->val);
        break;
      case FT_FIL_OP_NE:
        t = (ftt.secs != ftflcr->val);
        break;
      case FT_FIL_OP_GE:
        t = (ftt.secs >= ftflcr->val);
        break;
      case FT_FIL_OP_LE:
        t = (ftt.secs <= ftflcr->val);
        break;
      default:
        fterr_warnx("eval_match_start_time_date: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflcr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_start_time_date */

/*
 * function: eval_match_end_time_date
 *
 * Evalute end_time_date
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_end_time_date(struct ftfil_lookup_counter *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_counter_rec *ftflcr;
  struct fttime ftt;
  u_int32 *sysUpTime, *unix_secs, *unix_nsecs, *Last;
  int t, match;

  sysUpTime = ((u_int32*)(rec+fo->sysUpTime));
  unix_secs = ((u_int32*)(rec+fo->unix_secs));
  unix_nsecs = ((u_int32*)(rec+fo->unix_nsecs));
  Last = ((u_int32*)(rec+fo->Last));

  ftt = ftltime(*sysUpTime, *unix_secs, *unix_nsecs, *Last);

  match = 0;

  FT_STAILQ_FOREACH(ftflcr, &lookup->list, chain) {

    switch (ftflcr->op) {

      case FT_FIL_OP_LT:
        t = (ftt.secs < ftflcr->val);
        break;
      case FT_FIL_OP_GT:
        t = (ftt.secs > ftflcr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (ftt.secs == ftflcr->val);
        break;
      case FT_FIL_OP_NE:
        t = (ftt.secs != ftflcr->val);
        break;
      case FT_FIL_OP_GE:
        t = (ftt.secs >= ftflcr->val);
        break;
      case FT_FIL_OP_LE:
        t = (ftt.secs <= ftflcr->val);
        break;
      default:
        fterr_warnx("eval_match_end_time_date: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflcr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_end_time_date */

/*
 * function: eval_match_start_time
 *
 * Evalute start_time
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_start_time(struct ftfil_lookup_time *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  time_t t1, t2;
  struct tm *tm;
  struct ftfil_lookup_time_rec *ftfltmer;
  struct fttime ftt;
  u_int32 *sysUpTime, *unix_secs, *unix_nsecs, *First;
  int t, match;

  sysUpTime = ((u_int32*)(rec+fo->sysUpTime));
  unix_secs = ((u_int32*)(rec+fo->unix_secs));
  unix_nsecs = ((u_int32*)(rec+fo->unix_nsecs));
  First = ((u_int32*)(rec+fo->First));

  ftt = ftltime(*sysUpTime, *unix_secs, *unix_nsecs, *First);

  t1 = ftt.secs;

  /* tm is now "today" for the flow */
  tm = localtime(&t1);
  tm->tm_hour = 0;
  tm->tm_min = 0;
  tm->tm_sec = 0;

  match = 0;

  FT_STAILQ_FOREACH(ftfltmer, &lookup->list, chain) {

    /*
     * find where the hh:mm:ss for this filter falls relative to day start,
     * store as t2
     */
    tm->tm_hour = ftfltmer->hour;
    tm->tm_min = ftfltmer->min;
    tm->tm_sec = ftfltmer->sec;
    t2 = mktime(tm);

    switch (ftfltmer->op) {

      case FT_FIL_OP_LT:
        t = (t1 < t2);
        break;
      case FT_FIL_OP_GT:
        t = (t1 > t2);
        break;
      case FT_FIL_OP_EQ:
        t = (t1 == t2);
        break;
      case FT_FIL_OP_NE:
        t = (t1 != t2);
        break;
      case FT_FIL_OP_GE:
        t = (t1 >= t2);
        break;
      case FT_FIL_OP_LE:
        t = (t1 <= t2);
        break;
      default:
        fterr_warnx("eval_match_start_time: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftfltmer */

  /* if there was a match, then return that mode */
  if (match)
    return ftfltmer->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_start_time */

/*
 * function: eval_match_end_time
 *
 * Evalute end_time
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_end_time(struct ftfil_lookup_time *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  time_t t1, t2;
  struct tm *tm;
  struct ftfil_lookup_time_rec *ftfltmer;
  struct fttime ftt;
  u_int32 *sysUpTime, *unix_secs, *unix_nsecs, *Last;
  int t, match;

  sysUpTime = ((u_int32*)(rec+fo->sysUpTime));
  unix_secs = ((u_int32*)(rec+fo->unix_secs));
  unix_nsecs = ((u_int32*)(rec+fo->unix_nsecs));
  Last = ((u_int32*)(rec+fo->Last));

  ftt = ftltime(*sysUpTime, *unix_secs, *unix_nsecs, *Last);

  t1 = ftt.secs;

  /* tm is now "today" for the flow */
  tm = localtime(&t1);
  tm->tm_hour = 0;
  tm->tm_min = 0;
  tm->tm_sec = 0;

  match = 0;

  FT_STAILQ_FOREACH(ftfltmer, &lookup->list, chain) {

    /*
     * find where the hh:mm:ss for this filter falls relative to day start,
     * store as t2
     */
    tm->tm_hour = ftfltmer->hour;
    tm->tm_min = ftfltmer->min;
    tm->tm_sec = ftfltmer->sec;
    t2 = mktime(tm);

    switch (ftfltmer->op) {

      case FT_FIL_OP_LT:
        t = (t1 < t2);
        break;
      case FT_FIL_OP_GT:
        t = (t1 > t2);
        break;
      case FT_FIL_OP_EQ:
        t = (t1 == t2);
        break;
      case FT_FIL_OP_NE:
        t = (t1 != t2);
        break;
      case FT_FIL_OP_GE:
        t = (t1 >= t2);
        break;
      case FT_FIL_OP_LE:
        t = (t1 <= t2);
        break;
      default:
        fterr_warnx("eval_match_end_time: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftfltmer */

  /* if there was a match, then return that mode */
  if (match)
    return ftfltmer->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_end_time */

/*
 * function: eval_match_src_tag_l
 *
 * Evalute src_tag as list
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_src_tag_l(struct ftfil_lookup_tag_mask *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_tag_mask_rec *ftfltmr;
  u_int32 *src_tag;
  int match;

  src_tag = ((u_int32*)(rec+fo->src_tag));

  match = 0;

  FT_STAILQ_FOREACH(ftfltmr, &lookup->list, chain) {

    /* match? */
    if ((*src_tag & ftfltmr->mask) == ftfltmr->tag) {
      match = 1;
      break;
    }

  } /* ftfltmr */

  /* if there was a match, then return that mode */
  if (match)
    return ftfltmr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_src_tag_l */

/*
 * function: eval_match_src_tag_h
 *
 * Evalute src_tag as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_src_tag_h(struct ftfil_lookup_tag *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftchash_rec_fil_c32 *ftch_recfc32p;
  u_int32 *src_tag, hash;
  int match;

  src_tag = ((u_int32*)(rec+fo->src_tag));

  match = 0;

  hash = (*src_tag>>16) ^ (*src_tag & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(lookup->ftch, src_tag, hash)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftch_recfc32p->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_src_tag_h */

/*
 * function: eval_match_dst_tag_h
 *
 * Evalute dst_tag as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_dst_tag_h(struct ftfil_lookup_tag *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftchash_rec_fil_c32 *ftch_recfc32p;
  u_int32 *dst_tag, hash;
  int match;

  dst_tag = ((u_int32*)(rec+fo->dst_tag));

  match = 0;

  hash = (*dst_tag>>16) ^ (*dst_tag & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(lookup->ftch, dst_tag, hash)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftch_recfc32p->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_dst_tag_h */

/*
 * function: eval_match_dst_tag_l
 *
 * Evalute dst_tag as list
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_dst_tag_l(struct ftfil_lookup_tag_mask *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_tag_mask_rec *ftfltmr;
  u_int32 *dst_tag;
  int match;

  dst_tag = ((u_int32*)(rec+fo->dst_tag));

  match = 0;

  FT_STAILQ_FOREACH(ftfltmr, &lookup->list, chain) {

    /* match? */
    if ((*dst_tag & ftfltmr->mask) == ftfltmr->tag) {
      match = 1;
      break;
    }

  } /* ftfltmr */

  /* if there was a match, then return that mode */
  if (match)
    return ftfltmr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_dst_tag_l */

/*
 * function: eval_match_nexthop_l
 *
 * Evalute nexthop as list
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_nexthop_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_mask_rec *ftflipmr;
  u_int32 *nexthop;
  int match;

  nexthop = ((u_int32*)(rec+fo->nexthop));

  match = 0;

  FT_STAILQ_FOREACH(ftflipmr, &lookup->list, chain) {

    /* match? */
    if ((*nexthop & ftflipmr->mask) == ftflipmr->ip) {
      match = 1;
      break;
    }

  } /* ftflipmr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflipmr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_nexthop_addr_l */

/*
 * function: eval_match_ip_nexthop_addr_h
 *
 * Evalute nexthop as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_nexthop_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftchash_rec_fil_c32 *ftch_recfc32p;
  u_int32 *nexthop, hash;
  int match;

  nexthop = ((u_int32*)(rec+fo->nexthop));

  match = 0;

  hash = (*nexthop>>16) ^ (*nexthop & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(lookup->ftch, nexthop, hash)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftch_recfc32p->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_nexthop_addr_h */

/*
 * function: eval_match_ip_nexthop_addr_r
 *
 * Evalute nexthop as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_nexthop_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_prefix_rec *ftflipprr;
  struct radix_node_head *rhead;
  struct radix_sockaddr_in sock1;
  int match;

  sock1.sin_addr.s_addr = *((u_int32*)(rec+fo->nexthop));
  sock1.sin_len = sizeof sock1;
  sock1.sin_family = AF_INET;

  match = 0;

  rhead = lookup->rhead;

  if ((ftflipprr = (struct ftfil_lookup_ip_prefix_rec*)rhead->rnh_matchaddr(
    &sock1, rhead)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftflipprr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_nexthop_addr_r */

/*
 * function: eval_match_ip_sc_addr_l
 *
 * Evalute sc as list
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_sc_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_mask_rec *ftflipmr;
  u_int32 *sc;
  int match;

  sc = ((u_int32*)(rec+fo->router_sc));

  match = 0;

  FT_STAILQ_FOREACH(ftflipmr, &lookup->list, chain) {

    /* match? */
    if ((*sc & ftflipmr->mask) == ftflipmr->ip) {
      match = 1;
      break;
    }

  } /* ftflipmr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflipmr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_sc_addr_l */

/*
 * function: eval_match_ip_sc_addr_h
 *
 * Evalute sc as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_sc_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftchash_rec_fil_c32 *ftch_recfc32p;
  u_int32 *sc, hash;
  int match;

  sc = ((u_int32*)(rec+fo->router_sc));

  match = 0;

  hash = (*sc>>16) ^ (*sc & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(lookup->ftch, sc, hash)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftch_recfc32p->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_sc_addr_h */

/*
 * function: eval_match_ip_sc_addr_r
 *
 * Evalute sc as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_sc_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_prefix_rec *ftflipprr;
  struct radix_node_head *rhead;
  struct radix_sockaddr_in sock1;
  int match;

  sock1.sin_addr.s_addr = *((u_int32*)(rec+fo->router_sc));
  sock1.sin_len = sizeof sock1;
  sock1.sin_family = AF_INET;

  match = 0;

  rhead = lookup->rhead;

  if ((ftflipprr = (struct ftfil_lookup_ip_prefix_rec*)rhead->rnh_matchaddr(
    &sock1, rhead)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftflipprr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_sc_addr_r */

/*
 * function: eval_match_ip_src_addr_l
 *
 * Evalute ip_src_addr as list
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_src_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_mask_rec *ftflipmr;
  u_int32 *ip_src_addr;
  int match;

  ip_src_addr = ((u_int32*)(rec+fo->srcaddr));

  match = 0;

  FT_STAILQ_FOREACH(ftflipmr, &lookup->list, chain) {

    /* match? */
    if ((*ip_src_addr & ftflipmr->mask) == ftflipmr->ip) {
      match = 1;
      break;
    }

  } /* ftflipmr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflipmr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_src_addr_l */

/*
 * function: eval_match_ip_src_addr_h
 *
 * Evalute ip_src_addr as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_src_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftchash_rec_fil_c32 *ftch_recfc32p;
  u_int32 *ip_src_addr, hash;
  int match;

  ip_src_addr = ((u_int32*)(rec+fo->srcaddr));

  match = 0;

  hash = (*ip_src_addr>>16) ^ (*ip_src_addr & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(lookup->ftch, ip_src_addr, hash)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftch_recfc32p->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_src_addr_h */

/*
 * function: eval_match_ip_src_addr_r
 *
 * Evalute ip_src_addr as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_src_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_prefix_rec *ftflipprr;
  struct radix_node_head *rhead;
  struct radix_sockaddr_in sock1;
  int match;

  sock1.sin_addr.s_addr = *((u_int32*)(rec+fo->srcaddr));
  sock1.sin_len = sizeof sock1;
  sock1.sin_family = AF_INET;

  match = 0;

  rhead = lookup->rhead;

  if ((ftflipprr = (struct ftfil_lookup_ip_prefix_rec*)rhead->rnh_matchaddr(
    &sock1, rhead)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftflipprr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_src_addr_r */

/*
 * function: eval_match_ip_dst_addr_l
 *
 * Evalute ip_dst_addr as list
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_dst_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_mask_rec *ftflipmr;
  u_int32 *ip_dst_addr;
  int match;

  ip_dst_addr = ((u_int32*)(rec+fo->dstaddr));

  match = 0;

  FT_STAILQ_FOREACH(ftflipmr, &lookup->list, chain) {

    /* match? */
    if ((*ip_dst_addr & ftflipmr->mask) == ftflipmr->ip) {
      match = 1;
      break;
    }

  } /* ftflipmr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflipmr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_dst_addr_l */

/*
 * function: eval_match_ip_dst_addr_h
 *
 * Evalute ip_dst_addr as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_dst_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftchash_rec_fil_c32 *ftch_recfc32p;
  u_int32 *ip_dst_addr, hash;
  int match;

  ip_dst_addr = ((u_int32*)(rec+fo->dstaddr));

  match = 0;

  hash = (*ip_dst_addr>>16) ^ (*ip_dst_addr & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(lookup->ftch, ip_dst_addr, hash)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftch_recfc32p->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_dst_addr_h */

/*
 * function: eval_match_ip_dst_addr_r
 *
 * Evalute ip_dst_addr as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_dst_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_prefix_rec *ftflipprr;
  struct radix_node_head *rhead;
  struct radix_sockaddr_in sock1;
  int match;

  sock1.sin_addr.s_addr = *((u_int32*)(rec+fo->dstaddr));
  sock1.sin_len = sizeof sock1;
  sock1.sin_family = AF_INET;

  match = 0;

  rhead = lookup->rhead;

  if ((ftflipprr = (struct ftfil_lookup_ip_prefix_rec*)rhead->rnh_matchaddr(
    &sock1, rhead)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftflipprr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_dst_addr_r */

/*
 * function: eval_match_ip_exporter_addr_l
 *
 * Evalute ip_exporter_addr as list
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_exporter_addr_l(struct ftfil_lookup_ip_mask *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_mask_rec *ftflipmr;
  u_int32 *ip_exporter_addr;
  int match;

  ip_exporter_addr = ((u_int32*)(rec+fo->exaddr));

  match = 0;

  FT_STAILQ_FOREACH(ftflipmr, &lookup->list, chain) {

    /* match? */
    if ((*ip_exporter_addr & ftflipmr->mask) == ftflipmr->ip) {
      match = 1;
      break;
    }

  } /* ftflipmr */

  /* if there was a match, then return that mode */
  if (match)
    return ftflipmr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_exporter_addr_l */

/*
 * function: eval_match_ip_exporter_addr_h
 *
 * Evalute ip_exporter_addr as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_exporter_addr_h(struct ftfil_lookup_ip_address *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftchash_rec_fil_c32 *ftch_recfc32p;
  u_int32 *ip_exporter_addr, hash;
  int match;

  ip_exporter_addr = ((u_int32*)(rec+fo->exaddr));

  match = 0;

  hash = (*ip_exporter_addr>>16) ^ (*ip_exporter_addr & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(lookup->ftch, ip_exporter_addr, hash)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftch_recfc32p->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_exporter_addr_h */

/*
 * function: eval_match_ip_exporter_addr_r
 *
 * Evalute ip_exporter_addr as hash
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_ip_exporter_addr_r(struct ftfil_lookup_ip_prefix *lookup,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_ip_prefix_rec *ftflipprr;
  struct radix_node_head *rhead;
  struct radix_sockaddr_in sock1;
  int match;

  sock1.sin_addr.s_addr = *((u_int32*)(rec+fo->exaddr));
  sock1.sin_len = sizeof sock1;
  sock1.sin_family = AF_INET;

  match = 0;

  rhead = lookup->rhead;

  if ((ftflipprr = (struct ftfil_lookup_ip_prefix_rec*)rhead->rnh_matchaddr(
    &sock1, rhead)))
    match = 1;

  /* if there was a match, then return that mode */
  if (match)
    return ftflipprr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_ip_exporter_addr_r */

/*
 * function: eval_match_bps
 *
 * Evalute Bits Per Second
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_bps(struct ftfil_lookup_double *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_double_rec *ftfldr;
  double bps;
  u_int32 dOctets, Last, First, duration;
  int t, match;

  dOctets = *((u_int32*)(rec+fo->dOctets));
  Last = *((u_int32*)(rec+fo->Last));
  First = *((u_int32*)(rec+fo->First));
  duration = Last - First;

  if (duration)
    bps = (double)dOctets*8 / ((double)duration / 1000.0);
  else
    bps = 0;

  match = 0;

  FT_STAILQ_FOREACH(ftfldr, &lookup->list, chain) {

    switch (ftfldr->op) {

      case FT_FIL_OP_LT:
        t = (bps < ftfldr->val);
        break;
      case FT_FIL_OP_GT:
        t = (bps > ftfldr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (bps == ftfldr->val);
        break;
      case FT_FIL_OP_NE:
        t = (bps != ftfldr->val);
        break;
      case FT_FIL_OP_GE:
        t = (bps >= ftfldr->val);
        break;
      case FT_FIL_OP_LE:
        t = (bps <= ftfldr->val);
        break;
      default:
        fterr_warnx("eval_match_flows: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftfldr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_bps */

/*
 * function: eval_match_pps
 *
 * Evalute Packets Per Second
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_pps(struct ftfil_lookup_double *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  struct ftfil_lookup_double_rec *ftfldr;
  double pps;
  u_int32 dPkts, Last, First, duration;
  int t, match;

  dPkts = *((u_int32*)(rec+fo->dPkts));
  Last = *((u_int32*)(rec+fo->Last));
  First = *((u_int32*)(rec+fo->First));
  duration = Last - First;

  if (duration)
    pps = (double)dPkts / ((double)duration / 1000.0);
  else
    pps = 0;

  match = 0;

  FT_STAILQ_FOREACH(ftfldr, &lookup->list, chain) {

    switch (ftfldr->op) {

      case FT_FIL_OP_LT:
        t = (pps < ftfldr->val);
        break;
      case FT_FIL_OP_GT:
        t = (pps > ftfldr->val);
        break;
      case FT_FIL_OP_EQ:
        t = (pps == ftfldr->val);
        break;
      case FT_FIL_OP_NE:
        t = (pps != ftfldr->val);
        break;
      case FT_FIL_OP_GE:
        t = (pps >= ftfldr->val);
        break;
      case FT_FIL_OP_LE:
        t = (pps <= ftfldr->val);
        break;
      default:
        fterr_warnx("eval_match_flows: internal error");
        return -1;
        break;

    } /* switch */

    /* did this line match? */
    if (t) {
      match = 1;
      break;
    }

  } /* ftflcr */

  /* if there was a match, then return that mode */
  if (match)
    return ftfldr->mode;

  /* else return the default */
  return lookup->default_mode;

} /* eval_match_pps */

/*
 * function: eval_match_random_sample
 *
 * Evalute random_sample
 *
 * returns: FT_FIL_MODE_PERMIT
 *          FT_FIL_MODE_DENY
 */
inline int eval_match_random_sample(struct ftfil_lookup_rate *lookup, char *rec,
  struct fts3rec_offsets *fo)
{
  int val;

  /* val is a random number from 0..lookup->rate-1 */
  val = rand() % lookup->rate;

  /* pick 0 as the "pass" value -- could have picked any number in the range */
  if (!val)
    return (lookup->mode == FT_FIL_MODE_PERMIT) ? FT_FIL_MODE_PERMIT :
      FT_FIL_MODE_DENY;
  else
    return (lookup->mode == FT_FIL_MODE_PERMIT) ? FT_FIL_MODE_DENY :
      FT_FIL_MODE_PERMIT;

} /* eval_match_random_sample */

/*
 *************************************************************************
                              public ftfil_*
 *************************************************************************
 */

/*
 * function: ftfil_load
 *
 * Process fname into ftfil.
 *
 * returns: 0  ok
 *          <0 fail
 */
int ftfil_load(struct ftfil *ftfil, struct ftvar *ftvar, char *fname)
{
  struct stat sb;
  struct jump *jmp;
  struct line_parser lp;
  int fd, ret, found;
  char *buf, *buf2, *c;
  char sbuf[FT_LP_MAXLINE];

  ret = -1;
  buf = (char*)0L;
  bzero(&lp, sizeof lp);
  bzero(ftfil, sizeof *ftfil);

  FT_SLIST_INIT(&ftfil->defs);
  FT_SLIST_INIT(&ftfil->primitives);

  lp.sym_ip_prot = ftsym_new(FT_PATH_SYM_IP_PROT);
  lp.sym_ip_tcp_port = ftsym_new(FT_PATH_SYM_TCP_PORT);
  lp.sym_asn = ftsym_new(FT_PATH_SYM_ASN);
  lp.sym_tag = ftsym_new(FT_PATH_SYM_TAG);

  lp.fname = fname;

  if ((fd = open(fname, O_RDONLY, 0)) < 0) {
    fterr_warn("open(%s)", fname);
    goto load_fil_out;
  }

  if (fstat(fd, &sb) < 0) {
    fterr_warn("stat(%s)", fname);
    goto load_fil_out;
  }
  
  /* allocate storage for file */
  if (!(buf = malloc(sb.st_size+1))) {
    fterr_warn("malloc()");
    goto load_fil_out;
  }

  /* read in file */
  if (read(fd, buf, sb.st_size) != sb.st_size) {
    fterr_warnx("read(%s): short", fname);
    goto load_fil_out;
  }

  /* null terminate file */
  buf[sb.st_size] = 0;

  buf2 = buf;

  for (;;) {
  
    /* rip a line */
    for (;;) {
      c = strsep(&buf2, "\n");
      ++lp.lineno;
      if ((c && *c != 0) || (!c))
        break;
    }

    /* no more lines */
    if (!c) {
      goto load_fil_done;
    }

    /* do variable substitutions first */
    if (ftvar) {
      if (ftvar_evalstr(ftvar, c, sbuf, sizeof(sbuf)) < 0) {
        fterr_warnx("ftvar_evalstr(): failed");
        goto load_fil_done;
      }
    } else {
      strncpy(sbuf, c, sizeof(sbuf));
      sbuf[sizeof(sbuf)-1] = 0;
    }
  
    lp.buf = sbuf;

    /* first word */
    NEXT_WORD(&lp.buf, c);

    /* whitespace only line */
    if (!c) {
      continue;
    }

    /* comment line */
    if (c && *c == '#')
      continue;

    for (jmp = pjump; jmp->name; ++jmp) {

      found = 0;

      if (((!jmp->state) || (jmp->state & lp.state))
        && (!strcasecmp(c, jmp->name))) {

        found = 1;

        if (jmp->func(&lp, ftfil))
          goto load_fil_out;

        NEXT_WORD(&lp.buf, c);

        if (c) {
          fterr_warnx("%s line %d: Unexpected \"%s\".", lp.fname, lp.lineno, c);
          goto load_fil_out;;
        }

        break;

      }

    } /* test each word */

    if (!found) {
      fterr_warnx("%s line %d: Unexpected \"%s\".", lp.fname, lp.lineno, c);
      goto load_fil_out;
    }
   
  } /* more lines */

load_fil_done:

  if (resolve_primitives(ftfil)) {
    fterr_warnx("resolve_primitives(): failed");
    goto load_fil_out;
  }

  ret = 0;

load_fil_out:

  if (fd != -1)
    close(fd);

  if (buf)
    free(buf);

  if (ret == -1)
    ftfil_free(ftfil);

  if (lp.sym_ip_prot)
    ftsym_free(lp.sym_ip_prot);

  if (lp.sym_ip_tcp_port)
    ftsym_free(lp.sym_ip_tcp_port);

  if (lp.sym_asn)
    ftsym_free(lp.sym_asn);

  if (lp.sym_tag)
    ftsym_free(lp.sym_tag);

  return ret;

} /* ftfil_load */

void ftfil_free(struct ftfil *ftfil)
{
  struct ftfil_primitive *ftfp;
  struct ftfil_lookup_ip_address *ftflipa;
  struct ftfil_lookup_ip_prefix *ftflippr;
  struct ftfil_lookup_ip_mask *ftflipm;
  struct ftfil_lookup_counter *ftflc;
  struct ftfil_lookup_counter_rec *ftflcr;
  struct ftfil_lookup_tag *ftflt;
  struct ftfil_lookup_tag_mask *ftfltm;
  struct ftfil_lookup_tag_mask_rec *ftfltmr;
  struct ftfil_lookup_ip_mask_rec *ftflipmr;
  struct ftfil_def *ftfd;
  struct ftfil_match *ftm;
  struct ftfil_match_item *ftmi;
  struct ftfil_lookup_time *ftfltme;
  struct ftfil_lookup_time_rec *ftfltmer;
  struct ftfil_lookup_double *ftfld;
  struct ftfil_lookup_double_rec *ftfldr;

  /*
   * walk the primitive list, free each entry
   */

  while (!FT_SLIST_EMPTY(&ftfil->primitives)) {

    ftfp = FT_SLIST_FIRST(&ftfil->primitives);

    switch (ftfp->type) {

      case FT_FIL_PRIMITIVE_TYPE_IP_PREFIX:
        ftflippr = ftfp->lookup;
        if (ftflippr->init) {
          rhead = ftflippr->rhead;
          rhead->rnh_walktree(rhead, walk_free, 0);
        }
        break;

      case FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS:
        ftflipa = ftfp->lookup;
        if (ftflipa->init)
          ftchash_free(ftflipa->ftch);
        break;

      case FT_FIL_PRIMITIVE_TYPE_IP_MASK:
        ftflipm = ftfp->lookup;
        while (!FT_STAILQ_EMPTY(&ftflipm->list)) {
          ftflipmr = FT_STAILQ_FIRST(&ftflipm->list);
          FT_STAILQ_REMOVE_HEAD(&ftflipm->list, chain);
          free(ftflipmr);
        }
        break;

      case FT_FIL_PRIMITIVE_TYPE_COUNTER:
      case FT_FIL_PRIMITIVE_TYPE_TIME_DATE:
        ftflc = ftfp->lookup;
        while (!FT_STAILQ_EMPTY(&ftflc->list)) {
          ftflcr = FT_STAILQ_FIRST(&ftflc->list);
          FT_STAILQ_REMOVE_HEAD(&ftflc->list, chain);
          free(ftflcr);
        }
        break;
      
      case FT_FIL_PRIMITIVE_TYPE_TAG:
        ftflt = ftfp->lookup;
        if (ftflt->init)
          ftchash_free(ftflt->ftch);
        break;

      case FT_FIL_PRIMITIVE_TYPE_TAG_MASK:
        ftfltm = ftfp->lookup;
        while (!FT_STAILQ_EMPTY(&ftfltm->list)) {
          ftfltmr = FT_STAILQ_FIRST(&ftfltm->list);
          FT_STAILQ_REMOVE_HEAD(&ftfltm->list, chain);
          free(ftfltmr);
        }
        break;

      case FT_FIL_PRIMITIVE_TYPE_TIME:
        ftfltme = ftfp->lookup;
        while (!FT_STAILQ_EMPTY(&ftfltme->list)) {
          ftfltmer = FT_STAILQ_FIRST(&ftfltme->list);
          FT_STAILQ_REMOVE_HEAD(&ftfltme->list, chain);
          free(ftfltmer);
        }
        break;

      case FT_FIL_PRIMITIVE_TYPE_DOUBLE:
        ftfld = ftfp->lookup;
        while (!FT_STAILQ_EMPTY(&ftfld->list)) {
          ftfldr = FT_STAILQ_FIRST(&ftfld->list);
          FT_STAILQ_REMOVE_HEAD(&ftfld->list, chain);
          free(ftfldr);
        }
        break;

      case FT_FIL_PRIMITIVE_TYPE_UNSET:
      case FT_FIL_PRIMITIVE_TYPE_AS:
      case FT_FIL_PRIMITIVE_TYPE_IP_PROTOCOL:
      case FT_FIL_PRIMITIVE_TYPE_IP_PORT:
      case FT_FIL_PRIMITIVE_TYPE_IP_PREFIX_LEN:
      case FT_FIL_PRIMITIVE_TYPE_IP_TOS:
      case FT_FIL_PRIMITIVE_TYPE_IP_TCP_FLAGS:
      case FT_FIL_PRIMITIVE_TYPE_IF_INDEX:
      case FT_FIL_PRIMITIVE_TYPE_ENGINE:
      case FT_FIL_PRIMITIVE_TYPE_RATE:
        /* no work */
        break;

    } /* ftfp->type */

    FT_SLIST_REMOVE_HEAD(&ftfil->primitives, chain);

    if (ftfp->name)
      free(ftfp->name);
    free(ftfp->lookup);
    free(ftfp);

  } /* !ftfil->primitives list empty */

  /*
   * walk the definitions list, free each entry
   */

  while (!FT_SLIST_EMPTY(&ftfil->defs)) {

    ftfd = FT_SLIST_FIRST(&ftfil->defs);

    while (!FT_STAILQ_EMPTY(&ftfd->matches)) {

      ftm = FT_STAILQ_FIRST(&ftfd->matches);

      while (!FT_STAILQ_EMPTY(&ftm->items)) {

        ftmi = FT_STAILQ_FIRST(&ftm->items);

        if (ftmi->tmp_primitive)
          free(ftmi->tmp_primitive);

        if (ftmi->tmp_type)
          free(ftmi->tmp_type);

        FT_STAILQ_REMOVE_HEAD(&ftm->items, chain);
        free(ftmi);

      } /* !ftm->items list empty */

      FT_STAILQ_REMOVE_HEAD(&ftfd->matches, chain);
      free(ftm);

    } /* !ftfd->matches list empty */

    FT_SLIST_REMOVE_HEAD(&ftfil->defs, chain);

    if (ftfd->name)
      free(ftfd->name);
    free(ftfd);

  } /* !ftfil->defs list empty */

} /* ftfil_free */

struct ftfil_def *ftfil_def_find(struct ftfil *ftfil, char *name)
{
  struct ftfil_def *ftfd;
  int found;

  found = 0;

  FT_SLIST_FOREACH(ftfd, &ftfil->defs, chain) {

    if (!strcasecmp(name, ftfd->name))
      return ftfd;

  } /* ftfd */

  return (struct ftfil_def*)0L;

} /* ftfil_def_find */

/*
 * function: ftfil_def_eval
 *
 * Evaluate a flow with a filter definition
 *
 * returns: FT_FIL_MODE_PERMIT or FT_FIL_MODE_DENY
 *          <0 fail
 */
int ftfil_def_eval(struct ftfil_def *active_def,
  char *rec, struct fts3rec_offsets *fo)
{
  struct ftfil_match_item *ftmi;
  struct ftfil_match *ftm;
  int done;

  /* for each match (OR path) */
  FT_STAILQ_FOREACH(ftm, &active_def->matches, chain) {

    done = 1;

    /* for each matchi (AND path) */
    FT_STAILQ_FOREACH(ftmi, &ftm->items, chain) {

      if (ftmi->eval(ftmi->lookup, rec, fo) == FT_FIL_MODE_DENY) {
        done = 0;
        break;
      }

    } /* matchi */

    if (done)
      return active_def->invert ? FT_FIL_MODE_DENY : FT_FIL_MODE_PERMIT;

  } /* match */

  return active_def->invert ? FT_FIL_MODE_PERMIT : FT_FIL_MODE_DENY;

} /* ftfil_def_eval */

/*
 * function: ftfil_test_xfields
 *
 * Check if fields in current flow are valid for a filter -- ie
 * the filter does not reference a field not contained in the flow.
 *
 * returns: 0 okay
 *          1 fail
 */
int ftfil_def_test_xfields(struct ftfil_def *active_def, u_int64 test)
{

  if ((active_def->xfields & test) != active_def->xfields)
    return 1;
  else
    return 0;

} /* ftfil_def_test_xfields */

/*
 *************************************************************************
                             parse_definition_*
 *************************************************************************
 */

/*
 * function: parse_definition
 *
 * process the 'filter-definition' line.  Each primitive has a unique name
 * which is added to the ftfil->definitions linked list.  The current
 * definition is updated in lp.  Filter definitions reference filter
 * primitives
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_definition(struct line_parser *lp, struct ftfil *ftfil)
{
  char *c;
  struct ftfil_def *ftfd;

  NEXT_WORD(&lp->buf, c);

  if (!c) {
    fterr_warnx("%s line %d: Expecting name.", lp->fname, lp->lineno);
    return -1;
  }

  /* check if it exists */
  FT_SLIST_FOREACH(ftfd, &ftfil->defs, chain) {

    if (!strcasecmp(c, ftfd->name)) {
      fterr_warnx("%s line %d: Name (%s) previously defined.", lp->fname,
        lp->lineno, c);
      return -1;
    }

  }

  /* no, add a new entry to the list */
  if (!(ftfd = (struct ftfil_def*)malloc(sizeof
    (struct ftfil_def)))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero(ftfd, sizeof *ftfd);
  FT_STAILQ_INIT(&ftfd->matches);

  if (!(ftfd->name = (char*)malloc(strlen(c)+1))) {
    fterr_warn("malloc()");
    free(ftfd);
    return -1;
  }

  strcpy(ftfd->name, c);

  FT_SLIST_INSERT_HEAD(&ftfil->defs, ftfd, chain);

  lp->state = PARSE_STATE_DEFINITION;
  lp->cur_def = ftfd;
  lp->cur_def_match = (void*)0L;

  return 0;

} /* parse_definition */

/*
 * function: parse_definition_match
 *
 * process the definition match lines
 *
 * _must_ call resolve_primitives when done with all lines and before
 * destroying the file parser buffer.
 *
 * returns: 0  ok
 *          <0 fail
 */
static int parse_definition_match(struct line_parser *lp, struct ftfil *ftfil)
{
  struct ftfil_match_item *ftmi;
  struct ftfil_match *ftm;
  char *c;

  if (!lp->cur_def) {
    fterr_warnx("%s line %d: Not in filter-definition mode.", lp->fname,
    lp->lineno);
    return -1;
  }

  NEXT_WORD(&lp->buf, c);
  
  if (!c) {
    fterr_warnx("%s line %d: Expecting match type.", lp->fname,
    lp->lineno); 
    return -1;
  }

  NEXT_WORD(&lp->buf, lp->word);
  
  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting match primitive.", lp->fname,
    lp->lineno); 
    return -1;
  }

  /*
   * if cur_match is not set, allocate a new match.  This is either the
   * first match line or a match after an or statement (OR path)
   */

  if (!lp->cur_def_match) {

    if (!(ftm = (struct ftfil_match*)malloc(sizeof *ftm))) {
      fterr_warn("malloc()");
      return -1;
    }

    bzero(ftm, sizeof *ftm);
    FT_STAILQ_INIT(&ftm->items);

    lp->cur_def_match = ftm;

    FT_STAILQ_INSERT_TAIL(&lp->cur_def->matches, ftm, chain);

  }

  /* add this match line to the current items list (AND path) */
  if (!(ftmi = (struct ftfil_match_item*)malloc(sizeof *ftmi))) {
    fterr_warn("malloc()");
    if (!lp->cur_def_match)
      free(ftm);
    /* the potential ftm allocation will get cleaned up in ftfil_free() */
    return -1;
  }

  bzero(ftmi, sizeof *ftmi);

  if (!(ftmi->tmp_type = malloc(strlen(c)+1))) {
    if (!lp->cur_def_match)
      free(ftm);
    free(ftmi);
    fterr_warn("malloc()");
    return -1;
  }
  strcpy(ftmi->tmp_type, c);

  if (!(ftmi->tmp_primitive = malloc(strlen(lp->word)+1))) {
    if (!lp->cur_def_match)
      free(ftm);
    free(ftmi->tmp_type);
    free(ftmi);
    fterr_warn("malloc()");
    return -1;
  }
  strcpy(ftmi->tmp_primitive, lp->word);

  FT_STAILQ_INSERT_TAIL(&lp->cur_def_match->items, ftmi, chain);

  return 0;

} /* parse_definition_match */

/*
 * function: parse_definition_invert
 *
 * process the definition invert
 *
 * returns: 0  ok
 *          <0 fail
 */
static int parse_definition_invert(struct line_parser *lp, struct ftfil *ftfil)
{

  if (!lp->cur_def) {
    fterr_warnx("%s line %d: Not in filter-definition mode.", lp->fname,
    lp->lineno);
    return -1;
  }

  lp->cur_def->invert = 1;

  return 0;

} /* parse_definition_invert */

/*
 * function: resolve_primitives
 *
 * resolve the dangling pointers to primitives in definitions --
 * allows definitions to be defined before primitives.
 *
 * _must_ be called after work done by parse_definition_match
 *
 * returns: 0  ok
 *          <0 fail
 */
static int resolve_primitives(struct ftfil *ftfil)
{
  struct ftfil_match_item *ftmi;
  struct ftfil_match *ftm;
  struct ftfil_primitive *ftfp;
  struct ftfil_def *ftfd;
  int type, found, valid;
  void *eval;

  /* foreach definition */
  FT_SLIST_FOREACH(ftfd, &ftfil->defs, chain) {

    /* foreach match line in the definition */
    FT_STAILQ_FOREACH(ftm, &ftfd->matches, chain) {

      /* for each match item in the match line */
      FT_STAILQ_FOREACH(ftmi, &ftm->items, chain) {

        /* Find the primitive */
        found = 0;
        FT_SLIST_FOREACH(ftfp, &ftfil->primitives, chain) {

          if (!strcasecmp(ftmi->tmp_primitive, ftfp->name)) {
            found = 1;
            break;
          } /* if */

        } /* ftfp */

        if (!found) {
          fterr_warnx(
            "Unable to resolve primitive \"%s\" in filter-definition \"%s\".",
            ftmi->tmp_primitive, ftfd->name);
          return -1;
        }

        /*
         * primitive found, set it and make rest of checks
         */

        /* match on what? */
        if (!strcasecmp(ftmi->tmp_type, "src-as")) {
          type = FT_FIL_DEFINITION_MATCH_SRC_AS;
          eval =  eval_match_src_as;
          ftfd->xfields |= FT_XFIELD_SRC_AS;
        } else if (!strcasecmp(ftmi->tmp_type, "source-as")) {
          type = FT_FIL_DEFINITION_MATCH_SRC_AS;
          eval =  eval_match_src_as;
          ftfd->xfields |= FT_XFIELD_SRC_AS;
        } else if (!strcasecmp(ftmi->tmp_type, "dst-as")) {
          type = FT_FIL_DEFINITION_MATCH_DST_AS;
          eval =  eval_match_dst_as;
          ftfd->xfields |= FT_XFIELD_DST_AS;
        } else if (!strcasecmp(ftmi->tmp_type, "destination-as")) {
          type = FT_FIL_DEFINITION_MATCH_DST_AS;
          eval =  eval_match_dst_as;
          ftfd->xfields |= FT_XFIELD_DST_AS;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-protocol")) {
          type = FT_FIL_DEFINITION_MATCH_IP_PROTOCOL;
          eval =  eval_match_ip_prot;
          ftfd->xfields |= FT_XFIELD_PROT;
        } else if (!strcasecmp(ftmi->tmp_type, "src-ip-addr-prefix-len")) {
          type = FT_FIL_DEFINITION_MATCH_IP_SRC_PREFIX_LEN;
          eval =  eval_match_ip_src_prefix_len;
          ftfd->xfields |= FT_XFIELD_SRC_MASK;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-source-address-prefix-len")) {
          type = FT_FIL_DEFINITION_MATCH_IP_SRC_PREFIX_LEN;
          eval =  eval_match_ip_src_prefix_len;
          ftfd->xfields |= FT_XFIELD_SRC_MASK;
        } else if (!strcasecmp(ftmi->tmp_type, "dst-ip-addr-prefix-len")) {
          type = FT_FIL_DEFINITION_MATCH_IP_DST_PREFIX_LEN;
          eval =  eval_match_ip_dst_prefix_len;
          ftfd->xfields |= FT_XFIELD_DST_MASK;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-destination-address-prefix-len")) {
          type = FT_FIL_DEFINITION_MATCH_IP_DST_PREFIX_LEN;
          eval =  eval_match_ip_dst_prefix_len;
          ftfd->xfields |= FT_XFIELD_DST_MASK;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-tos")) {
          type = FT_FIL_DEFINITION_MATCH_IP_TOS;
          eval =  eval_match_ip_tos;
          ftfd->xfields |= FT_XFIELD_TOS;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-marked-tos")) {
          type = FT_FIL_DEFINITION_MATCH_IP_MARKED_TOS;
          eval =  eval_match_ip_marked_tos;
          ftfd->xfields |= FT_XFIELD_MARKED_TOS;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-tcp-flags")) {
          type = FT_FIL_DEFINITION_MATCH_IP_TCP_FLAGS;
          eval =  eval_match_ip_tcp_flags;
          ftfd->xfields |= FT_XFIELD_TCP_FLAGS;
        } else if (!strcasecmp(ftmi->tmp_type, "src-ip-port")) {
          type = FT_FIL_DEFINITION_MATCH_IP_SRC_PORT;
          eval =  eval_match_ip_src_port;
          ftfd->xfields |= FT_XFIELD_SRCPORT;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-source-port")) {
          type = FT_FIL_DEFINITION_MATCH_IP_SRC_PORT;
          eval =  eval_match_ip_src_port;
          ftfd->xfields |= FT_XFIELD_SRCPORT;
        } else if (!strcasecmp(ftmi->tmp_type, "dst-ip-port")) {
          type = FT_FIL_DEFINITION_MATCH_IP_DST_PORT;
          eval =  eval_match_ip_dst_port;
          ftfd->xfields |= FT_XFIELD_DSTPORT;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-destination-port")) {
          type = FT_FIL_DEFINITION_MATCH_IP_DST_PORT;
          eval =  eval_match_ip_dst_port;
          ftfd->xfields |= FT_XFIELD_DSTPORT;
        } else if (!strcasecmp(ftmi->tmp_type, "src-ifindex")) {
          type = FT_FIL_DEFINITION_MATCH_INPUT_IF;
          eval =  eval_match_src_if_index;
          ftfd->xfields |= FT_XFIELD_INPUT;
        } else if (!strcasecmp(ftmi->tmp_type, "input-interface")) {
          type = FT_FIL_DEFINITION_MATCH_INPUT_IF;
          eval =  eval_match_src_if_index;
          ftfd->xfields |= FT_XFIELD_INPUT;
        } else if (!strcasecmp(ftmi->tmp_type, "dst-ifindex")) {
          type = FT_FIL_DEFINITION_MATCH_OUTPUT_IF;
          eval =  eval_match_dst_if_index;
          ftfd->xfields |= FT_XFIELD_OUTPUT;
        } else if (!strcasecmp(ftmi->tmp_type, "output-interface")) {
          type = FT_FIL_DEFINITION_MATCH_OUTPUT_IF;
          eval =  eval_match_dst_if_index;
          ftfd->xfields |= FT_XFIELD_OUTPUT;
        } else if (!strcasecmp(ftmi->tmp_type, "engine-id")) {
          type = FT_FIL_DEFINITION_MATCH_ENGINE_ID;
          eval =  eval_match_engine_id;
          ftfd->xfields |= FT_XFIELD_ENGINE_ID;
        } else if (!strcasecmp(ftmi->tmp_type, "engine-type")) {
          type = FT_FIL_DEFINITION_MATCH_ENGINE_TYPE;
          eval =  eval_match_engine_type;
          ftfd->xfields |= FT_XFIELD_ENGINE_TYPE;
        } else if (!strcasecmp(ftmi->tmp_type, "flows")) {
          type = FT_FIL_DEFINITION_MATCH_FLOWS;
          eval =  eval_match_flows;
          ftfd->xfields |= FT_XFIELD_DFLOWS;
        } else if (!strcasecmp(ftmi->tmp_type, "octets")) {
          type = FT_FIL_DEFINITION_MATCH_OCTETS;
          eval =  eval_match_octets;
          ftfd->xfields |= FT_XFIELD_DOCTETS;
        } else if (!strcasecmp(ftmi->tmp_type, "packets")) {
          type = FT_FIL_DEFINITION_MATCH_PACKETS;
          eval =  eval_match_packets;
          ftfd->xfields |= FT_XFIELD_DPKTS;
        } else if (!strcasecmp(ftmi->tmp_type, "extra-packets")) {
          type = FT_FIL_DEFINITION_MATCH_XTRA_PACKETS;
          eval =  eval_match_xtra_packets;
          ftfd->xfields |= FT_XFIELD_EXTRA_PKTS;
        } else if (!strcasecmp(ftmi->tmp_type, "duration")) {
          type = FT_FIL_DEFINITION_MATCH_DURATION;
          eval =  eval_match_duration;
          ftfd->xfields |= (FT_XFIELD_FIRST|FT_XFIELD_LAST);
        } else if (!strcasecmp(ftmi->tmp_type, "start-time")) {
          type = FT_FIL_DEFINITION_MATCH_START_TIME;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TIME_DATE)
            eval =  eval_match_start_time_date;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TIME)
            eval =  eval_match_start_time;
          ftfd->xfields |= (FT_XFIELD_FIRST|FT_XFIELD_UNIX_SECS|
            FT_XFIELD_UNIX_NSECS|FT_XFIELD_SYSUPTIME);
        } else if (!strcasecmp(ftmi->tmp_type, "end-time")) {
          type = FT_FIL_DEFINITION_MATCH_END_TIME;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TIME_DATE)
            eval =  eval_match_end_time_date;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TIME)
            eval =  eval_match_end_time;
          ftfd->xfields |= (FT_XFIELD_LAST|FT_XFIELD_UNIX_SECS|
            FT_XFIELD_UNIX_NSECS|FT_XFIELD_SYSUPTIME);
        } else if (!strcasecmp(ftmi->tmp_type, "src-tag")) {
          type = FT_FIL_DEFINITION_MATCH_SRC_TAG;
          ftfd->xfields |= FT_XFIELD_SRC_TAG;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TAG_MASK)
            eval =  eval_match_src_tag_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TAG)
            eval =  eval_match_src_tag_h;
        } else if (!strcasecmp(ftmi->tmp_type, "source-tag")) {
          type = FT_FIL_DEFINITION_MATCH_SRC_TAG;
          ftfd->xfields |= FT_XFIELD_SRC_TAG;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TAG_MASK)
            eval =  eval_match_src_tag_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TAG)
            eval =  eval_match_src_tag_h;
        } else if (!strcasecmp(ftmi->tmp_type, "dst-tag")) {
          type = FT_FIL_DEFINITION_MATCH_SRC_TAG;
          ftfd->xfields |= FT_XFIELD_DST_TAG;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TAG_MASK)
            eval =  eval_match_dst_tag_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TAG)
            eval =  eval_match_dst_tag_h;
        } else if (!strcasecmp(ftmi->tmp_type, "destination-tag")) {
          type = FT_FIL_DEFINITION_MATCH_SRC_TAG;
          ftfd->xfields |= FT_XFIELD_DST_TAG;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TAG_MASK)
            eval =  eval_match_dst_tag_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_TAG)
            eval =  eval_match_dst_tag_h;
        } else if (!strcasecmp(ftmi->tmp_type, "nexthop-ip-addr")) {
          type = FT_FIL_DEFINITION_MATCH_IP_NEXT_HOP_ADDR;
          ftfd->xfields |= FT_XFIELD_NEXTHOP;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_nexthop_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_nexthop_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_nexthop_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-nexthop-address")) {
          type = FT_FIL_DEFINITION_MATCH_IP_NEXT_HOP_ADDR;
          ftfd->xfields |= FT_XFIELD_NEXTHOP;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_nexthop_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_nexthop_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_nexthop_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "shortcut-ip-addr")) {
          type = FT_FIL_DEFINITION_MATCH_IP_SC_ADDR;
          ftfd->xfields |= FT_XFIELD_ROUTER_SC;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_sc_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_sc_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_sc_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-shortcut-address")) {
          type = FT_FIL_DEFINITION_MATCH_IP_SC_ADDR;
          ftfd->xfields |= FT_XFIELD_ROUTER_SC;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_sc_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_sc_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_sc_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "src-ip-addr")) {
          type = FT_FIL_DEFINITION_MATCH_IP_SRC_ADDR;
          ftfd->xfields |= FT_XFIELD_SRCADDR;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_src_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_src_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_src_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-source-address")) {
          type = FT_FIL_DEFINITION_MATCH_IP_SRC_ADDR;
          ftfd->xfields |= FT_XFIELD_SRCADDR;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_src_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_src_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_src_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "dst-ip-addr")) {
          type = FT_FIL_DEFINITION_MATCH_IP_DST_ADDR;
          ftfd->xfields |= FT_XFIELD_DSTADDR;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_dst_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_dst_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_dst_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-destination-address")) {
          type = FT_FIL_DEFINITION_MATCH_IP_DST_ADDR;
          ftfd->xfields |= FT_XFIELD_DSTADDR;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_dst_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_dst_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_dst_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "exporter-ip-addr")) {
          type = FT_FIL_DEFINITION_MATCH_IP_EXPORTER_ADDR;
          ftfd->xfields |= FT_XFIELD_EXADDR;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_exporter_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_exporter_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_exporter_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "ip-exporter-address")) {
          type = FT_FIL_DEFINITION_MATCH_IP_EXPORTER_ADDR;
          ftfd->xfields |= FT_XFIELD_EXADDR;
          if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_MASK)
            eval =  eval_match_ip_exporter_addr_l;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS)
            eval =  eval_match_ip_exporter_addr_h;
          else if (ftfp->type == FT_FIL_PRIMITIVE_TYPE_IP_PREFIX)
            eval =  eval_match_ip_exporter_addr_r;
        } else if (!strcasecmp(ftmi->tmp_type, "bps")) {
          type = FT_FIL_DEFINITION_MATCH_BPS;
          eval =  eval_match_bps;
          ftfd->xfields |= FT_XFIELD_DOCTETS|FT_XFIELD_LAST|FT_XFIELD_FIRST;
        } else if (!strcasecmp(ftmi->tmp_type, "pps")) {
          type = FT_FIL_DEFINITION_MATCH_PPS;
          eval =  eval_match_pps;
          ftfd->xfields |= FT_XFIELD_DPKTS|FT_XFIELD_LAST|FT_XFIELD_FIRST;
        } else if (!strcasecmp(ftmi->tmp_type, "random-sample")) {
          type = FT_FIL_DEFINITION_MATCH_RANDOM_SAMPLE;
          eval =  eval_match_random_sample;
        } else {
          fterr_warnx(
            "Unknown match criteria \"%s\" in filter-definition \"%s\".",
            ftmi->tmp_type, ftfd->name);
          return -1;
        }
      
        /*
         * the match type must be valid for the primitive
         */
      
        valid = 0;
      
        switch (ftfp->type) {
      
          case FT_FIL_PRIMITIVE_TYPE_AS:
            if ((type == FT_FIL_DEFINITION_MATCH_SRC_AS) ||
                (type == FT_FIL_DEFINITION_MATCH_DST_AS))
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_IP_PROTOCOL:
            if (type == FT_FIL_DEFINITION_MATCH_IP_PROTOCOL)
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_IP_MASK:
            if ((type == FT_FIL_DEFINITION_MATCH_IP_NEXT_HOP_ADDR) || 
                (type == FT_FIL_DEFINITION_MATCH_IP_SRC_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_DST_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_EXPORTER_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_SC_ADDR))
              valid = 1;
              break;
      
          case FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS:
            if ((type == FT_FIL_DEFINITION_MATCH_IP_NEXT_HOP_ADDR) || 
                (type == FT_FIL_DEFINITION_MATCH_IP_SRC_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_DST_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_EXPORTER_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_SC_ADDR))
              valid = 1;
              break;
      
          case FT_FIL_PRIMITIVE_TYPE_IP_PREFIX:
            if ((type == FT_FIL_DEFINITION_MATCH_IP_NEXT_HOP_ADDR) || 
                (type == FT_FIL_DEFINITION_MATCH_IP_SRC_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_DST_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_EXPORTER_ADDR) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_SC_ADDR))
              valid = 1;
              break;
      
      
          case FT_FIL_PRIMITIVE_TYPE_IP_PORT:
            if ((type == FT_FIL_DEFINITION_MATCH_IP_SRC_PORT) || 
                (type == FT_FIL_DEFINITION_MATCH_IP_DST_PORT))
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_IP_PREFIX_LEN:
            if ((type == FT_FIL_DEFINITION_MATCH_IP_SRC_PREFIX_LEN) ||
                (type == FT_FIL_DEFINITION_MATCH_IP_DST_PREFIX_LEN))
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_IP_TOS:
            if ((type == FT_FIL_DEFINITION_MATCH_IP_TOS) || 
                (type == FT_FIL_DEFINITION_MATCH_IP_MARKED_TOS))
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_IP_TCP_FLAGS:
            if (type == FT_FIL_DEFINITION_MATCH_IP_TCP_FLAGS)
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_IF_INDEX:
            if ((type == FT_FIL_DEFINITION_MATCH_INPUT_IF) || 
                (type == FT_FIL_DEFINITION_MATCH_OUTPUT_IF))
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_COUNTER:
            if ((type == FT_FIL_DEFINITION_MATCH_FLOWS) || 
                (type == FT_FIL_DEFINITION_MATCH_OCTETS) ||
                (type == FT_FIL_DEFINITION_MATCH_PACKETS) ||
                (type == FT_FIL_DEFINITION_MATCH_XTRA_PACKETS) ||
                (type == FT_FIL_DEFINITION_MATCH_START_TIME) ||
                (type == FT_FIL_DEFINITION_MATCH_DURATION)) 
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_TIME:
          case FT_FIL_PRIMITIVE_TYPE_TIME_DATE:
            if ((type == FT_FIL_DEFINITION_MATCH_START_TIME) || 
                (type == FT_FIL_DEFINITION_MATCH_END_TIME))
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_ENGINE:
            if ((type == FT_FIL_DEFINITION_MATCH_ENGINE_ID) || 
                (type == FT_FIL_DEFINITION_MATCH_ENGINE_TYPE))
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_TAG_MASK:
            if ((type == FT_FIL_DEFINITION_MATCH_SRC_TAG) || 
                (type == FT_FIL_DEFINITION_MATCH_DST_TAG))
              valid = 1;
            break;
      
          case FT_FIL_PRIMITIVE_TYPE_TAG:
            if ((type == FT_FIL_DEFINITION_MATCH_SRC_TAG) || 
                (type == FT_FIL_DEFINITION_MATCH_DST_TAG))
              valid = 1;
            break;

          case FT_FIL_PRIMITIVE_TYPE_DOUBLE:
            if ((type == FT_FIL_DEFINITION_MATCH_PPS) || 
                (type == FT_FIL_DEFINITION_MATCH_BPS))
              valid = 1;
            break;

          case FT_FIL_PRIMITIVE_TYPE_RATE:
            if (type == FT_FIL_DEFINITION_MATCH_RANDOM_SAMPLE)
              valid = 1;
      
          default:
            break;
      
        } /* switch */
      
        /* make sure primitive is valid for match type */
        if (!valid) {
          fterr_warnx(
            "Primitive \"%s\" incompatible with match in filter-definition \"%s\".",
            ftmi->tmp_type, ftfd->name);
          return -1;
        }

        ftmi->lookup = ftfp->lookup;
        ftmi->eval = eval;

      } /* ftmi */

    } /* ftm */

  } /* ftfd */

  return 0;

} /* resolve_primitives */

/*
 * function: parse_definition_or
 *
 * process the definition or lines
 *
 * returns: 0  ok
 *          <0 fail
 */
static int parse_definition_or(struct line_parser *lp, struct ftfil *ftfil)
{
  lp->cur_def_match = (struct ftfil_match*)0L;
  return 0;
} /* parse_definition_or */

/*
 *************************************************************************
                            parse_primitive_*
 *************************************************************************
 */

/*
 * function: parse_primitive_type_asn
 *
 * process the asn primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_asn(struct line_parser *lp, struct ftfil *ftfil)
{
  struct ftfil_lookup_as *ftfla;

  /* enable symbol lookups */
  lp->sym_cur = lp->sym_tag;

  ftfla = (struct ftfil_lookup_as*)lp->cur_primitive->lookup;

  if (ftfil_load_lookup(lp, lp->word, 65536, ftfla->mode, lp->mode)) {
    fterr_warnx("load_lookup(): failed");
    return -1;
  }

  return 0;

} /* parse_primitive_type_asn */

/*
 * function: parse_primitive_type_ip_prot
 *
 * process the ip_prot primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_ip_prot(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_prot *ftflipp;

  /* enable symbol lookups */
  lp->sym_cur = lp->sym_ip_prot;

  ftflipp = (struct ftfil_lookup_ip_prot*)lp->cur_primitive->lookup;

  if (ftfil_load_lookup(lp, lp->word, 256, ftflipp->mode, lp->mode)) {
    fterr_warnx("load_lookup(): failed");
    return -1;
  }

  return 0;

} /* parse_primitive_type_ip_prot */

/*
 * function: parse_primitive_type_ip_port
 *
 * process the ip_port primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_ip_port(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_port *ftflippo;

  /* enable symbol lookups */
  lp->sym_cur = lp->sym_ip_tcp_port;

  ftflippo = (struct ftfil_lookup_ip_port*)lp->cur_primitive->lookup;

  if (ftfil_load_lookup(lp, lp->word, 65536, ftflippo->mode, lp->mode)) {
    fterr_warnx("load_lookup(): failed");
    return -1;
  }

  return 0;

} /* parse_primitive_type_ip_port */

/*
 * function: parse_primitive_type_ip_prefix_len
 *
 * process the ip_prefix_len primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_ip_prefix_len(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_prefix_len *ftflipl;

  /* disable symbol lookups */
  lp->sym_cur = (struct ftsym*)0L;

  ftflipl = (struct ftfil_lookup_ip_prefix_len*)lp->cur_primitive->lookup;

  if (ftfil_load_lookup(lp, lp->word, 33, ftflipl->mode, lp->mode)) {
    fterr_warnx("load_lookup(): failed");
    return -1;
  }

  return 0;

} /* parse_primitive_type_ip_prefix_len */

/*
 * function: parse_primitive_type_ip_tos
 *
 * process the ip_tos primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_ip_tos(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_tos *ftflipt;

  /* disable symbol lookups */
  lp->sym_cur = (struct ftsym*)0L;

  ftflipt = (struct ftfil_lookup_ip_tos*)lp->cur_primitive->lookup;

  if (ftfil_load_lookup(lp, lp->word, 256, ftflipt->mode, lp->mode)) {
    fterr_warnx("load_lookup(): failed");
    return -1;
  }

  return 0;

} /* parse_primitive_type_ip_tos */

/*
 * function: parse_primitive_type_ip_tcp_flags
 *
 * process the ip_tcp_flags primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_ip_tcp_flags(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_tcp_flags *ftfliptcp;

  /* disable symbol lookups */
  lp->sym_cur = (struct ftsym*)0L;

  ftfliptcp = (struct ftfil_lookup_ip_tcp_flags*)lp->cur_primitive->lookup;

  if (ftfil_load_lookup(lp, lp->word, 256, ftfliptcp->mode, lp->mode)) {
    fterr_warnx("load_lookup(): failed");
    return -1;
  }

  return 0;

} /* parse_primitive_type_ip_tcp_flags */

/*
 * function: parse_primitive_type_if_index
 *
 * process the if_index primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_if_index(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_if_index *ftflif;

  /* disable symbol lookups */
  lp->sym_cur = (struct ftsym*)0L;

  ftflif = (struct ftfil_lookup_if_index*)lp->cur_primitive->lookup;

  if (ftfil_load_lookup(lp, lp->word, 65536, ftflif->mode, lp->mode)) {
    fterr_warnx("load_lookup(): failed");
    return -1;
  }

  return 0;

} /* parse_primitive_type_if_index */

/*
 * function: parse_primitive_type_engine
 *
 * process the engine primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_engine(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_engine *ftfle;

  /* disable symbol lookups */
  lp->sym_cur = (struct ftsym*)0L;

  ftfle = (struct ftfil_lookup_engine*)lp->cur_primitive->lookup;

  if (ftfil_load_lookup(lp, lp->word, 65536, ftfle->mode, lp->mode)) {
    fterr_warnx("load_lookup(): failed");
    return -1;
  }

  return 0;

} /* parse_primitive_type_engine */

/*
 * function: parse_primitive_type_ip_address
 *
 * process the ip-address primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_ip_address(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_address *ftflipa;
  struct ftchash_rec_fil_c32 ftch_recfc32, *ftch_recfc32p;
  u_int32 hash;
  char fmt_buf[32];

  ftflipa = (struct ftfil_lookup_ip_address*)lp->cur_primitive->lookup;

  bzero(&ftch_recfc32, sizeof ftch_recfc32);

  ftch_recfc32.c32 = scan_ip(lp->word);

  hash = (ftch_recfc32.c32>>16) ^ (ftch_recfc32.c32 & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(ftflipa->ftch, &ftch_recfc32.c32,
    hash))) {
    fmt_ipv4(fmt_buf, ftch_recfc32p->c32, FMT_JUST_LEFT);
    fterr_warnx("%s line %d: entry %s previously set as %s.", lp->fname,
      lp->lineno, fmt_buf, mode_name_lookup[ftch_recfc32p->mode]);
  }

  if (!(ftch_recfc32p = ftchash_update(ftflipa->ftch, &ftch_recfc32, hash))) {
    fterr_warnx("ftch_update(): failed");
    return -1;
  }

  ftch_recfc32p->mode = lp->mode;

  return 0;

} /* parse_primitive_type_ip_address */

/*
 * function: parse_primitive_type_tag
 *
 * process the tag primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_tag(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_tag *ftflt;
  struct ftchash_rec_fil_c32 ftch_recfc32, *ftch_recfc32p;
  u_int32 hash, val;

  ftflt = (struct ftfil_lookup_tag*)lp->cur_primitive->lookup;

  bzero(&ftch_recfc32, sizeof ftch_recfc32);

  if (isalpha((int)lp->word[0])) {
    if (lp->sym_tag && ftsym_findbyname(lp->sym_tag, lp->word, &val))
      ftch_recfc32.c32 = val;
    else {
      fterr_warnx("%s line %d: symbol lookup for \"%s\" failed.", lp->fname,
        lp->lineno, lp->word);
      return -1;
    }
  } else
    ftch_recfc32.c32 = strtoul(lp->word, (char**)0L, 0);

  hash = (ftch_recfc32.c32>>16) ^ (ftch_recfc32.c32 & 0xFFFF);
  hash = ((hash >>8) ^ (hash & 0x0FFF));

  if ((ftch_recfc32p = ftchash_lookup(ftflt->ftch, &ftch_recfc32.c32, hash)))
    fterr_warnx("%s line %d: entry 0x%lX previously set as %s.", lp->fname,
      lp->lineno, ftch_recfc32.c32, mode_name_lookup[ftch_recfc32p->mode]);

  if (!(ftch_recfc32p = ftchash_update(ftflt->ftch, &ftch_recfc32, hash))) {
    fterr_warnx("ftch_update(): failed");
    return -1;
  }

  ftch_recfc32p->mode = lp->mode;

  return 0;

} /* parse_primitive_type_tag */

/*
 * function: parse_primitive_type_ip_mask
 *
 * process the ip-mask primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_ip_mask(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_mask *ftflipm;
  struct ftfil_lookup_ip_mask_rec *ftflipmr, *ftflipmr2;
  char fmt_buf1[32], fmt_buf2[32];
  char *ip;

  ip = lp->word;

  NEXT_WORD(&lp->buf, lp->word);

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting mask.", lp->fname, lp->lineno);
    return -1;
  }

  ftflipm = (struct ftfil_lookup_ip_mask*)lp->cur_primitive->lookup;

  if (!(ftflipmr = (struct ftfil_lookup_ip_mask_rec*)
    malloc(sizeof *ftflipmr))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero(ftflipmr, sizeof *ftflipmr);

  ftflipmr->ip = scan_ip(ip);
  ftflipmr->mask = scan_ip(lp->word);
  ftflipmr->mode = lp->mode;

  FT_STAILQ_FOREACH(ftflipmr2, &ftflipm->list, chain) {

    if ((ftflipmr2->ip == ftflipmr->ip) &&
        (ftflipmr2->mask == ftflipmr->mask)) {

      fmt_ipv4(fmt_buf1, ftflipmr2->ip, FMT_JUST_LEFT);
      fmt_ipv4(fmt_buf2, ftflipmr2->mask, FMT_JUST_LEFT);

      fterr_warnx("%s line %d: entry %s %s previously set as %s.", lp->fname,
        lp->lineno, fmt_buf1, fmt_buf2, mode_name_lookup[ftflipmr2->mode]);

    }
  }

  FT_STAILQ_INSERT_TAIL(&ftflipm->list, ftflipmr, chain);

  return 0;

} /* parse_primitive_type_ip_mask */

/*
 * function: parse_primitive_type_tag_mask
 *
 * process the tag-mask primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_tag_mask(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_tag_mask *ftfltm;
  struct ftfil_lookup_tag_mask_rec *ftfltmr, *ftfltmr2;
  u_int32 val, tval;
  char *tag;

  tag = lp->word;

  NEXT_WORD(&lp->buf, lp->word);

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting mask.", lp->fname, lp->lineno);
    return -1;
  }

  ftfltm = (struct ftfil_lookup_tag_mask*)lp->cur_primitive->lookup;

  if (isalpha((int)tag[0])) {
    if (lp->sym_tag && ftsym_findbyname(lp->sym_tag, tag, &val))
      tval = val;
    else {
      fterr_warnx("%s line %d: symbol lookup for \"%s\" failed.", lp->fname,
        lp->lineno, tag);
      return -1;
    }
  } else
    tval = strtoul(tag, (char**)0L, 0);

  if (!(ftfltmr = (struct ftfil_lookup_tag_mask_rec*)
    malloc(sizeof *ftfltmr))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero(ftfltmr, sizeof *ftfltmr);

  ftfltmr->tag = tval;
  ftfltmr->mask = strtoul(lp->word, (char**)0L, 0);
  ftfltmr->mode = lp->mode;

  FT_STAILQ_FOREACH(ftfltmr2, &ftfltm->list, chain) {

    if ((ftfltmr2->tag == ftfltmr->tag) &&
        (ftfltmr2->mask == ftfltmr->mask)) {

      fterr_warnx("%s line %d: entry 0x%lX 0x%lX previously set as %s.",
        lp->fname, lp->lineno, ftfltmr2->tag, ftfltmr2->mask,
        mode_name_lookup[ftfltmr2->mode]);

    }
  }

  FT_STAILQ_INSERT_TAIL(&ftfltm->list, ftfltmr, chain);

  return 0;

} /* parse_primitive_type_tag_mask */

/*
 * function: parse_primitive_type_ip_prefix
 *
 * process the ip-prefix primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_ip_prefix(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_prefix *ftflippr;
  struct ftfil_lookup_ip_prefix_rec *ftflipprr, *ftflipprr2;
  struct ip_prefix ipp;
  struct radix_sockaddr_in sock1, sock2;
  char fmt_buf[32];

  ftflippr = (struct ftfil_lookup_ip_prefix*)lp->cur_primitive->lookup;

  if (!(ftflipprr = (struct ftfil_lookup_ip_prefix_rec*)
     malloc(sizeof *ftflipprr))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero (ftflipprr, sizeof *ftflipprr);

  ipp = scan_ip_prefix(lp->word);

  ftflipprr->rt_nodes->rn_key = (caddr_t)&ftflipprr->addr;
  ftflipprr->addr.sin_addr.s_addr = ipp.addr;
  ftflipprr->addr.sin_len = sizeof (struct radix_sockaddr_in);
  ftflipprr->addr.sin_family = AF_INET;
  ftflipprr->masklen = ipp.len;
  ftflipprr->mode = lp->mode;

  bzero(&sock1, sizeof sock1);
  bzero(&sock2, sizeof sock2);

  sock1.sin_addr.s_addr = ipp.addr;
  sock1.sin_family = AF_INET;
  sock1.sin_len = sizeof sock1;

  sock2.sin_addr.s_addr = (!ipp.len) ? 0 : mask_lookup[ipp.len];
  sock2.sin_family = AF_INET;
  sock2.sin_len = sizeof sock2;

  ftflipprr2 = (struct ftfil_lookup_ip_prefix_rec*)
    ftflippr->rhead->rnh_lookup(&sock1, &sock2, ftflippr->rhead);

  if (ftflipprr2 && (ftflipprr2->addr.sin_addr.s_addr == ipp.addr) &&
     (ftflipprr2->masklen == ipp.len)) {

    fmt_ipv4prefix(fmt_buf, ftflipprr2->addr.sin_addr.s_addr,
      ftflipprr2->masklen, FMT_JUST_LEFT);

    fterr_warnx("%s line %d: entry %s previously set as %s.", lp->fname,
      lp->lineno, fmt_buf, mode_name_lookup[ftflipprr2->mode]);

    /* can't add this again */
    free(ftflipprr);
    return 0;

  }

  if (!ftflippr->rhead->rnh_addaddr(&ftflipprr->addr, &sock2, ftflippr->rhead,
    ftflipprr->rt_nodes)) {
    free(ftflipprr);
    fterr_warnx("rnh_addaddr(): failed for %s", lp->word);
    return -1;
  }

  return 0;

} /* parse_primitive_type_ip_prefix */

/*
 * function: parse_primitive_type_counter
 *
 * process the counter primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_counter(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_counter *ftflc;
  struct ftfil_lookup_counter_rec *ftflcr, *ftflcr2;
  char *c;
  enum ftfil_op op;

  c = lp->word;

  NEXT_WORD(&lp->buf, lp->word);

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting counter.", lp->fname, lp->lineno);
    return -1;
  }

  if (!strcasecmp(c, "lt"))
    op = FT_FIL_OP_LT;
  else if (!strcasecmp(c, "gt"))
    op = FT_FIL_OP_GT;
  else if (!strcasecmp(c, "eq"))
    op = FT_FIL_OP_EQ;
  else if (!strcasecmp(c, "ne"))
    op = FT_FIL_OP_NE;
  else if (!strcasecmp(c, "le"))
    op = FT_FIL_OP_LE;
  else if (!strcasecmp(c, "ge"))
    op = FT_FIL_OP_GE;
  else {
    fterr_warnx("%s line %d: Expecting one of {lt,gt,eq,ne,le,ge}",
      lp->fname, lp->lineno);
    return -1;
  }

  ftflc = (struct ftfil_lookup_counter*)lp->cur_primitive->lookup;

  if (!(ftflcr = (struct ftfil_lookup_counter_rec*) malloc(sizeof *ftflcr))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero(ftflcr, sizeof *ftflcr);

  ftflcr->val = strtoul(lp->word, (char**)0L, 0);
  ftflcr->op = op;
  ftflcr->mode = lp->mode;

  FT_STAILQ_FOREACH(ftflcr2, &ftflc->list, chain) {

    if ((ftflcr2->val == ftflcr->val) &&
        (ftflcr2->op == ftflcr->op)) {

      fterr_warnx("%s line %d: entry %s %lu previously set as %s.",
        lp->fname, lp->lineno, op_name_lookup[ftflcr2->op], ftflcr2->val,
        mode_name_lookup[ftflcr2->mode]);

    }
  }


  FT_STAILQ_INSERT_TAIL(&ftflc->list, ftflcr, chain);

  return 0;

} /* parse_primitive_type_counter */

/*
 * function: parse_primitive_type_time_date
 *
 * process the time/date primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_time_date(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_counter *ftflc;
  struct ftfil_lookup_counter_rec *ftflcr, *ftflcr2;
  time_t t;
  enum ftfil_op op;

  if (!lp->buf) {
    fterr_warnx("%s line %d: Expecting time/date.", lp->fname, lp->lineno);
    return -1;
  }

  if ((t = get_date(lp->buf, (time_t*)0L)) == -1) {
    fterr_warnx("%s line %d: Cannot parse time/date.", lp->fname, lp->lineno);
    return -1;
  }

  /* eat the line */
  for (; *(lp->buf); ++lp->buf);

  if (!strcasecmp(lp->word, "lt"))
    op = FT_FIL_OP_LT;
  else if (!strcasecmp(lp->word, "gt"))
    op = FT_FIL_OP_GT;
  else if (!strcasecmp(lp->word, "eq"))
    op = FT_FIL_OP_EQ;
  else if (!strcasecmp(lp->word, "ne"))
    op = FT_FIL_OP_NE;
  else if (!strcasecmp(lp->word, "le"))
    op = FT_FIL_OP_LE;
  else if (!strcasecmp(lp->word, "ge"))
    op = FT_FIL_OP_GE;
  else {
    fterr_warnx("%s line %d: Expecting one of {lt,gt,eq,ne,le,ge}",
      lp->fname, lp->lineno);
    return -1;
  }
  
  ftflc = (struct ftfil_lookup_counter*)lp->cur_primitive->lookup;

  if (!(ftflcr = (struct ftfil_lookup_counter_rec*) malloc(sizeof *ftflcr))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero(ftflcr, sizeof *ftflcr);

  ftflcr->val = t;
  ftflcr->op = op;
  ftflcr->mode = lp->mode;

  FT_STAILQ_FOREACH(ftflcr2, &ftflc->list, chain) {

    if ((ftflcr2->val == ftflcr->val) &&
        (ftflcr2->op == ftflcr->op)) {

      fterr_warnx("%s line %d: entry %s %lu previously set as %s.",
        lp->fname, lp->lineno, op_name_lookup[ftflcr2->op], ftflcr2->val,
        mode_name_lookup[ftflcr2->mode]);

    }
  }

  FT_STAILQ_INSERT_TAIL(&ftflc->list, ftflcr, chain);

  return 0;

} /* parse_primitive_type_time_date */

/*
 * function: parse_primitive_type_time
 *
 * process the time primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_time(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_time *ftfltme;
  struct ftfil_lookup_time_rec *ftfltmer, *ftfltmer2;
  enum ftfil_op op;
  char *c, *cop;
  int hour, min, sec;

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting time op.", lp->fname, lp->lineno);
    return -1;
  }

  cop = lp->word;

  NEXT_WORD(&lp->buf, lp->word);

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting time value.", lp->fname, lp->lineno);
    return -1;
  }

  /* parse out time */
  hour = min = sec = 0;

  if (!(c = strsep(&lp->word, ":")))
    goto done;
  
  hour = atoi(c);

  if (!(c = strsep(&lp->word, ":")))
    goto done;

  min = atoi(c);

  if (!(c = strsep(&lp->word, ":")))
    goto done;

  sec = atoi(c);

  if (lp->word) {
    fterr_warnx("%s line %d: Unexpected text: %s", lp->fname, lp->lineno, 
      lp->word);
    return -1;
  }

done:

  if (!strcasecmp(cop, "lt"))
    op = FT_FIL_OP_LT;
  else if (!strcasecmp(cop, "gt"))
    op = FT_FIL_OP_GT;
  else if (!strcasecmp(cop, "eq"))
    op = FT_FIL_OP_EQ;
  else if (!strcasecmp(cop, "ne"))
    op = FT_FIL_OP_NE;
  else if (!strcasecmp(cop, "le"))
    op = FT_FIL_OP_LE;
  else if (!strcasecmp(cop, "ge"))
    op = FT_FIL_OP_GE;
  else {
    fterr_warnx("%s line %d: Expecting one of {lt,gt,eq,ne,le,ge}",
      lp->fname, lp->lineno);
    return -1;
  }

  ftfltme = (struct ftfil_lookup_time*)lp->cur_primitive->lookup;

  if (!(ftfltmer = (struct ftfil_lookup_time_rec*) malloc(sizeof *ftfltmer))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero(ftfltmer, sizeof *ftfltmer);

  ftfltmer->hour = hour;
  ftfltmer->min = min;
  ftfltmer->sec = sec;
  ftfltmer->op = op;
  ftfltmer->mode = lp->mode;

  FT_STAILQ_FOREACH(ftfltmer2, &ftfltme->list, chain) {

    if ((ftfltmer2->hour == ftfltmer->hour) &&
        (ftfltmer2->min == ftfltmer->min) &&
        (ftfltmer2->sec == ftfltmer->sec) &&
        (ftfltmer2->op == ftfltmer->op)) {

      fterr_warnx("%s line %d: entry %d:%d:%d %lu previously set as %s.",
        lp->fname, lp->lineno, op_name_lookup[ftfltmer2->op], ftfltmer2->hour,
        ftfltmer2->min, ftfltmer2->sec, mode_name_lookup[ftfltmer2->mode]);

    }
  }

  FT_STAILQ_INSERT_TAIL(&ftfltme->list, ftfltmer, chain);

  return 0;

} /* parse_primitive_type_time */

/*
 * function: parse_primitive_type_double
 *
 * process the double primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_double(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_double *ftfld;
  struct ftfil_lookup_double_rec *ftfldr, *ftfldr2;
  char *c;
  enum ftfil_op op;

  c = lp->word;

  NEXT_WORD(&lp->buf, lp->word);

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting counter.", lp->fname, lp->lineno);
    return -1;
  }

  if (!strcasecmp(c, "lt"))
    op = FT_FIL_OP_LT;
  else if (!strcasecmp(c, "gt"))
    op = FT_FIL_OP_GT;
  else if (!strcasecmp(c, "eq"))
    op = FT_FIL_OP_EQ;
  else if (!strcasecmp(c, "ne"))
    op = FT_FIL_OP_NE;
  else if (!strcasecmp(c, "le"))
    op = FT_FIL_OP_LE;
  else if (!strcasecmp(c, "ge"))
    op = FT_FIL_OP_GE;
  else {
    fterr_warnx("%s line %d: Expecting one of {lt,gt,eq,ne,le,ge}",
      lp->fname, lp->lineno);
    return -1;
  }

  ftfld = (struct ftfil_lookup_double*)lp->cur_primitive->lookup;

  if (!(ftfldr = (struct ftfil_lookup_double_rec*) malloc(sizeof *ftfldr))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero(ftfldr, sizeof *ftfldr);

  ftfldr->val = strtod(lp->word, (char**)0L);
  ftfldr->op = op;
  ftfldr->mode = lp->mode;

  FT_STAILQ_FOREACH(ftfldr2, &ftfld->list, chain) {

    if ((ftfldr2->val == ftfldr->val) &&
        (ftfldr2->op == ftfldr->op)) {

      fterr_warnx("%s line %d: entry %s %f previously set as %s.",
        lp->fname, lp->lineno, op_name_lookup[ftfldr2->op], ftfldr2->val,
        mode_name_lookup[ftfldr2->mode]);

    }
  }


  FT_STAILQ_INSERT_TAIL(&ftfld->list, ftfldr, chain);

  return 0;

} /* parse_primitive_type_double */

/*
 * function: parse_primitive_type_rate
 *
 * process the rate primitive
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type_rate(struct line_parser *lp,
  struct ftfil *ftfil)
{
  struct ftfil_lookup_rate *ftflr;

  /* disable symbol lookups */
  lp->sym_cur = (struct ftsym*)0L;

  ftflr = (struct ftfil_lookup_rate*)lp->cur_primitive->lookup;

  if (ftflr->rate)
    fterr_warnx("%s line %d: Rate previously set.", lp->fname, lp->lineno);

  ftflr->rate = atoi(lp->word);

  if (!ftflr->rate)
    fterr_errx(1, "%s line %d: Rate can not be 0.", lp->fname, lp->lineno);
  ftflr->mode = lp->mode;

  return 0;

} /* parse_primitive_type_rate */

/*
 * function: parse_primitive
 *
 * process the 'filter-primitive' line.  Each primitive has a unique name
 * which is added to the ftfil->primitives linked list.  The current
 * primitive definition is updated in lp.  Primitives by themself do nothing,
 * they must be invoked by a definition.
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive(struct line_parser *lp, struct ftfil *ftfil)
{
  char *c;
  struct ftfil_primitive *ftfp;

  NEXT_WORD(&lp->buf, c);

  if (!c) {
    fterr_warnx("%s line %d: Expecting name.", lp->fname, lp->lineno);
    return -1;
  }

  /* check if it exists */
  FT_SLIST_FOREACH(ftfp, &ftfil->primitives, chain) {

    if (!strcasecmp(c, ftfp->name)) {
      fterr_warnx("%s line %d: Name (%s) previously defined.", lp->fname,
        lp->lineno, c);
      return -1;
    }

  }

  /* no, add a new entry to the list */
  if (!(ftfp = (struct ftfil_primitive*)malloc(sizeof
    (struct ftfil_primitive)))) {
    fterr_warn("malloc()");
    return -1;
  }

  bzero(ftfp, sizeof *ftfp);

  if (!(ftfp->name = (char*)malloc(strlen(c)+1))) {
    fterr_warn("malloc()");
    free(ftfp);
    return -1;
  }

  strcpy(ftfp->name, c);

  FT_SLIST_INSERT_HEAD(&ftfil->primitives, ftfp, chain);

  lp->state = PARSE_STATE_PRIMITIVE;
  lp->cur_primitive = ftfp;

  return 0;

} /* parse_primitive */

/*
 * function: parse_primitive_type
 *
 * process the 'type' line.  When the type is set the initial storage
 * is allocated.
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_type(struct line_parser *lp, struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_address *ftflipa;
  struct ftfil_lookup_ip_prefix *ftflippr;
  struct ftfil_lookup_ip_mask *ftflipm;
  struct ftfil_lookup_as *ftfla;
  struct ftfil_lookup_ip_prefix_len *ftflipl;
  struct ftfil_lookup_ip_prot *ftflipp;
  struct ftfil_lookup_ip_tos *ftflipt;
  struct ftfil_lookup_ip_tcp_flags *ftfliptcp;
  struct ftfil_lookup_if_index *ftflif;
  struct ftfil_lookup_engine *ftfle;
  struct ftfil_lookup_ip_port *ftflippo;
  struct ftfil_lookup_counter *ftflc;
  struct ftfil_lookup_tag *ftflt;
  struct ftfil_lookup_tag_mask *ftfltm;
  struct ftfil_lookup_counter *ftfltme;
  struct ftfil_lookup_double *ftfld;
  struct ftfil_lookup_rate *ftflr;

  if (!lp->cur_primitive) {
    fterr_warnx("%s line %d: Must set name first.", lp->fname, lp->lineno);
    return -1;
  }

  NEXT_WORD(&lp->buf, lp->word);

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting type.", lp->fname, lp->lineno);
    return -1;
  }

  if (lp->cur_primitive->type) {
    fterr_warnx("%s line %d: Type previously defined.", lp->fname, lp->lineno);
    return -1;
  }

  if (!strcasecmp(lp->word, "as")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_AS;
    PARSE_PRIMITIVE_TYPE_INIT(ftfla);

  } else if (!strcasecmp(lp->word, "ip-address-prefix")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IP_PREFIX;
    PARSE_PRIMITIVE_TYPE_INIT(ftflippr);

    RADIX_TRIE_INIT;

    if (rn_inithead((void**)&ftflippr->rhead, 32) < 0) {  
      fterr_warnx("rn_inithead(): failed");
      return -1;
    }

  } else if (!strcasecmp(lp->word, "ip-address")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS;
    PARSE_PRIMITIVE_TYPE_INIT(ftflipa);

    if (!(ftflipa->ftch = ftchash_new(4096,
      sizeof (struct ftchash_rec_fil_c32), 4, 12))) {
      fterr_warnx("ftchash_new(): failed");
      return -1;
    }

  } else if (!strcasecmp(lp->word, "ip-address-mask")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IP_MASK;
    PARSE_PRIMITIVE_TYPE_INIT(ftflipm);
    FT_STAILQ_INIT(&ftflipm->list);

  } else if (!strcasecmp(lp->word, "ip-protocol")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IP_PROTOCOL;
    PARSE_PRIMITIVE_TYPE_INIT(ftflipp);

  } else if (!strcasecmp(lp->word, "ip-port")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IP_PORT;
    PARSE_PRIMITIVE_TYPE_INIT(ftflippo);

  } else if (!strcasecmp(lp->word, "ip-address-prefix-len")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IP_PREFIX_LEN;
    PARSE_PRIMITIVE_TYPE_INIT(ftflipl);

  } else if (!strcasecmp(lp->word, "ip-tos")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IP_TOS;
    PARSE_PRIMITIVE_TYPE_INIT(ftflipt);

    ftflipt->mask = 0xFF;

  } else if (!strcasecmp(lp->word, "ip-tcp-flags")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IP_TCP_FLAGS;
    PARSE_PRIMITIVE_TYPE_INIT(ftfliptcp);

    ftfliptcp->mask = 0xFF;

  } else if (!strcasecmp(lp->word, "ifindex")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_IF_INDEX;
    PARSE_PRIMITIVE_TYPE_INIT(ftflif);

  } else if (!strcasecmp(lp->word, "counter")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_COUNTER;
    PARSE_PRIMITIVE_TYPE_INIT(ftflc);

    FT_STAILQ_INIT(&ftflc->list);

  } else if (!strcasecmp(lp->word, "time-date")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_TIME_DATE;
    PARSE_PRIMITIVE_TYPE_INIT(ftflc);

    FT_STAILQ_INIT(&ftflc->list);

  } else if (!strcasecmp(lp->word, "engine")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_ENGINE;
    PARSE_PRIMITIVE_TYPE_INIT(ftfle);

  } else if (!strcasecmp(lp->word, "tag")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_TAG;
    PARSE_PRIMITIVE_TYPE_INIT(ftflt);

    if (!(ftflt->ftch = ftchash_new(4096,
      sizeof (struct ftchash_rec_fil_c32), 4, 12))) {
      fterr_warnx("ftchash_new(): failed");
      return -1;
    }

  } else if (!strcasecmp(lp->word, "tag-mask")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_TAG_MASK;
    PARSE_PRIMITIVE_TYPE_INIT(ftfltm);

    FT_STAILQ_INIT(&ftfltm->list);

  } else if (!strcasecmp(lp->word, "time")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_TIME;
    PARSE_PRIMITIVE_TYPE_INIT(ftfltme);

    FT_STAILQ_INIT(&ftfltme->list);

  } else if (!strcasecmp(lp->word, "double")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_DOUBLE;
    PARSE_PRIMITIVE_TYPE_INIT(ftfld);

    FT_STAILQ_INIT(&ftfld->list);

  } else if (!strcasecmp(lp->word, "rate")) {

    lp->cur_primitive->type = FT_FIL_PRIMITIVE_TYPE_RATE;
    PARSE_PRIMITIVE_TYPE_INIT(ftflr);

    /* initialize random number generator */
    srand(getpid() ^ time((time_t*)0L));

  } else {

    fterr_warnx("%s line %d: Unrecognized type.", lp->fname, lp->lineno);
    return -1;

  } 

  return 0;

} /* parse_primitive_type */

int parse_primitive_permit(struct line_parser *lp, struct ftfil *ftfil)
{
  return parse2_primitive_permitdeny(lp, ftfil, FT_FIL_MODE_PERMIT);
}

int parse_primitive_deny(struct line_parser *lp, struct ftfil *ftfil)
{
  return parse2_primitive_permitdeny(lp, ftfil, FT_FIL_MODE_DENY);
}

static int parse_primitive_default(struct line_parser *lp, struct ftfil *ftfil)
{
  struct ftfil_lookup_ip_address *ftflipa;
  struct ftfil_lookup_ip_prefix *ftflippr;
  struct ftfil_lookup_ip_mask *ftflipm;
  struct ftfil_lookup_as *ftfla;
  struct ftfil_lookup_ip_prefix_len *ftflipl;
  struct ftfil_lookup_ip_prot *ftflipp;
  struct ftfil_lookup_ip_tos *ftflipt;
  struct ftfil_lookup_ip_tcp_flags *ftfliptcp;
  struct ftfil_lookup_if_index *ftflif;
  struct ftfil_lookup_engine *ftfle;
  struct ftfil_lookup_ip_port *ftflippo;
  struct ftfil_lookup_counter *ftflc;
  struct ftfil_lookup_tag *ftflt;
  struct ftfil_lookup_tag_mask *ftfltm;
  struct ftfil_lookup_counter *ftfltme;
  struct ftfil_lookup_counter *ftfld;

  int flag;

  NEXT_WORD(&lp->buf, lp->word);

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting permit or deny.", lp->fname,
    lp->lineno);
    return -1;
  }

  if (!strcasecmp(lp->word, "permit"))
    flag = FT_FIL_MODE_PERMIT;
  else if (!strcasecmp(lp->word, "deny"))
    flag = FT_FIL_MODE_DENY;
  else {
    fterr_warnx("%s line %d: Expecting permit or deny.", lp->fname,
    lp->lineno);
    return -1;
  }

  switch (lp->cur_primitive->type) {

    case FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS:
      ftflipa = lp->cur_primitive->lookup;
      ftflipa->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_PREFIX:
      ftflippr = lp->cur_primitive->lookup;
      ftflippr->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_MASK:
      ftflipm = lp->cur_primitive->lookup;
      ftflipm->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_AS:
      ftfla = lp->cur_primitive->lookup;
      ftfla->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_PROTOCOL:
      ftflipp = lp->cur_primitive->lookup;
      ftflipp->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_PREFIX_LEN:
      ftflipl = lp->cur_primitive->lookup;
      ftflipl->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_TOS:
      ftflipt = lp->cur_primitive->lookup;
      ftflipt->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_TCP_FLAGS:
      ftfliptcp = lp->cur_primitive->lookup;
      ftfliptcp->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_IF_INDEX:
      ftflif = lp->cur_primitive->lookup;
      ftflif->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_PORT:
      ftflippo = lp->cur_primitive->lookup;
      ftflippo->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_ENGINE:
      ftfle = lp->cur_primitive->lookup;
      ftfle->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_TAG:
      ftflt = lp->cur_primitive->lookup;
      ftflt->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_TAG_MASK:
      ftfltm = lp->cur_primitive->lookup;
      ftfltm->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_COUNTER:
      ftflc = lp->cur_primitive->lookup;
      ftflc->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_TIME_DATE:
      ftflc = lp->cur_primitive->lookup;
      ftflc->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_TIME:
      ftfltme = lp->cur_primitive->lookup;
      ftfltme->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_DOUBLE:
      ftfld = lp->cur_primitive->lookup;
      ftfld->default_mode = flag;
      break;

    case FT_FIL_PRIMITIVE_TYPE_RATE:
      fterr_warnx("%s line %d: No default mode for rate.", lp->fname,
        lp->lineno);
      break;

    default:
      fterr_errx(1, "parse_primitive_default(): internal error");
      break;

  } /* switch */

  return 0;
  
} /* parse_primitive_default */


/*
 * function: parse2_primitive_permitdeny
 *
 * process the permit and deny lines
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse2_primitive_permitdeny(struct line_parser *lp, struct ftfil *ftfil,
  int mode)
{
  int ret;

  ret = -1;

  if (!lp->cur_primitive) {
    fterr_warnx("%s line %d: Must set type first.", lp->fname, lp->lineno);
    return -1;
  }

  lp->mode = mode;

  NEXT_WORD(&lp->buf, lp->word);
  
  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting permit/deny data.", lp->fname,
    lp->lineno); 
    return -1;
  }

  switch (lp->cur_primitive->type) {

    case FT_FIL_PRIMITIVE_TYPE_AS:
      ret = parse_primitive_type_asn(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_PREFIX:
      ret = parse_primitive_type_ip_prefix(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_ADDRESS:
      ret = parse_primitive_type_ip_address(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_MASK:
      ret = parse_primitive_type_ip_mask(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_PROTOCOL:
      ret = parse_primitive_type_ip_prot(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_PORT:
      ret = parse_primitive_type_ip_port(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_PREFIX_LEN:
      ret = parse_primitive_type_ip_prefix_len(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_TOS:
      ret = parse_primitive_type_ip_tos(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_TCP_FLAGS:
      ret = parse_primitive_type_ip_tcp_flags(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IF_INDEX:
      ret = parse_primitive_type_if_index(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_COUNTER:
      ret = parse_primitive_type_counter(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_TIME_DATE:
      ret = parse_primitive_type_time_date(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_ENGINE:
      ret = parse_primitive_type_engine(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_TAG:
      ret = parse_primitive_type_tag(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_TAG_MASK:
      ret = parse_primitive_type_tag_mask(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_TIME:
      ret = parse_primitive_type_time(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_DOUBLE:
      ret = parse_primitive_type_double(lp, ftfil);
      break;

    case FT_FIL_PRIMITIVE_TYPE_RATE:
      ret = parse_primitive_type_rate(lp, ftfil);
      break;

    default:
      fterr_errx(1, "parse_primitive_permitdeny(): internal error");
      break;

  } /* switch */

  return ret;

} /* parse_primitive_permitdeny */

/*
 * function: parse_primitive_mask
 *
 * process the 'mask' line.
 *
 * returns: 0  ok
 *          <0 fail
 */
int parse_primitive_mask(struct line_parser *lp, struct ftfil *ftfil)
{

  struct ftfil_lookup_ip_tos *ftflipt;
  struct ftfil_lookup_ip_tcp_flags *ftfliptcp;


  if (!lp->cur_primitive) {
    fterr_warnx("%s line %d: Must set name first.", lp->fname, lp->lineno);
    return -1;
  }

  NEXT_WORD(&lp->buf, lp->word);

  if (!lp->word) {
    fterr_warnx("%s line %d: Expecting mask value.", lp->fname, lp->lineno);
    return -1;
  }

  switch (lp->cur_primitive->type) {

    case FT_FIL_PRIMITIVE_TYPE_IP_TOS:
      ftflipt = lp->cur_primitive->lookup;
      ftflipt->mask = strtoul(lp->word, (char**)0L, 0);
      break;

    case FT_FIL_PRIMITIVE_TYPE_IP_TCP_FLAGS:
      ftfliptcp = lp->cur_primitive->lookup;
      ftfliptcp->mask = strtoul(lp->word, (char**)0L, 0);
      break;

    default:
      fterr_warnx("%s line %d: Mask not supported for primitive.", lp->fname,
        lp->lineno);
      return -1;

  } /* switch */

  return 0;

} /* parse_primitive_mask */

/*
 *************************************************************************
                            internal support
 *************************************************************************
 */

/*
 * function: ftfil_load_lookup
 *
 *  loads a list of , seperated numbers into an array
 *  ! will invert the list
 *  - can be used as a range operator
 *
 *  example
 *   1,5-10   == 1,5,6,7,8,9,10
 *   !1       == all numbers in the range except for 1
 *
 * Only ranges that have been specified will be initialized.
 *
 * The array will either be unset, or set
 * to FT_FL_MODE_PERMIT or FT_FL_MODE_DENY
 */
static int ftfil_load_lookup(struct line_parser *lp, char *s, int size,
  char *list, int mode)
{
  char *p, *q, *r, c;
  int j, flag;
  unsigned i, i2;
  int permit,deny;
  u_int32 val;

  if (mode == FT_FIL_MODE_DENY) {
    permit = FT_FIL_MODE_DENY;
    deny = FT_FIL_MODE_PERMIT;
  } else if (mode == FT_FIL_MODE_PERMIT) {
    permit = FT_FIL_MODE_PERMIT;
    deny = FT_FIL_MODE_DENY;
  } else {
    fterr_errx(1, "ftfil_load_lookup(): internal error mode not set");
  }

  p = s;

  while ((*p == ' ') || (*p == '\t')) ++p;
  if (*p == '!') {
    flag = deny;
    ++p;
  } else {
    flag = permit;
  }

  while (*p) {

    /* skip white space */
    for (q = p; *q && (*q == ' ' || *q == '\t'); ++q);

    /* skip to the end of the word */
    for (r = q; *r && (*r != ',' && *r != '-'); ++r);

    /* save the character */
    c = *r;

    /* q is the null terminated word now */
    *r = 0;

    /* looks like a symbol? then try a lookup */
    if (isalpha((int)*q)) {
      if (lp->sym_cur && ftsym_findbyname(lp->sym_cur, q, &val))
        i = val;
      else {
        fterr_warnx("%s line %d: symbol lookup for \"%s\" failed.", lp->fname,
          lp->lineno, q);
        return -1;
      }
    } else
      i = (unsigned)strtoul(q, (char**)0L, 0);

    if (i >= size) {
      fterr_warnx("%s line %d: Value out of range.", lp->fname, lp->lineno);
      return -1;
    }

    if (list[i] != FT_FIL_MODE_UNSET)
      fterr_warnx("%s line %d: index %u previously set as %s.", lp->fname,
        lp->lineno, i, mode_name_lookup[(int)list[i]]);
      
    list[i] = flag;

    /* if the next char was a null terminator, then done */
    if (!c)
      break;

    /* skip to next word */
    p = r+1;

    if (c == '-') {

      /* skip white space */
      for (q = p; *q && (*q == ' ' || *q == '\t'); ++q);

      /* skip to the end of the word */
      for (r = q; *r && (*r != ',' && *r != '-'); ++r);
  
      /* save the character */
      c = *r;
  
      /* q is the null terminated word now */
      *r = 0;

      /* looks like a symbol? then try a lookup */
      if (isalpha((int)*q)) {
        if (lp->sym_cur && ftsym_findbyname(lp->sym_cur, q, &val))
          i2 = val;
        else {
          fterr_warnx("%s line %d: symbol lookup for \"%s\" failed.", lp->fname,
            lp->lineno, q);
          return -1;
        }
      } else
        i2 = (unsigned)strtoul(q, (char**)0L, 0);

      if (i2 >= size) {
        fterr_warnx("%s line %d: Value out of range.", lp->fname, lp->lineno);
        return -1;
      }

      for (j = i; j <= i2; ++j) {

        if ((j != i) && (list[j] != FT_FIL_MODE_UNSET))
          fterr_warnx("%s line %d: index %u previously set as %s.", lp->fname,
            lp->lineno, j, mode_name_lookup[(int)list[j]]);

        list[j] = flag;

      }

      /* skip to next word */
      p = r+1;

      /* if the next char was a null terminator, then done */
      if (!c)
        break;

    }

  } /* *p */

  return 0;

} /* ftfil_load_lookup */

static int walk_free(struct radix_node *rn, struct walkarg *UNUSED)
{
  struct ftfil_lookup_ip_prefix_rec *r;
  struct radix_sockaddr_in sock1, sock2;

  r = (struct ftfil_lookup_ip_prefix_rec*)rn;
  bzero(&sock1, sizeof sock1);
  bzero(&sock2, sizeof sock2);

  sock1.sin_addr.s_addr = r->addr.sin_addr.s_addr;
  sock1.sin_len = sizeof sock1;
  sock1.sin_family = AF_INET;

  sock2.sin_addr.s_addr = (!r->masklen) ? 0: mask_lookup[r->masklen];
  sock2.sin_len = sizeof sock2;
  sock2.sin_family = AF_INET;

  if (r != (struct ftfil_lookup_ip_prefix_rec*)rhead->rnh_deladdr(&sock1,
    &sock2, rhead))
    fterr_errx(1, "rn_deladdr(): failed.");
  else
    free(r);

  return 0;
} /* walk_free */
