/*
 * Copyright (c) 2002-2006 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagbits.c 15597 2012-03-28 22:21:32Z jomi.gregory $
 */

#define DAGBITS_BUILD

/* Endace headers. */
#include "dagapi.h"
#include "dagclarg.h"
#include "dagcrc.h"
#include "dagerf.h"
#include "dag_platform.h"
#include "dagutil.h"
#include "dag_aal_connections.h"
#include "dag_protocol_decode.h"

#ifdef _WIN32
#include <windows.h>	// To include LoadLibrary(), etc for plugins
#else
#include <dlfcn.h>		// To include dlopen(), etc for plugins
#endif

#include "dagbits_plugins/dagbits_plugin.h"

/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagbits.c 15597 2012-03-28 22:21:32Z jomi.gregory $";
static const char* const kRevisionString = "$Revision: 15597 $";

#define MC_RAW_MAX_PLOAD	32	// bytes

typedef struct mc_raw_rec_data
{
	uint16_t          rlen;
	uint16_t          wlen;
	uint8_t           pload[MC_RAW_MAX_PLOAD];
} mc_raw_rec_data_t;

typedef struct mc_hdlc_stats
{
	uint64_t frames;
	uint64_t fcs_errors;
	uint64_t short_errors;
	uint64_t long_errors;
	uint64_t abort_errors;
	uint64_t octet_errors;
	uint64_t lost_errors;
} mc_hdlc_stats_t;

typedef struct mc_atm_stats
{
	uint64_t cells;
	uint64_t lost_errors;
	uint64_t hecs_corrected;
	uint64_t oam_crc_errors;
	uint64_t oam_cells;
} mc_atm_stats_t;

typedef struct mc_aal2_stats
{
	uint64_t cells;
	uint64_t maal_errors;
} mc_aal2_stats_t;

typedef struct mc_aal5_stats
{
	uint64_t cells;
	uint64_t crc_errors;
	uint64_t length_error;
} mc_aal5_stats_t;

typedef struct mc_ima_stats
{
	uint64_t icp_cells;
	uint64_t idle_cells;
	uint64_t data_cells;
} mc_ima_stats_t;

typedef enum steering{
	STEER_FORCE0 = 0,
	STEER_PARITY = 1,
	STEER_CRC = 2,
	STEER_IFACE = 4,
	STEER_HLB_RANGE = 8
} steering_t;


#define TEST_PRINT           1LL
#define TEST_DELTA              (1 <<  1)
#define TEST_MONO               (1 <<  2)
#define TEST_IP                 (1 <<  3)
#define TEST_RAWSEND            (1 <<  4)
#define TEST_JITTER             (1 <<  5)
#define TEST_CCELL              (1 <<  6)
#define TEST_CIPH               (1 <<  7)
#define TEST_IPID               (1 <<  8)
#define TEST_BTX                (1 <<  9)
#define TEST_LFSR               (1 << 10)
#define TEST_LCTR               (1 << 11)
#define TEST_CPACKET            (1 << 12)
#define TEST_FCS                (1 << 13)
#define TEST_FLAGS              (1 << 14)
#define TEST_OFFSET             (1 << 15)
#define TEST_SIG                (1 << 16)
#define TEST_WLEN               (1 << 17)
#define TEST_RLEN               (1 << 18)
#define TEST_DGE                (1 << 19)
#define TEST_TIME               (1 << 20)
#define TEST_IDLE               (1 << 21)
#define TEST_AAL5               (1 << 22)
#define TEST_BOPT               (1 << 23)
#define TEST_MAAL               (1 << 24)
#define TEST_TYPE               (1 << 25)
#define TEST_ALIGN64            (1 << 26)
#define TEST_INCR_DATA          (1 << 27)
#define TEST_IMA_CNT            (1 << 28)
#define TEST_MODIFY_TIMESTAMPS  (1 << 29)
#define TEST_STEERING           (1 << 30)
#define TEST_CPACKET2           (1LL << 31)
#define TEST_DECODE             (1LL << 32)
#define TEST_DELTA_VARIANCE     (1LL << 33)
#define TEST_IPF_TP             (1LL << 34)
#define TEST_STATIC_DATA        (1LL << 35)
#define TEST_SEQUENCE           (1LL << 36)
#define TEST_ALIGN              (1LL << 37)



/* Max record size for buffers = 64kB */
#define MAXLEN 1024*64

#define MAX_CHANNELS 512
#define MC_RAW_COMBINATIONS 2048

#define BUFSIZE 256

#define IB_VCRC_LEN 2
#define IB_ICRC_LEN 4

#define ERF_HEADER	16	// bytes
#define ERF_EXT_HEADER	8	// bytes

/********************************************************************************************************************/
/* Plugin test variables */
#define PLUGIN_PARAM_CNT	8192
#define PLUGIN_TEST_CNT		50
#define PLUGIN_TEST_NAME_LEN	200
#define PLUGIN_PARAM_NAME_LEN	4096
#define PLUGIN_ERR_MSG	250

static char plugin_buf[PLUGIN_PARAM_CNT];

global_params_t global_params;
struct_protocol_t protocol;

typedef struct plugin_data
{
	char		*test_name;
	unsigned int	param_cnt;
	char		*params[PLUGIN_PARAM_CNT];
	
	/* Plugin entry points (defined as function pointers in dagbits_plugin.h) */
	void		*handle;
	test_init	plugin_init;
	test_run	plugin_run;
	test_printf	plugin_printf;
	test_err_msg	plugin_err_msg;
	test_final_msg	plugin_final_msg;
	test_cleanup	plugin_cleanup;
} plugin_data_t;

plugin_data_t *plugin_info[PLUGIN_TEST_CNT];	/* Array of pointers that each point to a structure */
unsigned int plugin_counter = 0;		/* Counts the number of --plugin options used when calling dagbits */

/* Function prototypes */
void plugin_load (void);
void plugin_process_buf (void);

static unsigned int dagclarg_offset = 1;	/* The params array used for processing by dagclarg in the _init plugin function needs to be offset by 1 */

/* String pointers for use in opening plugin functions/symbols */
//must be fixed to use the config script $PREFIX ?? or to be moved 
//static char *lib_prefix = "/usr/local/lib/dagbits_plugins/lib";
#ifdef _WIN32
static char *lib_prefix = "dagbits_plugin_";
#ifdef _DEBUG
static char *lib_postfix = "_d.dll";
#else
static char *lib_postfix = ".dll";
#endif
#else
static char *lib_prefix = LIBPREFIX"/";
static char *lib_postfix = ".so";
#endif

static char *init_sym = "_init";
static char *run_sym = "_run";
//static char *printf_sym = "_printf";
static char *err_msg_sym = "_err_msg";
static char *final_msg_sym = "_final_msg";
static char *cleanup_sym = "_cleanup";

#ifdef _WIN32
static char plugin_err_msg_win[PLUGIN_ERR_MSG];
#endif

/********************************************************************************************************************/

static char dagname_buf[DAGNAME_BUFSIZE] = "dag0";
static char jthresh_buf[BUFSIZE];
static char reclen_buf[BUFSIZE];
static char stop_buf[BUFSIZE];
static char halt_buf[BUFSIZE];
static char haltmax_buf[BUFSIZE];
static char wlen_buf[BUFSIZE];
static char rlen_buf[BUFSIZE];
static char idle_int_buf[BUFSIZE];
static char maxrec_buf[BUFSIZE];
static char dlay_buf[BUFSIZE];
static char wait_buf[BUFSIZE];
static char prnmaxerr_buf[BUFSIZE];
static char numpkts_buf[BUFSIZE];
static char api_buf[BUFSIZE];
static char fname_buf[BUFSIZE];
static char crc_buf[BUFSIZE];
static char params_buf[BUFSIZE];
static char steering_buf[BUFSIZE];
static char maal_buf[BUFSIZE];
static char numstreams_buf[BUFSIZE];
static char stream_buf[BUFSIZE];
static char delta_min_buf[BUFSIZE];
static char delta_max_buf[BUFSIZE];
static char steer_hlb_range_buf[BUFSIZE];
static char pause_buf[BUFSIZE];

/* Internal routines. */
static void     error_printf(char *fmt, ...);
static uint64_t      config(int argc, char *argv[]);
static void     anysig(int sig);
static void     alarmsig(int sig);
static void     report(void);
static int      dispatch_rec(char *rec, int len);
static int      loop_file(void);
static int      loop_api(void);
static int      loop_api_advance(void);
static int      loop_api_next(void);

static void     print_rec(char *rec, int len, uint64_t index, uint64_t boffset);
static int      print_delta(char *rec, int len);
static int      test_mono(char *rec, int len);
static int      test_mono_hdlc(char *rec, int len);
static int      test_mono_mc_atm(char *rec, int len);
static int      test_mono_mc_aal(char *rec, int len);
static int      test_ip(char *rec, int len);
static int      test_rawsend(char *rec, int len);
static int      test_jitter(char *rec, int len);
static int      test_incr_data(char* rec, int len);
static int      test_incr_array(uint8_t *parray, uint32_t byte_count);
static int      test_ccell(char *rec, char *lastrec);
static int      test_ciph(char *rec, char *lastrec);
static int      test_cpacket(char *rec, char *lastrec);
static int      test_cpacket2(char *rec, char *lastrec);
static int      test_ipid(char *rec, char *lastrec);
static int      test_btx(char *rec, int len);
static int      test_lfsr(char *rec, int len);
static int      test_lctr(char *rec, int len);
static int      test_fcs(char *rec, int len);
static int      test_flags(char *rec, int len);
static int      test_sig(char *rec, int len);
static int      test_wlen(char *rec, int len);
static int      test_rlen(char *rec, int len);
static int      test_dge(char *rec, char *lastrec);
static int      test_time(char *rec, int len);
static int      test_aal5(char *rec, int len);
static int      test_bopt(char *rec, int len);
static int      test_type(char *rec, int len);
static int      test_align64(char *rec, int len);
static int      test_align(char *rec, int len);
static int      test_ima_cnt(char *rec, int len);
static int      test_modify_timestamps(char *rec, int len);
static int      test_steering(char* rec, int len);
static int      test_maal(char *rec, int len);
static int      test_vci_delta_variance(char *rec, int len);
static int      test_ipf_tp(char *rec, int len);
static int      dir(char *rec);
static int      reclen(char *rec);
static char *   find_iph(char *rec);
static int      ipcmp(char *r1, char *r2);
static int      ipcmp2(char *r1, char *r2);
static int      calc_parity( uint32_t resxor );
static int 	test_static_data(char* rec, int len);
static int	test_sequence(char* rec, int len);

static bool		IsFirstFrame(dag_record_t *drec, int type);

static void print_version(void);
static void print_usage(ClArgPtr clarg);

static void print_infiniband_headers(infiniband_rec_t  rec);
static int test_infiniband_crc( dag_record_t *drec);
static int test_infiniband_icrc( dag_record_t *drec);
/* Internal variables. */
static int  strict = 0, jthresh = 0, count=0, run=1, warning=0, quiet=0, global_ts_order_check = 0;
static uint64_t    pc=0, offset=0;
static uint64_t packet_count[MAX_INTERFACES], rlen_count[MAX_INTERFACES], wlen_count[MAX_INTERFACES];
static uint8_t packet_erf_type[MAX_INTERFACES];
static int      conf_len = 64, len;
static tt_t tt = TT_ATM;
static int      crclen=16;
static int  crc_correction=0;
static char     *lastrec[MAX_INTERFACES];

static uint64_t first_ts[MAX_INTERFACES];
static uint64_t last_ts[MAX_INTERFACES];

static char     *lastchanrec[MAX_CHANNELS];
static uint64_t lastchanindex[MAX_CHANNELS], lastchanoffset[MAX_CHANNELS];
static int      connection = -1;
static int      intf; /* interface of current record */
static uint64_t      tests; /* There are now more tests than will fit in a 32-bit integer. */

static char dagname[DAGNAME_BUFSIZE];
static int dagstream = 0;
static int numrxstreams = 0; /* number of rx streams on firmware */

static uint8_t allow_erf_type_array[256] ={0}; /* to support multiple types for -t test.if to allow type n, make  allow_erf_type_array[n] =1 */
static int      allow_type = ERF_TYPE_HDLC_POS;
static int      check_type = 0;
static int      error_lim = 0;
static uint64_t max_errors = 20;
static uint64_t errors = 0;
static time_t   seconds = 0;
static time_t   passed_seconds = 0;
static char     *params = NULL;
static int      update_max=0;
static int      swapts=0;
static int      delay_nsecs=0;
static int      wait_microsecs=0;
static uint64_t actual_packets=0;
static uint64_t expected_packets=0;
static int      test_numpkts=0;
static int	set_atm_nni=0;
static int      input_api=1;
static int      stop_when_idle = 0;
static int      print_ipf_colour = 0;
static int      print_tcp_ip_counter = 0;
static int      steer = 0; /* how the packets in 4.3 should be steered */
static int		maal_num = -1;
static int      delay_test_count = -1;
static uint64_t delta_min=0;
static uint64_t delta_max=0;
static int 		delta_print=1;
#define			STEER_HLB_COUNT_MAX		8
static int 		steer_hlb_count=0;
static int		steer_hlb_min[STEER_HLB_COUNT_MAX];
static int		steer_hlb_max[STEER_HLB_COUNT_MAX];
static int		steer_hlb_min_reg[STEER_HLB_COUNT_MAX];
static int		steer_hlb_max_reg[STEER_HLB_COUNT_MAX];
static int      count_pad = 0;
static int      no_pad = 0;
static int      erase_pad = 0;

static uint64_t delta_min_seen=0;
static uint64_t delta_max_seen=0;

/* for test_wlen */
static int      wlen_exp = 128;
/* for test_rlen */
static int      rlen_exp = 128;
/* for test_idle */
static int      idle_interval = 1;
static int      idle_count = 0;
static double		rate_sum = 0;
/* for align test. Making it as default 64 bits test */
static int align_bytes_len = 8;

/* for test_time */
struct timeval  now;

/* for testing delays */
struct timeval  next_event;
struct timeval  pause_period;
static int      pause_time;

/* maximum number of errors to print to screen, 0=all */
static int      max_print_errors = 0;
static int      total_errors = 0;

static uint64_t    mono_fail=0; /* for mono test */
static uint64_t    chan_fail=0; /* for atm mono test */
static uint64_t    ip_fail=0; /* for mono test */
static uint64_t    rawsend_etype_fail=0; /* for rawsend test */
static uint64_t    rawsend_seq_fail=0; /* for rawsend test */
static uint64_t    rawsend_crc_fail=0; /* for rawsend test */
static uint64_t    jitter_fail=0; /* for jitter test */
static uint64_t    ccell_fail=0; /* for ccell test */
static uint64_t    ciph_fail=0; /* for ciph test */
static uint64_t    cpacket_fail=0; /* for cpacket test */
static uint64_t    cpacket2_fail=0; /*for cpacket2 test */
static uint64_t    ipid_fail=0; /* for ipid test */
static uint64_t    btx_fixed_fail=0; /* for btx test */
static uint64_t    btx_pkt_fail=0; /* for btx test */
static uint64_t    btx_wlen_fail=0; /* for btx test */
static uint64_t    btx_pload_fail=0; /* for btx test */
static uint64_t    btx_slen_fail=0; /* for btx test */
static uint64_t    lfsr_fail=0; /* for lfsr test */
static uint64_t    total_loss=0; /* for lctr test */
static int         greater = 0; /* for lctr test */
static uint64_t    fcs_fail=0; /* for fcs test */
static uint64_t    fcs_pass=0; /* for fcs test */
static uint64_t    fcs_ignored=0; /* for fcs test */
static uint64_t    fcs_slen=0; /* for fcs test */
static uint64_t    fcs_unsupported=0; /* for fcs test */
static uint64_t    truncated=0, dserror=0, rxerror=0; /* for flags test */
static uint64_t    sig_fail=0; /* for sig test */
static uint64_t    offset_fail=0; /* for offset test */
static uint64_t    wlen_fail=0; /* for wlen test */
static uint64_t    rlen_fail=0; /* for rlen test */
static uint64_t    dge_fail=0; /* for dge test */
static uint64_t    time_fail=0; /* for time test */
static uint64_t    idle_fail=0; /* for idle test */
static uint64_t    aal5_fail=0; /* for aal5 test */
static uint64_t    aal5_fiderr=0; /* number of aal5 frame id errors */
static uint64_t    aal5_corderr=0; /* number of aal5 cell ordering errors */
static uint64_t    aal5_spcerr=0; /* number of aal5 strict payload checking errors */
static uint64_t    aal5_forderr=0; /* number of aal5 frame ordering errors */
static uint64_t    type_fail=0; /* for aal5 test */
static uint64_t    align64_fail=0; /* for align64 test */
static uint64_t    align_fail=0; /* for align  test */
static uint64_t    type_steerfail=0;	/* for steering test */
static uint64_t    total_maal_errors = 0; /* for maal test */
static uint64_t    incr_data_fail = 0; /* for incrimenting data test */
static uint64_t    delta_min_fail = 0; /* for delta min test*/
static uint64_t    delta_max_fail = 0; /* for delta max test*/
static uint64_t    ipf_tp_ignored = 0; /* for ipf_tp test */
static uint64_t    static_data_fail = 0; /* for static_data test */
static uint64_t    sequence_fail = 0; /* for sequence test on 0c48 and 0c192 raw traffic */


static char *infilename = NULL;

/* For TYPE_MC_X testing */
mc_hdlc_stats_t    mc_hdlc_channel[MAX_CHANNELS];
mc_atm_stats_t     mc_atm_channel[MAX_CHANNELS];
mc_aal2_stats_t    mc_aal2_channel[MAX_CHANNELS];
mc_aal5_stats_t    mc_aal5_channel[MAX_CHANNELS];
mc_ima_stats_t     mc_ima_count[MAX_CHANNELS];
mc_aal_vc_t**	   mc_aal_conn[MAX_CHANNELS];

mc_hdlc_stats_t    mc_hdlc_total;
mc_atm_stats_t     mc_atm_total;
mc_aal2_stats_t    mc_aal2_total;
mc_aal5_stats_t    mc_aal5_total;
mc_raw_rec_data_t  mc_raw_rec_info[MC_RAW_COMBINATIONS];

/* For BOPT test: */
static uint64_t bopt_min_len_fail=0;
static uint64_t bopt_rlen_fail=0;
static uint64_t bopt_ip_hdr_fail=0;
static uint64_t bopt_ip_hdr_len_fail=0;
static uint64_t bopt_burst_id_fail=0;
static uint64_t bopt_packet_id_fail=0;
static uint64_t bopt_eth_hdr_fail=0;
static uint64_t bopt_udp_checksum_fail=0;
static uint64_t bopt_packet_hdr_fail=0;
static uint64_t bopt_timestamp_fail=0;
static uint64_t bopt_payload_byte_fail=0;
static uint64_t bopt_last_timestamp=0;

/* opcode tells  presense of deth in the packet.  Here if the array value  is -1 no deth, or else it stores the offset frm the start of extension headers*/

static int8_t opcode_to_deth_offset[256];
/* function to initialize the array */
static void init_deth_offset()
{
    uint8_t i;
    memset(opcode_to_deth_offset, -1,256);

    /* Fill DETH offsets for RD Not all RDs hv DETH */
    /* from 010 0000 to 0x010 01100  has DETH*/
    for(i = 0x40; i < 0x4d;i++)
    {
        opcode_to_deth_offset[i] = 4;
    }
    for(i = 0x53; i < 0x56 ;i++)
    {
        opcode_to_deth_offset[i] = 4;
    }
    /*Fill DETH offsets for UD */
    
    opcode_to_deth_offset[0x64] = 0;
    opcode_to_deth_offset[0x65] = 0;
}
static uint64_t dagbits_str_to_ts(char * str)
{
	char* str_start, * str_end;
	unsigned int sec, ms, us, ns;
	unsigned int temp_val;
	uint64_t ret_val;
	str_start=str;
	sec = strtoul(str_start,&str_end,10);
	str_start = str_end+1;
	ms = strtoul(str_start,&str_end,10);
	str_start = str_end+1;
	us = strtoul(str_start,&str_end,10);
	str_start = str_end+1;
	ns = strtoul(str_start,NULL,10);

	temp_val = ms*4294967,
	temp_val += us*4294967/1000,
	temp_val += ns*4294967/1000000,
	ret_val = (((uint64_t)sec)<<32) + (uint64_t)temp_val;
	return ret_val;
}

static int read_hlb_range_str(char* steer_hlb_range_buf)
{
	char* start_ptr, *end_ptr;
	int int_part, frac_part;
	start_ptr = steer_hlb_range_buf;
	
	if((steer_hlb_count+1)>=STEER_HLB_COUNT_MAX)
	{
		return EXIT_FAILURE;
	}
	
    if(start_ptr[0]<'0'||start_ptr[0]>'9')
          start_ptr++;
	int_part = strtoul(start_ptr,&end_ptr,10);
	if(start_ptr == end_ptr)
	{
		dagutil_error("while processing option %s\n", steer_hlb_range_buf);
		return EXIT_FAILURE;
	}
	frac_part = 0;
	if(end_ptr[0]=='.')
	{
		start_ptr = end_ptr+1;
		frac_part = strtoul(start_ptr,&end_ptr,10);
		while(frac_part>10)
			frac_part = frac_part/10;
	}
	steer_hlb_min[steer_hlb_count] = int_part*10+frac_part;
    if(steer_hlb_min[steer_hlb_count]==999)
           steer_hlb_min_reg[steer_hlb_count] = 1023;
    else
        steer_hlb_min_reg[steer_hlb_count] = steer_hlb_min[steer_hlb_count]*1024/1000;
	if(end_ptr[0]=='-')
	{
		start_ptr = end_ptr+1;
		int_part = strtoul(start_ptr,&end_ptr,10);
		if(start_ptr == end_ptr)
		{
			dagutil_error("while processing option %s\n", steer_hlb_range_buf);
			return EXIT_FAILURE;
		}
		frac_part = 0;
		if(end_ptr[0]=='.')
		{
			start_ptr = end_ptr+1;
			frac_part = strtoul(start_ptr,&end_ptr,10);
			while(frac_part>10)
				frac_part = frac_part/10;
		}
		steer_hlb_max[steer_hlb_count] = int_part*10+frac_part;
        steer_hlb_max_reg[steer_hlb_count] = steer_hlb_max[steer_hlb_count]*1024/1000-1;
	}
	else
	{
		dagutil_error("while processing option %s\n", steer_hlb_range_buf);
		return EXIT_FAILURE;
	}
    if(steer_hlb_max[steer_hlb_count] ==steer_hlb_min[steer_hlb_count])
    {
        steer_hlb_min_reg[steer_hlb_count] = 1024;
        steer_hlb_max_reg[steer_hlb_count] = 1024;
    }
    dagutil_verbose("steer hlb range is %d - %d, %d - %d\n",steer_hlb_min[steer_hlb_count],steer_hlb_max[steer_hlb_count],steer_hlb_min_reg[steer_hlb_count],steer_hlb_max_reg[steer_hlb_count]);
    steer_hlb_count++;
    return 0;
}


#define TS(X) (swapts?(bswap_64(X)):(X))


enum
{
	/* General options. */
	CLA_HELP,
	CLA_VERBOSE,
	CLA_STRICT,
	CLA_LEGACY_FORMAT_IS_ATM,
	CLA_LEGACY_FORMAT_IS_ETH,
	CLA_LEGACY_FORMAT_IS_POS,
	CLA_COUNT,
	CLA_WARNINGS_ARE_ERRORS,
	CLA_QUIET,
	CLA_STOP ,
	CLA_STRIPCRC,
	CLA_CRC16,
	CLA_DUMMY16,
	CLA_CRC32,
	CLA_DUMMY32,
	CLA_DEVICE,
	CLA_JTHRESH,
	CLA_RECLEN,
	CLA_STOPCAPT,
	CLA_HALTANY,
	CLA_HALTMAX,
	CLA_WLEN,
	CLA_RLEN,
	CLA_MAXREC,
	CLA_ENDIANBIG,
	CLA_DELAY,
	CLA_PRNMAXERR,
	CLA_NUMPKTS,
	CLA_API,
	CLA_FNAME,
	CLA_VERSION,
	CLA_CRCCN,
	CLA_IPF,
	CLA_PARAMS,
	CLA_STEER,
	CLA_MAAL,
	CLA_PRINT_TCP_IP_COUNTER,
	CLA_WAIT,
	CLA_IDLE_INT,
	CLA_NUM_STREAMS,
	CLA_STEER_HLB_RANGE,
	CLA_STREAM,
	CLA_DELTA_MIN,
	CLA_DELTA_MAX,
	CLA_SET_NNI,
	CLA_DELAY_TESTING,
	CLA_COUNT_PAD,
	CLA_PAUSE_TIME,
	CLA_NO_PAD,
	CLA_PLUGIN,
	CLA_ERASE_PAD,
    CLA_GLOBAL_TS_ORDER,
    CLA_ALIGN_PARAM
};

static void
print_version(void)
{
	printf("dagbits (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}

static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagbits - test random bits in Endace DAG card traces.\n"
		"Usage (live capture): dagbits -d <device> [options] tests > ascii-out\n"
		"Usage (from file)   : dagbits [options] tests < trace-file-in > ascii-out\n"
		"Or                  : dagbits [options] tests -f trace-file-in > ascii-out\n");
	dagclarg_display_usage(clarg, stdout);
	printf("\nThe following tests are available:\n"
		"\tprint\t\t\t\tPrint rec timestamp, CRC, ATM header and payload\n"
		"\tdecode\t\t\t\tDecode and print IP, TCP, UDP and ICMP headers\n"
		"\tdelta\t\t\t\tPrint timestamp differences, if used with --delta-min --delta-max, this will test timestamp differences with the provided min/max value\n"
		"\tmono\t\t\t\tCheck for monotonically increasing timestamps\n"
		"\tipsum\t\t\t\tVerify IP checksum\n"
		"\trawsend\t\t\t\tExtended IP test for rawsend source\n"
		"\tjitter\t\t\t\tCheck if timestamp difference variation between consecutive rec pairs is smaller than t/2^32 sec\n"
		"\tccell\t\t\t\tCheck that consecutive cells are identical (ATM only)\n"
		"\tciph\t\t\t\tCheck that consecutive IP headers are identical\n"
		"\tcpacket\t\t\t\tCheck that consecutive IP headers and payloads are identical\n"
		"\tcpacket2\t\t\tCheck that consecutive IP headers and payloads are identical with variable wlen\n"
		"\tipid\t\t\t\tCheck that the IP-Id field increments\n"
		"\tbtx\t\t\t\tTest for btx sources, checks lengths, seq and loss\n"
		"\tlfsr\t\t\t\tTests artificial data stream for ATM\n"
		"\tfcs\t\t\t\tTest Frame Check Sum on each ERF frame\n"
		"\tlctr\t\t\t\tCheck and accumulate in-band packet loss indications (not considered an error -- will return 0)\n"
		"\tflags\t\t\t\tCheck and accumulate in-band packet error indications (these are considered to be errors and return 1)\n"
		"\toffset\t\t\t\tCheck that offset from API is record aligned (only for live DAG API capture)\n"
		"\twlen\t\t\t\tCheck that the wlen in the record is a given value (-W)\n"
		"\trlen\t\t\t\tCheck that the rlen in the record is a given value (-R)\n"
		"\tdge\t\t\t\tTest for 4.2ge card against specific traffic format\n"
		"\ttime\t\t\t\tCheck that each record timestamp is within 60s of the host time\n"
		"\tidle\t\t\t\tCheck that the record arrival rate (per second) is never 0\n"
		"\taal5\t\t\t\tCheck aal5 reassembly using cell and frame ordering fields\n"
		"\tbopt\t\t\t\tPerform BOPT-specific tests\n"
		"\ttype\t\t\t\tCheck the ERF records are of a known type\n"
		"\talign64\t\t\t\tCheck the ERF record lengths are multiples of 64-bits\n"
		"\tmodts\t\t\t\tModify timestamps timing test\n"
		"\tsteer\t\t\t\tCheck to make sure packets have been sent to the correct memory hole\n"
		"\tincr_data\t\t\tCheck that the payload has incrementing byte values \n"
		"\tstatic_data\t\t\tCheck that the payload has static byte value \n"
		"\ttsvari\t\t\t\tTest the variance between timestamps in multichannel atm streams\n"
		"\tsequence\t\t\tTest the sequence numbers between ERF records in oc48 and oc192 raw traffic\n"
        "\talign\t\t\t\tCheck the ERF record lengths are multiples of n-bits, specified by -A(--align) option\n");
}

int
dagbits_main(int argc, char *argv[])
{
	ClArgPtr clarg = NULL;
	FILE* errorfile = NULL;
	int argindex = 0;
	int chan = 0;
	int clarg_result = 0;
	int code = 0;
	int i = 0, j;
	int index = 0;
	int retval = 0;
	int verbosity = 0;
	
	// Plugin variables
	unsigned int cnt;
	int counter = 0;
	int result;
	char msg[MAX_STR_LEN] = "Test complete!";

	params="";
	dagutil_set_progname("dagbits");

	/* dagbits is one of the few programs that doesn't default to using dag0.
	 * if no device is specified it reads from a file/stdin instead. 
	 */
	memset(dagname, 0, DAGNAME_BUFSIZE);

#if defined(_WIN32) 
	/* Change the default file mode to binary. */
	_fmode = O_BINARY;
#endif
	/* Set up the command line options. */
	clarg = dagclarg_init(argc, (const char* const *) argv);

	/* General options. */
	dagclarg_add(clarg, "Display help", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');

    dagclarg_add(clarg, "CRC has been stripped (DAG 4.2)", "--stripcrc", '0', CLA_STRIPCRC);
	dagclarg_add(clarg, "PoS CRC16 for FCS test", "--crc16", '1', CLA_CRC16);
	dagclarg_add(clarg, "PoS CRC32 for FCS test", "--crc32", '3', CLA_CRC32);
	dagclarg_add(clarg, "Sets legacy format to ATM (default)", "--atmformat", 'a', CLA_LEGACY_FORMAT_IS_ATM);
    dagclarg_add_int(clarg, "Check the ERF record lengths are multiples of n-bits. To be used with align test", "--align", 'A', "n", &align_bytes_len, CLA_ALIGN_PARAM);
    dagclarg_add(clarg, "Treat Timestamps as big-endian", "--bigendian", 'b', CLA_ENDIANBIG);
	dagclarg_add(clarg, "Count records processed, progress indicator", "--count", 'c',CLA_COUNT);
	dagclarg_add_string(clarg, "CRC correction factor for btx test, 0, 16 or 32 bits", "--crcfactor", 'C', "n",crc_buf, BUFSIZE, CLA_CRCCN);
	dagclarg_add_string(clarg, "DAG device to use(stdin is the default source if -f or -d are not used)", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add_string(clarg, "Delay processing by an extra n nanoseconds on each record", "--delay", 'D', "n", dlay_buf, BUFSIZE, CLA_DELAY);
	dagclarg_add(clarg, "Sets legacy format to Ethernet", "--ethformat", 'e', CLA_LEGACY_FORMAT_IS_ETH);
	dagclarg_add_string(clarg, "Halt operation after a maximum of n errors", "--haltmax", 'E', "n", haltmax_buf, BUFSIZE, CLA_HALTMAX);
	dagclarg_add_string(clarg, "Filename to read from(stdin is the default source if -f or -d are not used)", "--fname", 'f', "fname",fname_buf, BUFSIZE, CLA_FNAME);
	dagclarg_add(clarg, "Global, causes mono to test for globally increasing rather than per-port timestamps", "--global", 'g', CLA_GLOBAL_TS_ORDER);
	dagclarg_add_string(clarg, "Steering control number for steering test, DAG 4.3 and DAG 4.3GE:\n\t\t\t\t\t\t\t0: stream0 force\n\t\t\t\t\t\t\t1: test parity\n\t\t\t\t\t\t\t2: test CRC\n\t\t\t\t\t\t\t3: iface", "--steer", 'H', "n", steering_buf, BUFSIZE, CLA_STEER);
	dagclarg_add_string(clarg, "Use <api> for live DAG API capture:\n\t\t\t\t\t\t\t0: DAG 2.4 legacy API [dag_offset()]\n\t\t\t\t\t\t\t1: DAG 2.5 API [dag_advance_stream()]\n\t\t\t\t\t\t\t2: DAG 2.5 API [dag_rx_stream_next_record()]", "--api", 'i', "api", api_buf, BUFSIZE, CLA_API);
	dagclarg_add(clarg, "Prints the colour information", "--ipf", 'I', CLA_IPF);
	dagclarg_add_string(clarg, "Jitter threshold", "--jthresh", 'j', "t", jthresh_buf, BUFSIZE, CLA_JTHRESH);
	dagclarg_add_string(clarg, "Check for the specified MAAL error code", "--maal", 'l', "m", maal_buf, BUFSIZE, CLA_MAAL); 
	dagclarg_add_string(clarg, "For test idle, interval to detect data is n", "--idle_int", 'L', "n", idle_int_buf, BUFSIZE, CLA_IDLE_INT);
	dagclarg_add_string(clarg, "Only print the first n errored records; continue testing records but only count the errors", "--prnmaxerr", 'm', "n", prnmaxerr_buf, BUFSIZE, CLA_PRNMAXERR);
	dagclarg_add_string(clarg, "Expected number of packets to receive (return code is nonzero if actual number of packets differs)", "--numpkts", 'n', "n", numpkts_buf, BUFSIZE, CLA_NUMPKTS);
	dagclarg_add(clarg, "Sets legacy format to PoS", "--posformat", 'p', CLA_LEGACY_FORMAT_IS_POS);
	dagclarg_add_string(clarg,"DAG 3.5S capture parameters","--params", 'P', "params", params_buf, BUFSIZE, CLA_PARAMS);
	dagclarg_add(clarg, "Quiet - suppresses summary info at end, but not warnings", "--quiet", 'q', CLA_QUIET);
	dagclarg_add_string(clarg, "Record length in bytes for legacy format records", "--reclen", 'r', "n", reclen_buf, BUFSIZE, CLA_RECLEN);
	dagclarg_add_string(clarg, "For test rlen, expect rlen to be n", "--rlen", 'R', "n", rlen_buf, BUFSIZE, CLA_RLEN);
	dagclarg_add(clarg, "Strict, causes mono to test for strictly increasing rather than monotonic timestamps", "--strict", 's', CLA_STRICT);
    dagclarg_add_string(clarg, "Stop after n seconds (only for live DAG API capture)", "--stop", 'S', "n", stop_buf, BUFSIZE, CLA_STOPCAPT);
	dagclarg_add_string(clarg, "Halt operation if any record does not match ERF type n", "--haltany", 't', "n", halt_buf, BUFSIZE, CLA_HALTANY);
	dagclarg_add_string(clarg, "Pause stream for testing purposes where x is the delay to start y is the period of pause repetition and z is the length of the pause", "--pause_timing", 'T', "x:y:z", pause_buf, BUFSIZE, CLA_PAUSE_TIME);
	dagclarg_add(clarg, "Count pad records and strictly check -t option ", "--count_pad", 'u', CLA_COUNT_PAD);
	dagclarg_add_string(clarg, "Maximum number of records to process in one pass (only for live DAG API capture)", "--maxrec", 'U', "n", maxrec_buf, BUFSIZE, CLA_MAXREC);
	dagclarg_add(clarg, "Increase verbosity", "--verbose", 'v', CLA_VERBOSE);
    dagclarg_add(clarg, "Print version information", "--version", 'V', CLA_VERSION);
	dagclarg_add(clarg, "Treat warnings as errors", "--warnings", 'w', CLA_WARNINGS_ARE_ERRORS);
	dagclarg_add_string(clarg, "For test wlen, expect wlen to be n", "--wlen", 'W', "n", wlen_buf, BUFSIZE, CLA_WLEN);
	dagclarg_add_string(clarg, "Wait before first packet capture from the stream extra n microseconds", "--wait", 'X', "n", wait_buf, BUFSIZE, CLA_WAIT);
	dagclarg_add_int(clarg, "Start testing after n records have been received", "--delay_test", 'y', "n", &delay_test_count, CLA_DELAY_TESTING);
	 dagclarg_add(clarg, "Stop when no traffic is received for one second", "--idlestop", 'z', CLA_STOP );
	
    dagclarg_add_string(clarg, "Delta test minmum timestamp difference, any timestamp difference smaller than the value will be reported as a failure", "--delta-min", 0, "sec.ms.us.ns", delta_min_buf, BUFSIZE, CLA_DELTA_MIN);
	dagclarg_add_string(clarg, "Delta test maximum timestamp difference, any timestamp difference larger than the value will be reported as a failure", "--delta-max", 0, "sec.ms.us.ns", delta_max_buf, BUFSIZE, CLA_DELTA_MAX);
	dagclarg_add(clarg, "Erase padding bytes from print function", "--erase_pad", 0, CLA_ERASE_PAD);	
	dagclarg_add_string(clarg, "Steer hlb range test range, for example: 15.0-50.0", "--hlb-range", 0, "min-max", steer_hlb_range_buf, BUFSIZE, CLA_STEER_HLB_RANGE);
	dagclarg_add(clarg, "Turn off pad record flushing", "--no_pad", 0, CLA_NO_PAD);
	dagclarg_add(clarg, "Dummy argument for crc16", "--d16", '6', CLA_DUMMY16);
	dagclarg_suppress_display(clarg, CLA_DUMMY16);
	dagclarg_add(clarg, "Dummy argument for crc32", "--d32", '2', CLA_DUMMY32);
	dagclarg_suppress_display(clarg, CLA_DUMMY32);	
	dagclarg_add_string(clarg, "Plug-in test to be loaded, including test parameters (p1, p2, etc)", "--plugin", 0, "testname:\"-p1 -p2 ... -pn\"", plugin_buf, PLUGIN_PARAM_CNT, CLA_PLUGIN);
dagclarg_add_string(clarg, "Number of streams in capture file (for steer -H2)", "--rxstreams", 0, "n", numstreams_buf, BUFSIZE, CLA_NUM_STREAMS);
	dagclarg_add_string(clarg, "Stream number file was captured from (for steer -H2)", "--stream", 0, "n", stream_buf, BUFSIZE, CLA_STREAM);
	dagclarg_add(clarg, "Set ATM network interface to NNI (default UNI)", "--nni", 0, CLA_SET_NNI);
	dagclarg_add(clarg, "Print TCP Flow and IP Address Counter erf records", "--tcp_ip_counter", 0, CLA_PRINT_TCP_IP_COUNTER);
	
	/* Allocate memory for the structure pointed to by each plugin array entry and initialise to a default state */
	for (cnt = 0; cnt < PLUGIN_TEST_CNT; cnt++)
	{
		plugin_info[cnt] = malloc (sizeof(plugin_data_t));
		if (plugin_info[cnt] == NULL)
		{
			fprintf (stderr, "dagbits.c: %s - Insufficient memory for plugin array structures\n", __FUNCTION__);
			exit(1);
		}
		plugin_info[cnt]->test_name = NULL;		
		plugin_info[cnt]->param_cnt = dagclarg_offset;	/* This offset is needed by dagclarg in the _init() of a plugin test */
		plugin_info[cnt]->handle = NULL;
	}
	/* initialize the array required for infiniband */
	init_deth_offset();

	/* Parse the command line options. */
	clarg_result = dagclarg_parse(clarg, NULL, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg); 
				return EXIT_SUCCESS;
				break;
			
			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				errorfile = stderr;
				break;
			
			case CLA_STRICT:
				strict++;
				break;
			
            case CLA_GLOBAL_TS_ORDER:
                global_ts_order_check = 1;
                break;
			
			case CLA_LEGACY_FORMAT_IS_ATM:
				tt = TT_ATM;
				break;
			
			case CLA_LEGACY_FORMAT_IS_ETH:
				tt = TT_ETH;
				break;
			
			case CLA_LEGACY_FORMAT_IS_POS:
				tt = TT_POS;
				break;
			
			case CLA_COUNT:
				count++;
				break;
			
			case CLA_WARNINGS_ARE_ERRORS:
				warning=1;
				break;
			
			case CLA_QUIET:
				quiet=1;
				break;

			case CLA_SET_NNI:
                                set_atm_nni = 1;
                                break;
			
			case CLA_STOP:
				stop_when_idle = 1;
				break;
			
			case CLA_STRIPCRC:
				crclen=0;
				break;
			
			case CLA_CRC16:
				crclen=16;
				break;
			
			case CLA_CRC32:
				crclen=32;
				break;
			
			case CLA_DEVICE:
				if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
				{
					dagutil_verbose("device=%s\n", dagname);
					dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
				}
				break;
			
			case CLA_JTHRESH:
				jthresh = strtol(jthresh_buf, NULL, 0);
				break;
			
			case CLA_RECLEN:
				conf_len = strtol(reclen_buf, NULL, 0);
				break;
			
			case CLA_STOPCAPT:
				seconds = strtoul(stop_buf, NULL, 0);
				break;
			
			case CLA_HALTANY:
				allow_type = strtoul(halt_buf, NULL, 0);
				if( allow_type < 256)
				{
					allow_erf_type_array[allow_type] = 1;
					check_type = 1;
				}
				else 
				{
					dagutil_warning("Invalid type %d for -t/--haltany.Ignoring it. \n",allow_type);
				}
				break;
			
			case CLA_HALTMAX:
				max_errors = strtoul(haltmax_buf, NULL, 0);
				error_lim = 1;
				break;
			
			case CLA_WLEN:
				wlen_exp = strtoul(wlen_buf, NULL, 0);
				break;
			
			case CLA_RLEN:
				rlen_exp = strtoul(rlen_buf, NULL, 0);
				break;

			case CLA_IDLE_INT:
				idle_interval = strtoul(idle_int_buf, NULL, 0);
				break;
			
			case CLA_MAXREC:
				update_max = strtoul(maxrec_buf, NULL, 0);
				break;
			
			case CLA_ENDIANBIG:
				swapts++;
				break;
			
			case CLA_DELAY:
				delay_nsecs = strtoul(dlay_buf, NULL, 0);
				break;
			case CLA_WAIT:
				wait_microsecs = strtoul(wait_buf, NULL, 0);
				break;
			
			case CLA_PRNMAXERR:
				max_print_errors = strtoul(prnmaxerr_buf, NULL, 0);
				break;
			
			case CLA_NUMPKTS:
				expected_packets = strtoul(numpkts_buf, NULL, 0);
				test_numpkts = 1;
				break;
			
			case CLA_API:
				input_api = strtoul(api_buf, NULL, 0);
				break;
			
			case CLA_FNAME:
				infilename = fname_buf;
				break;
			
			case CLA_VERSION:
				printf("dagbits (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
				return EXIT_SUCCESS;
				break;
			
			case CLA_CRCCN:
				crc_correction = strtoul(crc_buf, NULL, 0);
				break;
			
			case CLA_IPF:
				print_ipf_colour = 1;
				break;
	
			case CLA_PRINT_TCP_IP_COUNTER:
				print_tcp_ip_counter = 1;
				break;
			
			case CLA_PARAMS:
				params = params_buf;
				break;

			case CLA_MAAL:
				maal_num = strtoul(maal_buf, NULL, 0);
				break;
			
			case CLA_STEER:
				{
					/* Use the AAL5 CRC-32 because it is the same CRC, and it does not
					   bitswap the polynomial. */
					dagcrc_make_aal5_table();
					steer = strtoul(steering_buf, NULL, 0);
				}
				break;
			
			case CLA_NUM_STREAMS:
				numrxstreams = strtoul(numstreams_buf, NULL, 0);
				break;

			case CLA_STREAM:
				dagstream = strtoul(stream_buf, NULL, 0);
				break;
				
			case CLA_DELAY_TESTING:
				/* Do nothing here */
				break;

			case CLA_DELTA_MIN:
				delta_min = dagbits_str_to_ts(delta_min_buf);
				delta_print=0;
				break;
			case CLA_DELTA_MAX:
				delta_max = dagbits_str_to_ts(delta_max_buf);
				delta_print=0;
				break;
			case CLA_STEER_HLB_RANGE:
				if(read_hlb_range_str(steer_hlb_range_buf)!=0)
					return EXIT_FAILURE;
				break;

			case CLA_COUNT_PAD:
				count_pad = 1;
				break;

				
			case CLA_NO_PAD:
				no_pad = 1;
				break;

			case CLA_ERASE_PAD:
				erase_pad = 1;
				break;

			case CLA_DUMMY16:
			case CLA_DUMMY32:
				/* Do nothing. */
				break;

			case CLA_PAUSE_TIME:
				/* split up the string into the three integers and convert */
				sscanf(pause_buf, "%d:%d:%d", &i, &j, &pause_time);

				/* sanity check */
				if(pause_time > j )
					dagutil_panic("Pause time can not be greater than the period between pauses\n");

				/* convert the time periods */
				pause_period.tv_sec = (int)(j/1000);
				pause_period.tv_usec = (int)((j%1000)*1000);

				gettimeofday(&next_event, NULL);
				next_event.tv_sec += (int)(i/1000);
				next_event.tv_usec += (int)((i%1000)*1000);
				if(next_event.tv_usec >= 1000000) 
				{                        
					next_event.tv_sec++;                                
					next_event.tv_usec -= 1000000;                      
				}                         				

				break;

			case CLA_PLUGIN:

#if 0				/* Testing for plugin_buf - remove later! */
				printf ("dagbits.c: %s - argc: %d\n", __FUNCTION__, argc);
				int cnt;
				for (cnt = 0; cnt < argc; cnt++)
					printf ("dagbits.c: %s - argv[%d]: %s\n", __FUNCTION__, cnt, argv[cnt]);

				printf ("dagbits.c: %s - plugin_counter: %d, length: %d, plugin_buf: %s\n", __FUNCTION__, plugin_counter, strlen(plugin_buf), plugin_buf);
#endif	

				/* Create the entry points into the plugin and initialise it */
				plugin_load ();

#if 0				/* Testing only - remove later! */
				printf ("dagbits.c: %s - plugin_info[%d]->test_name: %s\n", __FUNCTION__, plugin_counter, plugin_info[plugin_counter]->test_name);
				printf ("dagbits.c: %s - plugin_info[%d]->param_cnt: %d\n", __FUNCTION__, plugin_counter, plugin_info[plugin_counter]->param_cnt);	
				unsigned int v;
				for (v = 0; v < plugin_info[plugin_counter]->param_cnt; v++)
					printf ("dagbits.c: %s - plugin_info[%d]->params[%d]: %s\n", __FUNCTION__, plugin_counter, v, plugin_info[plugin_counter]->params[v]);	
#endif

				plugin_counter++;
				break;
            case CLA_ALIGN_PARAM:
                /* make sure that the value is positive integer */
                if ( align_bytes_len <= 0 )
                {
                    dagutil_error("Invalid align parameter. Give byte aligned positive integer\n");
                    return EXIT_FAILURE;
                }
                if ( (align_bytes_len % 8) != 0 )
                {
                    dagutil_warning("align parameter is not byte aligned.Using %d\n", (align_bytes_len / 8) * 8);
                }
                /* input in bits. rec len is given in bytes. So convert into bytes */
                align_bytes_len = align_bytes_len / 8;
                break;
			default:
			if (argv[argindex][0] == '-')
			{
				/* Unknown option. */
				dagutil_error("unknown option %s\n", argv[argindex]);
				print_usage(clarg);
				return EXIT_FAILURE;
			}
			break;
		}
		clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

#if 0	/* Testing only - prints out information (test_name, param_cnt, params[]) on all user supplied plugin tests - remove later! */
	unsigned int n, y;

	for (n = 0; n < plugin_counter; n++)
	{
		printf ("dagbits.c: %s - plugin_info[%d]->test_name: %s\n", __FUNCTION__, n, plugin_info[n]->test_name);
		printf ("dagbits.c: %s - plugin_info[%d]->param_cnt: %d\n", __FUNCTION__, n, plugin_info[n]->param_cnt);	
		for (y = 0; y < plugin_info[n]->param_cnt; y++)
			printf ("dagbits.c: %s - plugin_info[%d]->params[%d]: %s\n", __FUNCTION__, n, y, plugin_info[n]->params[y]);
		
	}
#endif

	if (-1 == clarg_result)
	{
		if (argindex < argc)
		{
			dagutil_error("while processing option %s\n", argv[argindex]);
		}
		dagclarg_display_usage(clarg, stderr);
		return EXIT_FAILURE;
	}

	verbosity = dagutil_get_verbosity();
	
	/* Display unprocessed arguments if verbose flag was given. */
	/* Note: Unprocessed arguments are the tests to be performed. */
	argv = (char**) dagclarg_get_unprocessed_args(clarg, &argc);
	if ((NULL != argv) && (0 < argc) && (2 < verbosity))
	{
		for (index = 0; index < argc; index++)
		{
			dagutil_verbose("unprocessed argument: '%s'\n", argv[index]);
		}
	}

	tests = config(argc, argv);

	/* Set general signal handler and timer handler. */
	dagutil_set_signal_handler(anysig);

	report(); /* initialization */
    dagutil_set_timer_handler(alarmsig, 1);


	if (tests & TEST_RAWSEND)
		dagcrc_make_ethernet_table_r();
	
	if (tests & TEST_FCS) {
		/* Ethernet*/
		dagcrc_make_ethernet_table_r();

		/* PPP 16-bit. 32-bit is included statically in dagcrc.c */
		dagcrc_make_ppp_fcs16_table();

		/* ATM AAL5 */
		dagcrc_make_aal5_table();
        /*infiniband */
        dagcrc_make_infiniband_vcrc_table_r();
	}

	if (tests & TEST_MONO)
	{
		/*set all virtual connections to null for detection */
		for (i = 0; i< MAX_CHANNELS; i++)
			mc_aal_conn[i] = NULL;
	}

	if (dagname[0] != '\0')
	{
		switch (input_api)
		{
			case 0:
				dagutil_verbose("Input API: 2.4 [dag_offset()]\n");
				retval = loop_api();
				break;
			
			case 1:
				dagutil_verbose("Input API: 2.5 [dag_advance_stream()]\n");
				retval = loop_api_advance();
				break;
			
			case 2:
				dagutil_verbose("Input API: 2.5 [dag_rx_stream_next_record()]\n");
				retval = loop_api_next();
				break;
			
			default:
				dagutil_panic("Input API: unknown (%d)\n", input_api);
				break;
		}
	}
	else
	{
		if (tests & TEST_OFFSET)
			dagutil_panic("The 'offset' test can only be performed during a live capture (-d flag).\n");
		if (delay_nsecs)
			dagutil_panic("The delay (-D) flag can only be used with live capture (-d flag).\n");
		if(wait_microsecs)
			dagutil_panic("The delay (-D) flag can only be used with live capture (-d flag).\n");

		retval = loop_file();
	}

    /* kill timer */
    dagutil_set_timer_handler(NULL, 0);
	
	if (!quiet)
	{
		printf("----------------------------------------------------------------------\n");

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

		printf("\nRecords processed: %" PRIu64 " Bytes processed: %" PRIu64 "\n\n", pc, offset);

#elif defined(_WIN32)

		/* Split printf, otherwise it doesn't display correct values */
		printf("\nRecords processed: %I64u ", pc);
		printf("Bytes processed: %I64u\n\n", offset);

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

		for(i=0; i<MAX_INTERFACES; i++) {
			if(packet_count[i]) {
				printf("Interface %d\nRecords processed: %"PRIu64" Bytes processed: %"PRIu64"\n",
				       i,
				       packet_count[i],
				       rlen_count[i]);
				if( (packet_count[i] > 1) && (last_ts[i] > first_ts[i]) ){
					printf("Period: %.9fs Rate: %.2fpps\nERF:   %.0fBps (%.0fbps)\nFrame: %.0fBps (%.0fbps)\n",
					       (double)(last_ts[i] - first_ts[i])/0x100000000ll,
					       packet_count[i] / ((double)(last_ts[i] - first_ts[i])/0x100000000ll),
					       rlen_count[i] / ((double)(last_ts[i] - first_ts[i])/0x100000000ll),
					       8*rlen_count[i] / ((double)(last_ts[i] - first_ts[i])/0x100000000ll),
					       wlen_count[i] / ((double)(last_ts[i] - first_ts[i])/0x100000000ll),
					       8*wlen_count[i] / ((double)(last_ts[i] - first_ts[i])/0x100000000ll));
					if ( (packet_erf_type[i] ==  ERF_TYPE_ETH) || (packet_erf_type[i] == ERF_TYPE_COLOR_ETH ) || (packet_erf_type[i] ==  ERF_TYPE_DSM_COLOR_ETH) )
                    	printf("Eth:   %.0fBps (%.0fbps)\n",
						       (20*packet_count[i] + wlen_count[i]) / ((double)(last_ts[i] - first_ts[i])/0x100000000ll),
						       8*(20*packet_count[i] + wlen_count[i]) / ((double)(last_ts[i] - first_ts[i])/0x100000000ll) );
				}
				printf("\n");
			}
		}

		/* Print final message for each plugin test and perform cleanup */
		while (plugin_info[counter]->test_name != NULL)
		{
			result = plugin_info[counter]->plugin_final_msg(msg, MAX_STR_LEN);
			if ( result == TEST_FAIL)
			{
				/* set retval as 1 to report error */
				if (0 ==retval )
				{
					retval = 1;
				}
    		}

			printf("%s", msg);

			result = plugin_info[counter]->plugin_cleanup();
			if (result != TEST_PASS)
			{
				fprintf (stderr, "dagbits.c: %s - Cleanup failed for plugin: %s\n", __FUNCTION__, plugin_info[counter]->test_name);
				exit(1);
			}

#ifdef _WIN32
			if (!FreeLibrary(plugin_info[counter]->handle))
			{
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, plugin_err_msg_win, sizeof(plugin_err_msg_win), 0);
				fprintf (stderr, "dagbits.c: %s - FreeLibrary() error: %s\n", __FUNCTION__, plugin_err_msg_win);
			}
#else
			dlclose(plugin_info[counter]->handle);
#endif
			++counter;
		}


		if (tests & TEST_MONO)
			printf("mono: total failures %"PRIu64"\n", mono_fail);
		if (tests & TEST_DELTA)
		{
			if(delta_min)
				printf("delta: delta_min total failures %"PRIu64"\n",delta_min_fail);
			if(delta_max)
				printf("delta: delta_max total failures %"PRIu64"\n",delta_max_fail);
			printf("delta: min seen %.9f 0x%016"PRIx64"\n",
			       (double)delta_min_seen / 0x100000000ll,
			       delta_min_seen);
			printf("delta: max seen %.9f 0x%016"PRIx64"\n",
			       (double)delta_max_seen / 0x100000000ll,
			       delta_max_seen);
		}
		if (tests & TEST_IP)
			printf("ip: total failures %"PRIu64"\n", ip_fail);
		if (tests & TEST_RAWSEND)
			printf("rawsend: etype %"PRIu64" sequence %"PRIu64" crc %"PRIu64"\n",
			       rawsend_etype_fail, rawsend_seq_fail, rawsend_crc_fail);
		if (tests & TEST_JITTER)
			printf("jitter: total failures %"PRIu64"\n", jitter_fail);
		if (tests & TEST_CCELL)
			printf("ccell: total failures %"PRIu64"\n", ccell_fail);
		if (tests & TEST_CIPH)
			printf("ciph: total failures %"PRIu64"\n", ciph_fail);
		if (tests & TEST_CPACKET)
			printf("cpacket: total failures %"PRIu64"\n", cpacket_fail);
		if (tests & TEST_CPACKET2)
			printf("cpacket2: total failures %"PRIu64"\n", cpacket2_fail);
		if (tests & TEST_IPID)
			printf("ipid: total failures %"PRIu64"\n", ipid_fail);
		if (tests & TEST_BTX)
			printf("btx: fixed %"PRIu64" pktcount %"PRIu64" wlenmatch %"PRIu64" payloadcount %"PRIu64" snaplen %"PRIu64"\n",
			       btx_fixed_fail, btx_pkt_fail, btx_wlen_fail, btx_pload_fail, btx_slen_fail);
		if (tests & TEST_LFSR)
			printf("lfsr: total failures %"PRIu64"\n", lfsr_fail);
		if (tests & TEST_LCTR)
			printf("lctr: total loss %s%"PRIu64"\n", greater ? ">= " : "", total_loss);
		if (tests & TEST_FCS) {
			printf("fcs: failures                    %9"PRIu64"\n", fcs_fail);
			printf("fcs: passed                      %9"PRIu64"\n", fcs_pass);
			printf("fcs: ignored (atm loss)          %9"PRIu64"\n", fcs_ignored);
			printf("fcs: ignored (insufficient slen) %9"PRIu64"\n", fcs_slen);
			printf("fcs: ignored (unsupported type)  %9"PRIu64"\n", fcs_unsupported);
		}
		if (tests & TEST_FLAGS) {
			printf("flags: trunc %"PRId64" rxerror %"PRId64" dserror %"PRId64"\n",
			       truncated, rxerror, dserror);
			if (mc_hdlc_total.frames) {
				printf("mc_hdlc: %"PRIu64" frames, errors: fcs %"PRIu64" short %"PRIu64" long %"PRIu64" abort %"PRIu64" octet %"PRIu64" lostbyte %"PRIu64"\n",
				       mc_hdlc_total.frames,
				       mc_hdlc_total.fcs_errors,
				       mc_hdlc_total.short_errors,
				       mc_hdlc_total.long_errors,
				       mc_hdlc_total.abort_errors,
				       mc_hdlc_total.octet_errors,
				       mc_hdlc_total.lost_errors);
				if (dagutil_get_verbosity()>0) {
					for (chan=0; chan<MAX_CHANNELS; chan++) {
						if (mc_hdlc_channel[chan].frames)
						{
							printf("mc_hdlc chan %3d: %"PRIu64" frames, errors:  fcs %"PRIu64" short %"PRIu64" long %"PRIu64" abort %"PRIu64" octet %"PRIu64" lostbyte %"PRIu64"\n",
							       chan,
							       mc_hdlc_channel[chan].frames,
							       mc_hdlc_channel[chan].fcs_errors,
							       mc_hdlc_channel[chan].short_errors,
							       mc_hdlc_channel[chan].long_errors,
							       mc_hdlc_channel[chan].abort_errors,
							       mc_hdlc_channel[chan].octet_errors,
							       mc_hdlc_channel[chan].lost_errors);
						}
					}
				}
			}
			if (mc_atm_total.cells) {
				printf("mc_atm: %"PRIu64" cells, %"PRIu64" OAM, hec_corr %"PRIu64", errors: lostbyte %"PRIu64" oam crc %"PRIu64"\n",
					mc_atm_total.cells,
					mc_atm_total.oam_cells,
					mc_atm_total.hecs_corrected,
					mc_atm_total.lost_errors,
					mc_atm_total.oam_crc_errors);
				if (dagutil_get_verbosity()>0) {
					for (chan=0; chan<MAX_CHANNELS; chan++) {
						if (mc_atm_channel[chan].cells)
						{
							printf("mc_atm chan %3d: %"PRIu64" cells, %"PRIu64" OAM, hec_corr %"PRIu64", errors: lostbyte %"PRIu64" oam crc %"PRIu64"\n",
								chan,
								mc_atm_channel[chan].cells,
								mc_atm_channel[chan].oam_cells,
								mc_atm_channel[chan].hecs_corrected,
								mc_atm_channel[chan].lost_errors,
								mc_atm_channel[chan].oam_crc_errors);
						}
					}
				}
			}
			if (mc_aal2_total.cells) {
				printf("mc_aal2: %"PRIu64" cells, %"PRIu64" MAAL errors\n",
					mc_aal2_total.cells,
					mc_aal2_total.maal_errors);
				if (dagutil_get_verbosity()>0) {
					for (chan=0; chan<MAX_CHANNELS; chan++) {
						if (mc_aal2_channel[chan].cells)
						{
							printf("mc_aal2 chan %3d: %"PRIu64" cells, %"PRIu64"  MAAL errors\n",
								chan,
								mc_aal2_channel[chan].cells,
								mc_aal2_channel[chan].maal_errors);
						}
					}
				}
			}
			if (mc_aal5_total.cells) {
				printf("mc_aal5: %"PRIu64" cells, %"PRIu64" CRC errors, %"PRIu64" Length errors\n",
					mc_aal5_total.cells,
					mc_aal5_total.crc_errors,
					mc_aal5_total.length_error);
				if (dagutil_get_verbosity()>0) {
					for (chan=0; chan<MAX_CHANNELS; chan++) {
						if (mc_aal5_channel[chan].cells)
						{
							printf("mc_aal5 chan %3d: %"PRIu64" cells, %"PRIu64" CRC errors, %"PRIu64" Length errors\n",
								chan,
								mc_aal5_channel[chan].cells,
								mc_aal5_channel[chan].crc_errors,
								mc_aal5_channel[chan].length_error);
						}
					}
				}
			}
		}
		if (tests & TEST_SIG)
			printf("sig: total failures %"PRIu64"\n", sig_fail);
		if (tests & TEST_OFFSET)
			printf("offset: total failures %"PRIu64"\n", offset_fail);
		if (tests & TEST_WLEN)
			printf("wlen: total failures %"PRIu64"\n", wlen_fail);
		if (tests & TEST_RLEN)
			printf("rlen: total failures %"PRIu64"\n", rlen_fail);
		if (tests & TEST_DGE)
			printf("dge: total failures %"PRIu64"\n", dge_fail);
		if (tests & TEST_TIME)
			printf("time: total failures %"PRIu64"\n", time_fail);
		if (tests & TEST_IDLE)
			printf("idle: total seconds idle %"PRIu64"\n", idle_fail);
		if (tests & TEST_AAL5) {
			printf("aal5: total failures %"PRIu64"\n", aal5_fail);
			printf("aal5: frame ID errors %"PRIu64"\n", aal5_fiderr);
			printf("aal5: strict payload checking errors %"PRIu64"\n", aal5_spcerr);
			printf("aal5: cell ordering errors %"PRIu64"\n", aal5_corderr);
			printf("aal5: frame ordering errors %"PRIu64"\n", aal5_forderr);
		}
		if (tests & TEST_BOPT)
		{
			uint64_t bopt_fail = 0;

			printf("bopt: minimum length failures %"PRIu64"\n", bopt_min_len_fail);
			bopt_fail += bopt_min_len_fail;

			printf("bopt: ERF record length failures %"PRIu64"\n", bopt_rlen_fail);
			bopt_fail += bopt_rlen_fail;

			printf("bopt: IP header failures %"PRIu64"\n", bopt_ip_hdr_fail);
			bopt_fail += bopt_ip_hdr_fail;

			printf("bopt: IP header length failures %"PRIu64"\n", bopt_ip_hdr_len_fail);
			bopt_fail += bopt_ip_hdr_len_fail;

			printf("bopt: burst ID failures %"PRIu64"\n", bopt_burst_id_fail);
			bopt_fail += bopt_burst_id_fail;

			printf("bopt: packet ID failures %"PRIu64"\n", bopt_packet_id_fail);
			bopt_fail += bopt_packet_id_fail;

			printf("bopt: ethernet header burst consistency failures %"PRIu64"\n", bopt_eth_hdr_fail);
			bopt_fail += bopt_eth_hdr_fail;

			printf("bopt: packet header burst consistency failures %"PRIu64"\n", bopt_packet_hdr_fail);
			bopt_fail += bopt_packet_hdr_fail;

			printf("bopt: UDP checksum failures %"PRIu64"\n", bopt_udp_checksum_fail);
			bopt_fail += bopt_udp_checksum_fail;

			printf("bopt: incrementing timestamp failures %"PRIu64"\n", bopt_timestamp_fail);
			bopt_fail += bopt_timestamp_fail;

			printf("bopt: payload byte burst consistency failures %"PRIu64"\n", bopt_payload_byte_fail);
			bopt_fail += bopt_payload_byte_fail;

			printf("bopt: total failures %"PRIu64"\n", bopt_fail);
		}
		if (tests & TEST_TYPE)
			printf("type: total failures %"PRIu64"\n", type_fail);
		if (tests & TEST_ALIGN64)
			printf("align64: total failures %"PRIu64"\n", align64_fail);
		if (tests & TEST_ALIGN)
			printf("align%d: total failures %"PRIu64"\n", align_bytes_len * 8, align_fail);
		if (tests & TEST_IMA_CNT)
		{
			for (i = 0; i < MAX_CHANNELS; i++)
			{
				if ((mc_ima_count[i].icp_cells) 
						|| (mc_ima_count[i].idle_cells) 
						|| (mc_ima_count[i].data_cells))
				{
					printf("Connection %u: %"PRIu64" cells  (ICP=%"PRIu64" IDLE=%"PRIu64" DATA=%"PRIu64")\n", 
							i,                             
							mc_ima_count[i].icp_cells + mc_ima_count[i].idle_cells + mc_ima_count[i].data_cells,
							mc_ima_count[i].icp_cells,
							mc_ima_count[i].idle_cells, 
							mc_ima_count[i].data_cells);
				}
			}
		}
		if (tests & TEST_STEERING)
			printf("steering: total failures %"PRIu64"\n", type_steerfail);
		if (tests & TEST_MODIFY_TIMESTAMPS )
/*			printf("type: total time %"PRIu64"\n", mod_time ); */
			printf("type: total time \n" );
		if (tests & TEST_MAAL )
			printf("maal: maal %d failures %"PRIu64"\n", maal_num, total_maal_errors  );
		if (tests & TEST_INCR_DATA)
			printf("incr_data: total failures %"PRIu64"\n", incr_data_fail  );
		if (tests & TEST_STATIC_DATA)
			printf("static_data: total failures %"PRIu64"\n", static_data_fail);

		if (tests & TEST_SEQUENCE)
			printf("sequence: total failures %"PRIu64"\n", sequence_fail);
	}

	return retval;
}


/* Dynamically load a plugin test */
void
plugin_load(void)
{
	const char *error = NULL;
	char buffer[BUFSIZE];
	int dag_fd;
	int result;

	/* Process the --plugin argument */
	plugin_process_buf();

	/* Initialise global parameter array */
	global_params.verbose = dagutil_get_verbosity();
	global_params.cap_stream = dagstream;

	if (dagname[0] != '\0')
	{
		strcpy(global_params.dagname, dagname);
		dag_fd = dag_open(dagname);
		if (dag_fd != -1)
		{
			global_params.ifc_cnt = (int) dag_get_interface_count(dag_fd);
			dag_close(dag_fd);		
		}
		else
		{
			fprintf(stderr, "dagbits.c: %s - Couldn't open DAG card to get interface count: %s\n", __FUNCTION__, (strcmp(dagname,"")==0) ? "no card input" : dagname);
			exit(EXIT_FAILURE);
		}
	}
	//printf ("Global_params -  verbose: %d, cap_stream: %d, ifc_cnt: %d\n", global_params.verbose, global_params.cap_stream, global_params.ifc_cnt);

	/* Open the shared object file */
	strcpy(buffer, lib_prefix);
	strcat(buffer, plugin_info[plugin_counter]->test_name);
	strcat(buffer, lib_postfix);
#ifdef _WIN32
	plugin_info[plugin_counter]->handle = (void *)LoadLibrary(buffer);
	if (!plugin_info[plugin_counter]->handle)
	{
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, plugin_err_msg_win, sizeof(plugin_err_msg_win), 0);
		fprintf (stderr, "dagbits.c: %s - LoadLibrary() error: %s\n", __FUNCTION__, plugin_err_msg_win);
		exit(EXIT_FAILURE);
	}
#else
	plugin_info[plugin_counter]->handle = (void *)dlopen(buffer, RTLD_LAZY);
	if (!plugin_info[plugin_counter]->handle)
	{
		fprintf (stderr, "dagbits.c: %s - dlopen() error: %s\n", __FUNCTION__, dlerror());
		exit(EXIT_FAILURE);
	}
	dlerror();    /* Clear any existing error */
#endif

	/* Open plugin initialisation function */
	strcpy (buffer, plugin_info[plugin_counter]->test_name);
#ifdef _WIN32
	plugin_info[plugin_counter]->plugin_init = (void *)GetProcAddress(plugin_info[plugin_counter]->handle, strcat(buffer, init_sym));
	if (!plugin_info[plugin_counter]->plugin_init)
	{
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, plugin_err_msg_win, sizeof(plugin_err_msg_win), 0);
		fprintf (stderr, "dagbits.c: %s - GetProcAddress() plugin_init error: %s\n", __FUNCTION__, plugin_err_msg_win);
		exit(EXIT_FAILURE);
	}
#else
	plugin_info[plugin_counter]->plugin_init = dlsym(plugin_info[plugin_counter]->handle, strcat(buffer, init_sym));
	if ((error = dlerror()) != NULL)
	{
		fprintf (stderr, "dagbits.c: %s - dlsym() _init error: %s\n", __FUNCTION__, error);
		exit(EXIT_FAILURE);
	}
#endif

#if 0	// NOTE: Removed for now
	/* Open plugin printf function */
	strcpy (buffer, plugin_info[plugin_counter]->test_name);
#ifdef _WIN32
	plugin_info[plugin_counter]->plugin_printf = (void *)GetProcAddress(plugin_info[plugin_counter]->handle, strcat(buffer, printf_sym));
	if (!plugin_info[plugin_counter]->plugin_printf)
	{
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, plugin_err_msg_win, sizeof(plugin_err_msg_win), 0);
		fprintf (stderr, "dagbits.c: %s - GetProcAddress() plugin_printf error: %s\n", __FUNCTION__, plugin_err_msg_win);
	}
#else
    plugin_info[plugin_counter]->plugin_printf = dlsym(plugin_info[plugin_counter]->handle, strcat(buffer, printf_sym));
	if ((error = dlerror()) != NULL)
	{
		fprintf (stderr, "dagbits.c: %s - dlsym() _printf error: %s\n", __FUNCTION__, error);
		exit(1);
	}
#endif
#endif	//#if 0

	/* Execute the initialisation function */
	strcpy (buffer, plugin_info[plugin_counter]->test_name);
	result = plugin_info[plugin_counter]->plugin_init (plugin_info[plugin_counter]->params, plugin_info[plugin_counter]->param_cnt, &global_params, plugin_info[plugin_counter]->plugin_printf);
	if (result == TEST_PASS)
	{	
		/* Open plugin run function */
#ifdef _WIN32
		plugin_info[plugin_counter]->plugin_run = (void *)GetProcAddress(plugin_info[plugin_counter]->handle, strcat(buffer, run_sym));
		if (!plugin_info[plugin_counter]->plugin_run)
		{
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, plugin_err_msg_win, sizeof(plugin_err_msg_win), 0);
			fprintf (stderr, "dagbits.c: %s - GetProcAddress() plugin_run error message: %s\n", __FUNCTION__, plugin_err_msg_win);
			exit(EXIT_FAILURE);
		}
#else
		plugin_info[plugin_counter]->plugin_run = dlsym(plugin_info[plugin_counter]->handle, strcat(buffer, run_sym));
		if ((error = dlerror()) != NULL)
		{
			fprintf (stderr, "dagbits.c: %s - dlsym() _run error: %s\n", __FUNCTION__, error);
			exit(EXIT_FAILURE);
		}
#endif
	}
	else if (result == TEST_HELP)
	{
		exit(EXIT_SUCCESS);
	}
	else
	{
		fprintf (stderr, "dagbits.c: %s - Executing %s%s failed\n", __FUNCTION__, plugin_info[plugin_counter]->test_name, init_sym);
		exit(EXIT_FAILURE);
	}

	/* Open plugin error message function */
	strcpy (buffer, plugin_info[plugin_counter]->test_name);
#ifdef _WIN32
	plugin_info[plugin_counter]->plugin_err_msg = (void *)GetProcAddress(plugin_info[plugin_counter]->handle, strcat(buffer, err_msg_sym));
	if (!plugin_info[plugin_counter]->plugin_err_msg)
	{
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, plugin_err_msg_win, sizeof(plugin_err_msg_win), 0);
		fprintf (stderr, "dagbits.c: %s - GetProcAddress() plugin_err_msg error: %s\n", __FUNCTION__, plugin_err_msg_win);
		exit(EXIT_FAILURE);
	}
#else
	plugin_info[plugin_counter]->plugin_err_msg = dlsym(plugin_info[plugin_counter]->handle, strcat(buffer, err_msg_sym));
	if ((error = dlerror()) != NULL)
	{
		fprintf (stderr, "dagbits.c: %s - dlsym() _err_msg error: %s\n", __FUNCTION__, error);
		exit(EXIT_FAILURE);
	}
#endif

	/* Open plugin final message function */
	strcpy (buffer, plugin_info[plugin_counter]->test_name);
#ifdef _WIN32
	plugin_info[plugin_counter]->plugin_final_msg = (void *)GetProcAddress(plugin_info[plugin_counter]->handle, strcat(buffer, final_msg_sym));
	if (!plugin_info[plugin_counter]->plugin_final_msg)
	{
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, plugin_err_msg_win, sizeof(plugin_err_msg_win), 0);
		fprintf (stderr, "dagbits.c: %s - GetProcAddress() plugin_final_msg error: %s\n", __FUNCTION__, plugin_err_msg_win);
		exit(EXIT_FAILURE);
	}
#else
	plugin_info[plugin_counter]->plugin_final_msg = dlsym(plugin_info[plugin_counter]->handle, strcat(buffer, final_msg_sym));
	if ((error = dlerror()) != NULL)
	{
		fprintf (stderr, "dagbits.c: %s - dlsym() _final_msg error: %s\n", __FUNCTION__, error);
		exit(EXIT_FAILURE);
	}
#endif

	/* Open plugin cleanup function */
	strcpy (buffer, plugin_info[plugin_counter]->test_name);
#ifdef _WIN32
	plugin_info[plugin_counter]->plugin_cleanup = (void *)GetProcAddress(plugin_info[plugin_counter]->handle, strcat(buffer, cleanup_sym));
	if (!plugin_info[plugin_counter]->plugin_cleanup)
	{
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, plugin_err_msg_win, sizeof(plugin_err_msg_win), 0);
		fprintf (stderr, "dagbits.c: %s - GetProcAddress() plugin_cleanup error: %s\n", __FUNCTION__, plugin_err_msg_win);
		exit(EXIT_FAILURE);
	}
#else
	plugin_info[plugin_counter]->plugin_cleanup = dlsym(plugin_info[plugin_counter]->handle, strcat(buffer, cleanup_sym));
	if ((error = dlerror()) != NULL)
	{
		fprintf (stderr, "dagbits.c: %s - dlsym() _cleanup error: %s\n", __FUNCTION__, error);
		exit(EXIT_FAILURE);
	}
#endif

#if 0	/* Testing only - remove later! */
	printf ("dagbits.c: %s - plugin_info[%d]->test_name: %s\n", __FUNCTION__, plugin_counter, plugin_info[plugin_counter]->test_name);
	printf ("dagbits.c: %s - plugin_info[%d]->param_cnt: %d\n", __FUNCTION__, plugin_counter, plugin_info[plugin_counter]->param_cnt);	
	unsigned int i;
	for (i = 0; i < plugin_info[plugin_counter]->param_cnt; i++)
		printf ("dagbits.c: %s - plugin_info[%d]->params[%d]: %s\n", __FUNCTION__, plugin_counter, i, plugin_info[plugin_counter]->params[i]);	

	printf ("dagbits.c: %s - Entry points for %s plugin successfully loaded.\n", __FUNCTION__, plugin_info[plugin_counter]->test_name);
#endif

}

/* Process the argument provided to --plugin */
void
plugin_process_buf (void)
{
	char *tmp;

	/* Allocate memory for the plugin test name */
	plugin_info[plugin_counter]->test_name = malloc (sizeof (char) * PLUGIN_TEST_NAME_LEN);
	
	if (plugin_info[plugin_counter]->test_name != NULL)
		strcpy (plugin_info[plugin_counter]->test_name, strtok (plugin_buf, ":"));
	else
	{
		fprintf (stderr, "dagbits.c: %s - Insufficient memory for plugin test name\n", __FUNCTION__);
		exit(1);
	}

	/* Process the plugin test parameters */
	while (1)
	{
		/* Allocate memory for each plugin test paramater name */
		plugin_info[plugin_counter]->params[plugin_info[plugin_counter]->param_cnt] = malloc (sizeof (char) * PLUGIN_PARAM_NAME_LEN);

		if (plugin_info[plugin_counter]->params[plugin_info[plugin_counter]->param_cnt] != NULL)
		{
			tmp = strtok (NULL, " ");
			if (tmp == NULL)
				break;

			strcpy (plugin_info[plugin_counter]->params[plugin_info[plugin_counter]->param_cnt], tmp);
		}
		else
		{
			fprintf (stderr, "dagbits.c: %s - Insufficient memory for plugin test param %d\n", __FUNCTION__, plugin_info[plugin_counter]->param_cnt);
			exit(1);
		}
		++plugin_info[plugin_counter]->param_cnt;
	}

#if 0	/* Testing only - remove later */
	printf ("dagbits.c: %s - plugin_info[%d]->test_name: %s\n", __FUNCTION__, plugin_counter, plugin_info[plugin_counter]->test_name);
	printf ("dagbits.c: %s - plugin_info[%d]->param_cnt: %d\n", __FUNCTION__, plugin_counter, plugin_info[plugin_counter]->param_cnt);	
	unsigned int i;
	for (i = 0; i < plugin_info[plugin_counter]->param_cnt; i++)
		printf ("dagbits.c: %s - plugin_info[%d]->params[%d]: %s\n", __FUNCTION__, plugin_counter, i, plugin_info[plugin_counter]->params[i]);
#endif

}


static void
anysig(int sig)
{
	run = 0;
}

static void
alarmsig(int sig)
{
	report();
}

static void
report(void)
{
	static struct timeval last;
	static int64_t lastoff, lastpc, pcdiff;
	struct timeval tdiff;
	uint64_t odiff;
	double rate, rps;

	gettimeofday(&now, NULL);
	if (0 == timerisset(&last))
	{
		/*
		 * Initial call
		 */
		last = now;
		return;
	}

	timersub(&now, &last, &tdiff);
	odiff = (uint64_t)(offset - lastoff);
	pcdiff = pc - lastpc;
	/*
	 * Bytes/microsecond == MBytes/second.
	 */
	rate = (double)odiff / (((double)tdiff.tv_sec * 1000 * 1000) + tdiff.tv_usec);
	rps = (double)pcdiff / ( (double)tdiff.tv_sec + ((double)tdiff.tv_usec/1000000) );
    
	if (0 != count)
	{
		fprintf(stderr, "Rate: %8.1f rec/s %.1f MB/s    Total: %"PRIu64" recs %.1f MB   \r", rps, rate, pc, (double)offset/1000000 );
	}
	
	lastoff = offset;
	lastpc = pc;
	last = now;

	if (tests & TEST_IDLE)
	{
	 
		idle_count++;
		if((idle_count % idle_interval) ==0)
		{
			idle_count = 0;

			if (rate_sum < 0.01 && rate < 0.01 && pcdiff == 0 ) 
			{
				rate_sum = 0;
				error_printf("idle %"PRIu64": file offset 0x%"PRIx64"\n", pc, offset);
				idle_fail++;
			}
		}
		else
			rate_sum += rate;
			
	}

	if ((1 == stop_when_idle) && (0 == rate))
	{
		run = 0;
	}

	passed_seconds ++;	
	if ((0 != seconds) && passed_seconds >= seconds)
	{
		run = 0;
	}
}

/*
 * We could be using lex and yacc for clarity, but wouldn't it be overkill ?
 */
typedef struct configtab {
	char * key;       /* the command */
	int mlen;         /* minimum match length */
	uint64_t inst;    /* instruction to be executed */
} configtab_t;

static configtab_t configtab[] =
{
	{ "print",   1,  TEST_PRINT   },
	{ "delta",   1,  TEST_DELTA   },
	{ "mono",    2,  TEST_MONO    },
	{ "ipsum",   3,  TEST_IP      },
	{ "rawsend", 1,  TEST_RAWSEND },
	{ "jitter",  1,  TEST_JITTER  },
	{ "ccell",   2,  TEST_CCELL   },
	{ "ciph",    2,  TEST_CIPH    },
	{ "ipid",    3,  TEST_IPID    },
	{ "btx",     1,  TEST_BTX     },
	{ "lfsr",    2,  TEST_LFSR    },
	{ "lctr",    2,  TEST_LCTR    },
	{ "cpacket", 2,  TEST_CPACKET },
	{ "cpacket2", 2,  TEST_CPACKET2 },
	{ "fcs",     3,  TEST_FCS     },
	{ "flags",   2,  TEST_FLAGS   },
	{ "offset",  1,  TEST_OFFSET  },
	{ "sig",     1,  TEST_SIG     },
	{ "wlen",    2,  TEST_WLEN    },
	{ "rlen",    2,  TEST_RLEN    },
	{ "dge",     2,  TEST_DGE     },
	{ "time",    2,  TEST_TIME    },
	{ "idle",    2,  TEST_IDLE    },
	{ "aal5",    1,  TEST_AAL5    },
	{ "bopt",    1,  TEST_BOPT    },
	{ "hdlccount", 1,  TEST_FLAGS }, /* backwards compatibility, merged with flags */
	{ "maal",    4,  TEST_MAAL    },
	{ "type",    2,  TEST_TYPE },
	{ "align64", 6,  TEST_ALIGN64 },
	{ "incr_raw_data", 2,  TEST_INCR_DATA},
	{ "incr_data", 2,  TEST_INCR_DATA},
	{ "ima_cnt", 7, TEST_IMA_CNT},
	{ "modts",   3, TEST_MODIFY_TIMESTAMPS },
	{ "steer",   4, TEST_STEERING },
	{ "decode",  3, TEST_DECODE },
	{ "tsvari",  4, TEST_DELTA_VARIANCE },
	{ "ipf_tb",  4, TEST_IPF_TP },
	{ "static_data", 2, TEST_STATIC_DATA},
	{ "sequence", 2, TEST_SEQUENCE},
    { "align", 2,  TEST_ALIGN },
	{  NULL,     0,  0 }
};


uint64_t
config(int argc, char *argv[])
{
	uint64_t tests = 0;
	int opt;
	int i;
	int len;
	char* p;

	for (opt = 0; opt < argc; opt++)
	{
		/*
		 * FIXME: might want to split up comma separated list at some stage.
		 */
		for (p = argv[opt]; ('\0' != *p) ; p++)
			*p = tolower(*p);
		
		p = argv[opt];
		for (i = 0; configtab[i].key != NULL; i++)
		{
			len = strlen(p);
			
			if (len < configtab[i].mlen)
				continue;
			
			if (0 == strncmp(p, configtab[i].key, strlen(p)))
				break;
		}
		if (configtab[i].key == NULL)
		{
			/* if it doesn't open then run dagutil_panic() */
			dagutil_panic("unsupported configuration option '%s'\nReminder: commandline options (-f 'filename', -vvc, etc.) must all come before the tests (print, align64, etc.).\n", argv[opt]);
		}
		tests |= configtab[i].inst;

	}

	if (tests & TEST_BOPT)
	{
		/* Check for presence of tests that should be run together with BOPT tests. */
		if (0 == (tests & TEST_IP))
		{
			dagutil_warning("bopt test enabled but IP checksum test (ipsum) disabled\n");
		}

		if (0 == (tests & TEST_IPID))
		{
			dagutil_warning("bopt test enabled but IP ID test (ipid) disabled\n");
		}

		if (0 == (tests & TEST_FCS))
		{
			dagutil_warning("bopt test enabled but Frame Checksum test (fcs) disabled\n");
		}
	}
	
	if (tests & TEST_IMA_CNT)
	{
		for (i = 0; i < MAX_CHANNELS; i++)
		{
			mc_ima_count[i].icp_cells = 0;
			mc_ima_count[i].idle_cells = 0;
			mc_ima_count[i].data_cells = 0;
		}
	}
	
	return tests;
}

static int
loop_file(void)
{
	static uint64_t lastoff[MAX_INTERFACES];
	static uint64_t lastind[MAX_INTERFACES];
	
	char    buf[4][MAXLEN-1000];
	char    *last_mc_rec = NULL;	
	char    hbuf[dag_record_size]; /* holds read-in header */
	char    *rec;
	int     retval = 0, ret;
	FILE    *infile = stdin;
	int     rsize;

	char	*temprec = malloc (MAXLEN);	/* Temporary storage for copy of current record */

	if (infilename)
	{
		dagutil_verbose("Reading input from %s\n", infilename);

#if defined(__FreeBSD__) ||  defined(__linux__) || defined(__NetBSD__) || (defined (__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

		infile = fopen(infilename, "rb");

#elif defined(_WIN32)

		infile = fopen(infilename, "r");
#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

		if (infile == NULL) 
			dagutil_panic("unable to open input file \"%s\"\n",infilename);
	}
	
	if (run)
	{
		last_mc_rec = dagutil_malloc((MAXLEN-1000)*(MAX_CHANNELS));
		assert(last_mc_rec != NULL);
		if (last_mc_rec == NULL)
			dagutil_panic("Memory could not be allocated\n");

		rsize = fread(hbuf, 1, dag_record_size, infile);
		if (rsize != dag_record_size) 
		{
			dagutil_verbose("%"PRIu64": record size %d < dag_record_size %d\n",
							pc, rsize, dag_record_size);
		}

		while (run && (rsize == dag_record_size)) 
		{

			len = reclen(hbuf);
			if (len == 20)
			{
				len = 28;
				printf("warning: len change 20->28\n");
			}

			intf = dir(hbuf);
			rec = &buf[intf][0];
		
			/* copy erf header to correct interface buffer. XXX performance */
			memcpy(rec, hbuf, dag_record_size);

			if (fread(&buf[intf][dag_record_size],1,len-dag_record_size, infile) != len-dag_record_size)
			{
				if(feof(infile))
				{
					/*if eof, exit gracefully,instead of panic */
					dagutil_warning("%"PRIu64": Error reading rest of last record. record length=%d\n", pc+1, len);
					break; 
				}
				else
				{
					dagutil_error("%"PRIu64": Error reading rest of record. record length=%d\n", pc+1, len);
					retval |= 1;
					break; 
				}
			}

			ret = dispatch_rec(rec, len);
		
			if (ret && dagutil_get_verbosity() &&
				!(max_print_errors && (total_errors > max_print_errors)))
			{
				if (0 == chan_fail)
				{
					print_rec(lastrec[intf], reclen(lastrec[intf]), lastind[intf], lastoff[intf]);
				}
				else
				{
					/* print the previous record*/
					print_rec(&last_mc_rec[chan_fail*(MAXLEN-1000)], reclen(&last_mc_rec[chan_fail*(MAXLEN-1000)]), lastchanindex[chan_fail], lastchanoffset[chan_fail]);
					/* save the current record as the previous for next pass through tests */
					/* because in the case of looping from a file the "record memory space" is 
					   constantly overwritten we cannot save a pointer to the space where the
					   last record is held, as it gets constantly overwritten.  This is not needed
					   when capturing from a device */
					memcpy(&last_mc_rec[chan_fail*(MAXLEN-1000)], rec, len);
					lastchanindex[chan_fail] = pc;
					lastchanoffset[chan_fail] = offset;
					chan_fail = 0;
				}
				print_rec(rec, len, pc, offset);
				printf("\n");
			}

			if ( error_lim )
			{
				errors += ret;
				if ( errors >= max_errors )
					run = 0;
			}
			offset += len;

			/* shuffle buffers */
			/* As mentioned in the comment just above: "in the case of looping from a file the "record memory space" is 
			   constantly overwritten we cannot save a pointer to the space where the last record is held, as it gets 
			   constantly overwritten". So create a temporary storage place for the current record and have it pointed 
			   to by the previous record pointer */
			memcpy(temprec, rec, len);
			lastrec[intf] = temprec;
			//lastrec[intf] = rec;
			lastind[intf] = pc;
			lastoff[intf] = offset;

			/*if multichannel record, record the record in the multichannel buffer */
			if (connection >= 0)
			{
				memcpy(&last_mc_rec[connection*(MAXLEN-1000)], rec, len);
				lastchanindex[connection] = pc;
				lastchanoffset[connection] = offset;
			}

			retval |= ret;

			rsize = fread(hbuf, 1, dag_record_size, infile);
			if (rsize != dag_record_size) /* read failed */
			{
				if (ferror(infile)) /* error condition */
					perror("Read error: ");
				if (!feof(infile)) /* if not eof, print */
					dagutil_verbose("%"PRIu64": record size %d < dag_record_size %d\n",
							pc, rsize, dag_record_size);
			}
		}

		if (last_mc_rec)
		{
			dagutil_free(last_mc_rec);
			last_mc_rec = NULL;
		}

		if (feof(infile))
		{
			dagutil_verbose("End of file reached                                                \n");
			if (test_numpkts)
			{
				if (actual_packets != expected_packets)
				{
					error_printf("%"PRId64": %"PRIu64" packets expected, %"PRIu64" received\n", 
							pc, expected_packets, actual_packets);
					retval |= 1;
				}
				return retval;
			}
		}
	}

	if (infile != stdin) 
		fclose(infile);


	return retval;
}

static int
loop_api(void)
{
	int     dagfd;
	uint64_t hold_idle;
	uint64_t   lastoff[MAX_INTERFACES], lastind[MAX_INTERFACES];
	char    *rec=NULL;
	void    *buf;
	uint32_t        bottom = 0, top, diff;
	int     retval = 0, ret, update=0;
	struct timeval  maxwait, poll;
	
	if ((dagfd = dag_open(dagname)) < 0)
		dagutil_panic("dag_open %s: %s\n", dagname, strerror(errno));
	

	if (dag_configure(dagfd, params) < 0)
		dagutil_panic("dag_configure %s: %s\n", dagname, strerror(errno));
	
	/* Setting reverse mode for using with soft dag from daemon side */
	if (0 != (dagstream & 0x01))
	{
		/* Setting reverse mode for using with soft dag from daemon side */
		if(dag_set_mode(dagfd, dagstream, DAG_REVERSE_MODE)) {
			dagutil_panic("Could not set reverse mode on %s:%d\n", dagname, dagstream);
		}
	};
	
	if ((buf = dag_mmap(dagfd)) == MAP_FAILED)
		dagutil_panic("dag_mmap %s: %s\n", dagname, strerror(errno));
	
	if (dag_start(dagfd) < 0)
		dagutil_panic("dag_start %s: %s\n", dagname, strerror(errno));
	
	if(no_pad){
		if(dag_set_param(dagfd, dagstream, DAG_FLUSH_RECORDS, &retval))
			dagutil_panic("dag_set_param %s: %s\n", dagname, strerror(errno));
	}

	/*
	 * Initialise DAG Polling parameters.
	 */
	timerclear(&maxwait);
	maxwait.tv_usec = 100 * 1000; /* 100ms timeout */
	timerclear(&poll);
	poll.tv_usec = 10 * 1000; /* 10ms poll interval */
	
	if ((numrxstreams = dag_rx_get_stream_count(dagfd)) < 0)
		dagutil_panic("dag_rx_get_stream_count for %u failed\n", dagfd);
	
	/* 32kB minimum data to return */
	dag_setpollparams(32*1024, &maxwait, &poll);
	if (wait_microsecs)
	{
		printf("Wait before capturing...\n");
		dagutil_microsleep(wait_microsecs);
	};
	
	while(run)
	{
	next_loop:
		top = dag_offset(dagfd, (int*) &bottom, 0);
	
		/* delay testing here so when there is no traffic, delays occur */
		if(timerisset(&next_event))
		{
			gettimeofday(&now, NULL);

			/* Windows does not support >= or <= for timercmp. */
			if (timercmp(&now, &next_event, >))
			{
				/* set next event time*/
				timeradd(&pause_period, &now, &next_event);

				/* 'turn off' the idle test temporarily during the pause so 
				 * idle errors are not reported in the report thread 
				 */
				hold_idle = tests & TEST_IDLE;
				tests &= ~TEST_IDLE;
				
				/* pause the packet processing */ 
				dagutil_microsleep(pause_time*1000);

				/* turn the idle test back on if necessary */
				tests |= hold_idle;

			}
		}


		diff = top - bottom;
		if (diff == 0)
		{
			continue;
		}
		assert(diff >= dag_record_size);

		while((run)&&((top-bottom)>=dag_record_size))
		{
			rec = (char*)buf+bottom;
			len = reclen(rec);
			intf = dir(rec);

			if (len == 20)
			{
				len = 28;
				printf("warning: len change 20->28\n");
			}

			if ((top-bottom) < (uint32_t)len)
				break;
		
			ret = dispatch_rec(rec, len);
			if (delay_nsecs)
				dagutil_nanosleep(delay_nsecs);

			if (ret && dagutil_get_verbosity() &&
			    !(max_print_errors && (total_errors > max_print_errors))) 
			{
				if (0 == chan_fail)
				{
					print_rec(lastrec[intf], reclen(lastrec[intf]), lastind[intf], lastoff[intf]);
				}
				else
				{
					print_rec(lastchanrec[chan_fail], reclen(lastchanrec[chan_fail]), lastchanindex[chan_fail], lastchanoffset[chan_fail]);
					lastchanrec[chan_fail] = rec;
					lastchanindex[chan_fail] = pc;
					lastchanoffset[chan_fail] = offset;
					chan_fail = 0;
				}
				print_rec(rec, len, pc, offset);
				printf("\n");
			}

			offset += len;
			if ( error_lim )
			{
				errors += ret;
				if ( errors >= max_errors )
					run = 0;
			}

			/* shuffle buffers except if this is a pad record and they are being ignored */
			if(count_pad || ((dag_record_t*)rec)->type != TYPE_PAD)
			{
				lastrec[intf] = rec;
				lastind[intf] = pc;
				lastoff[intf] = offset;
			}

			/*if multichannel record, record the record in the multichannel buffer */
			if (connection >= 0)
			{
				lastchanrec[connection] = rec;
				lastchanindex[connection] = pc;
				lastchanoffset[connection] = offset;
			}

			retval |= ret;

			bottom += len;
			
			if (update_max)
			{
				update++;
				if (update >= update_max)
				{
					update=0;
					goto next_loop;
				}
			}
		}
		if ( (tests & TEST_OFFSET) && (top-bottom) && (run) )
		{
			error_printf("%"PRId64": offset Bottom 0x%08x Top 0x%08x Diff %d\n",
						 pc, bottom, top, top-bottom);
			offset_fail++;
			retval |= 1;
			if ( error_lim )
			{
				errors++;
			}
		}

		if (test_numpkts && (actual_packets > expected_packets))
		{
			error_printf("%"PRId64": %"PRIu64" packets expected, %"PRIu64" received\n", 
						 pc, expected_packets, actual_packets);
			run = 0;
			retval |= 1;
		}
	}
	dag_stop(dagfd);
	dag_close(dagfd);

	if (test_numpkts && (actual_packets != expected_packets))
	{
		error_printf("%"PRId64": %"PRIu64" packets expected, %"PRIu64" received\n", 
			     pc, expected_packets, actual_packets);
		retval |= 1;
	}
	
	if ((tests & TEST_IDLE) && idle_fail)
		retval |= 1;

	return retval;
}

static int
loop_api_advance(void)
{
	int dagfd;
	uint64_t hold_idle = 0;
	uint64_t lastoff[MAX_INTERFACES], lastind[MAX_INTERFACES];
	char* rec=NULL;
	uint32_t diff;
	int retval = 0, ret, update=0;
	struct timeval maxwait, poll;
	uint8_t* bottom = NULL;
	uint8_t* top = NULL;
	uint8_t* virt_base;
	int phy_base;

	if ((dagfd = dag_open(dagname)) < 0)
		dagutil_panic("dag_open %s: %s\n", dagname, strerror(errno));

	if (dag_configure(dagfd, params) < 0)
		dagutil_panic("dag_configure %s: %s\n", dagname, strerror(errno));
	
	/* Setting reverse mode for using with soft dag from daemon side */
	if (0 != (dagstream & 0x01))
	{
		/* Setting reverse mode for using with soft dag from daemon side */
		if(dag_set_mode(dagfd, dagstream, DAG_REVERSE_MODE)) {
			dagutil_panic("Could not set reverse mode on %s:%d\n", dagname, dagstream);
		}
	};

	if (dag_attach_stream(dagfd, dagstream, 0, 0) < 0)
		dagutil_panic("dag_attach_stream %s:%u: %s\n", dagname, dagstream, strerror(errno));

	virt_base = dag_get_stream_buffer_virt_base_address(dagfd, dagstream);
	phy_base = dag_get_stream_phy_buffer_address(dagfd, dagstream);

	if (dag_start_stream(dagfd, dagstream) < 0)
		dagutil_panic("dag_start_stream %s:%u: %s\n", dagname, dagstream, strerror(errno));

	if(no_pad){
		if(dag_set_param(dagfd, dagstream, DAG_FLUSH_RECORDS, &retval))
			dagutil_panic("dag_set_param %s: %s\n", dagname, strerror(errno));
	}
	/*
	 * Initialise DAG Polling parameters.
	 */
	timerclear(&maxwait);
	maxwait.tv_usec = 100 * 1000; /* 100ms timeout */
	timerclear(&poll);
	poll.tv_usec = 10 * 1000; /* 10ms poll interval */
	
	if ((numrxstreams = dag_rx_get_stream_count(dagfd)) < 0)
		dagutil_panic("dag_rx_get_stream_count for %u failed\n", dagfd);
	
	/* 32kB minimum data to return */
	dag_set_stream_poll(dagfd, dagstream, 32*1024, &maxwait, &poll);

	if (wait_microsecs)
	{
		printf("Wait before capturing...\n");
		dagutil_microsleep(wait_microsecs);
	};

	while(run)
	{
	next_loop:
		top = dag_advance_stream(dagfd, dagstream, &bottom);

		if (NULL == top)
		{
			if((dagstream & 0x1) && (errno == EAGAIN)) {
				usleep(10 * 1000);
				bottom = top;
				continue;
			}
			else {
				dagutil_panic("dag_advance_stream %s:%u: %s\n", dagname, dagstream, strerror(dag_get_last_error()));
		        }
		}

		/* delay testing here so when there is no traffic, delays occur */
		if(timerisset(&next_event))
		{
			gettimeofday(&now, NULL);

			/* Windows does not support >= or <= for timercmp. */
			if (timercmp(&now, &next_event, >))
			{
				/* set next event time*/
				timeradd(&pause_period, &now, &next_event);

				/* 'turn off' the idle test temporarily during the pause so 
				 * idle errors are not reported in the report thread 
				 */
				hold_idle = tests & TEST_IDLE;
				tests &= ~TEST_IDLE;
				
				/* pause the packet processing */ 
				dagutil_microsleep(pause_time*1000);

				/* turn the idle test back on if necessary */
				tests |= hold_idle;

			}
		}

		diff = top - bottom;
		if (diff == 0)
		{
			continue;
		}
		assert(diff >= dag_record_size);

		while((run)&&((top-bottom)>=dag_record_size))
		{
			rec = (char*)bottom;
			len = reclen(rec);
			intf = dir(rec);

			if (len == 20) {
				len = 28;
				printf("warning: len change 20->28\n");
			}

			if ((top-bottom) < len)
				break;
		
			ret = dispatch_rec(rec, len);
			if (delay_nsecs)
				dagutil_nanosleep(delay_nsecs);

			if (ret && dagutil_get_verbosity() &&
			    !(max_print_errors && (total_errors > max_print_errors)))
			{
				if (0 == chan_fail)
				{
					print_rec(lastrec[intf], reclen(lastrec[intf]), lastind[intf], lastoff[intf]);
				}
				else
				{
					print_rec(lastchanrec[chan_fail], reclen(lastchanrec[chan_fail]), lastchanindex[chan_fail], lastchanoffset[chan_fail]);
					lastchanrec[chan_fail] = rec;
					lastchanindex[chan_fail] = pc;
					lastchanoffset[chan_fail] = offset;
					chan_fail = 0;
				}
				print_rec(rec, len, pc, offset);
				printf("\n");
			}

			offset += len;
			if ( error_lim )
			{
				errors += ret;
				if ( errors >= max_errors )
					run = 0;
			}

			/* shuffle buffers */
			if(count_pad || ((dag_record_t*)rec)->type != TYPE_PAD)
			{
				lastrec[intf] = rec;
				lastind[intf] = pc;
				lastoff[intf] = offset;
			}

			/*if multichannel record, record the record in the multichannel buffer */
			if (connection >= 0)
			{
				lastchanrec[connection] = rec;
				lastchanindex[connection] = pc;
				lastchanoffset[connection] = offset;
			}

			retval |= ret;

			bottom += len;
			
			if (update_max)
			{
				update++;
				if (update >= update_max){
					update=0;
					goto next_loop;
				}
			}

		}
		if ( (tests & TEST_OFFSET) && (top-bottom) && (run) )
		{
			error_printf("%"PRId64": offset Bottom %08p Top %08p Diff %d (0x%08x - 0x%08x = 0x%x)\n",
				     pc, bottom, top, top-bottom,
				     (uint32_t)((top - virt_base)&0xffffffff)+phy_base,
				     (uint32_t)((bottom - virt_base)&0xffffffff)+phy_base,
				     diff);
			offset_fail++;
			retval |= 1;
			if ( error_lim )
			{
				errors++;
			}
		}

		if (test_numpkts && (actual_packets > expected_packets))
		{
			error_printf("%"PRId64": %"PRIu64" packets expected, %"PRIu64" received\n", 
						 pc, expected_packets, actual_packets);
			run = 0;
			retval |= 1;
		}

	}
	dag_stop_stream(dagfd, dagstream);
	dag_detach_stream(dagfd, dagstream);
	dag_close(dagfd);

	if (test_numpkts && (actual_packets != expected_packets))
	{
		error_printf("%"PRId64": %"PRIu64" packets expected, %"PRIu64" received\n", 
			     pc, expected_packets, actual_packets);
		retval |= 1;
	}
	

	if ((tests & TEST_IDLE) && idle_fail)
		retval |= 1;

	return retval;
}

static int
loop_api_next(void)
{
	int dagfd;
	uint64_t hold_idle;
	uint64_t lastoff[MAX_INTERFACES], lastind[MAX_INTERFACES];
	char * rec=NULL;
	int retval = 0, ret;
	struct timeval maxwait, poll;

	if (tests & TEST_OFFSET)
		dagutil_panic("offset test not supported under dag_rx_stream_next_record API (-i 2 or --api 2)\n");

	if ((dagfd = dag_open(dagname)) < 0)
		dagutil_panic("dag_open %s: %s\n", dagname, strerror(errno));

	if (dag_configure(dagfd, params) < 0)
		dagutil_panic("dag_configure %s: %s\n", dagname, strerror(errno));
	
	/* Setting reverse mode for using with soft dag from daemon side */
	if (0 != (dagstream & 0x01))
	{
		/* Setting reverse mode for using with soft dag from daemon side */
		if(dag_set_mode(dagfd, dagstream, DAG_REVERSE_MODE)) {
			dagutil_panic("Could not set reverse mode on %s:%d\n", dagname, dagstream);
		}
	};

	if (dag_attach_stream(dagfd, dagstream, 0, 0) < 0)
		dagutil_panic("dag_attach_stream %s:%u: %s\n", dagname, dagstream, strerror(errno));

	if (dag_start_stream(dagfd, dagstream) < 0)
		dagutil_panic("dag_start_stream %s:%u: %s\n", dagname, dagstream, strerror(errno));

	if(no_pad){
		if(dag_set_param(dagfd, dagstream, DAG_FLUSH_RECORDS, &retval))
			dagutil_panic("dag_set_param %s: %s\n", dagname, strerror(errno));
	}
	/*
	 * Initialise DAG Polling parameters.
	 */
	timerclear(&maxwait);
	maxwait.tv_usec = 100 * 1000; /* 100ms timeout */
	timerclear(&poll);
	poll.tv_usec = 10 * 1000; /* 10ms poll interval */

	/* 32kB minimum data to return */
	dag_set_stream_poll(dagfd, dagstream, 32*1024, &maxwait, &poll);

	if (wait_microsecs)
	{
		printf("Wait before capturing...\n");
		dagutil_microsleep(wait_microsecs);
	};

	while(run)
	{
		rec = (char*)dag_rx_stream_next_record(dagfd, dagstream);
		if (rec) {
			len = reclen(rec);
			intf = dir(rec);
			
			if (len == 20)
			{
				len = 28;
				printf("warning: len change 20->28\n");
			}
						
			ret = dispatch_rec(rec, len);
			if (delay_nsecs)
				dagutil_nanosleep(delay_nsecs);
			
			if (ret && dagutil_get_verbosity() &&
			    !(max_print_errors && (total_errors > max_print_errors)))
			{
				if (0 == chan_fail)
				{
					print_rec(lastrec[intf], reclen(lastrec[intf]), lastind[intf], lastoff[intf]);
				}
				else
				{
					print_rec(lastchanrec[chan_fail], reclen(lastchanrec[chan_fail]), lastind[intf], lastoff[intf]);
					lastchanrec[chan_fail] = rec;
					lastchanindex[chan_fail] = pc;
					lastchanoffset[chan_fail] = offset;
					chan_fail = 0;
				}
				print_rec(rec, len, pc, offset);
				printf("\n");
			}
			
			offset += len;
			if ( error_lim )
			{
				errors += ret;
				if ( errors >= max_errors )
					run = 0;
			}
			
			/* shuffle buffers */
			if(count_pad || ((dag_record_t*)rec)->type != TYPE_PAD)
			{
				lastrec[intf] = rec;
				lastind[intf] = pc;
				lastoff[intf] = offset;
			}

			/*if multichannel record, record the record in the multichannel buffer */
			if (connection >= 0)
			{
				lastchanrec[connection] = rec;
				lastchanindex[connection] = pc;
				lastchanoffset[connection] = offset;
			}

			retval |= ret;
			
			if (test_numpkts && (actual_packets > expected_packets))
			{
				error_printf("%"PRId64": %"PRIu64" packets expected, %"PRIu64" received\n", 
							 pc, expected_packets, actual_packets);
				run = 0;
				retval |= 1;
			}

		}
		else
		{ /* rec == NULL */
			int last_error = dag_get_last_error();
			
			if (last_error != EAGAIN)
				dagutil_panic("dag_rx_stream_next_record: %s\n", strerror(last_error));
		}

		if(timerisset(&next_event))
		{
			gettimeofday(&now, NULL);

			/* Windows does not support >= or <= for timercmp. */
			if (timercmp(&now, &next_event, >))
			{
				/* set next event time*/
				timeradd(&pause_period, &now, &next_event);

				/* 'turn off' the idle test temporarily during the pause so 
				 * idle errors are not reported in the report thread 
				 */
				hold_idle = tests & TEST_IDLE;
				tests &= ~TEST_IDLE;
				
				/* pause the packet processing */ 
				dagutil_microsleep(pause_time*1000);

				/* turn the idle test back on if necessary */
				tests |= hold_idle;

			}
		}
	}
	dag_stop_stream(dagfd, dagstream);
	dag_detach_stream(dagfd, dagstream);
	dag_close(dagfd);

	if (test_numpkts && (actual_packets != expected_packets))
	{
		error_printf("%"PRId64": %"PRIu64" packets expected, %"PRIu64" received\n", 
		     pc, expected_packets, actual_packets);
		retval |= 1;
	}
	
	if ((tests & TEST_IDLE) && idle_fail)
		retval |= 1;

	return retval;
}

static int
dispatch_rec(char *rec, int len)
{
	int ret = 0;

	/* Handle multiple tests for plugins */
	unsigned int counter = 0;
	int result = 0;
	char err_msg[MAX_STR_LEN] = "ERROR!";
	static unsigned int pass_cnt = 0;
	static unsigned int fail_cnt = 0;
	static unsigned int warning_cnt = 0;
	unsigned int return_val = 0;
	int iface;

	pc++;

	if(!count_pad && ((dag_record_t*)rec)->type == TYPE_PAD)
	{
		pc--;
		return 0;
	}
	else if ( check_type )
	{
		if (! allow_erf_type_array[((dag_record_t*)rec)->type])
		{
			error_printf("erf %"PRIu64": type %u != %u\n", pc, ((dag_record_t*)rec)->type,
						 allow_type); 
			ret = 1;
		}
	}

	actual_packets++;

	iface = ((dag_record_t*)rec)->flags.iface;
	packet_count[iface]++;
	rlen_count[iface] += ntohs(((dag_record_t*)rec)->rlen);
	wlen_count[iface] += ntohs(((dag_record_t*)rec)->wlen);
	
	if(first_ts[iface] == 0)
		first_ts[iface] = ((dag_record_t*)rec)->ts;
	
	last_ts[iface] = ((dag_record_t*)rec)->ts;
    if (((dag_record_t*)rec)->type != TYPE_PAD) 
        packet_erf_type[iface] = rec[8] & 0x7f;
    
	while (plugin_info[counter]->test_name != NULL)
	{

#if 0		/* Testing only - remove later */
		printf ("dagbits.c: %s - plugin_info[%d]->test_name: %s\n", __FUNCTION__, plugin_counter, plugin_info[plugin_counter]->test_name);
		printf ("dagbits.c: %s - plugin_info[%d]->param_cnt: %d\n", __FUNCTION__, plugin_counter, plugin_info[plugin_counter]->param_cnt);	
		unsigned int i;
		for (i = 0; i < plugin_info[plugin_counter]->param_cnt; i++)
			printf ("dagbits.c: %s - plugin_info[%d]->params[%d]: %s\n", __FUNCTION__, plugin_counter, i, plugin_info[plugin_counter]->params[i]);
#endif

		result = plugin_info[counter]->plugin_run(rec, len, lastrec[intf], protocol, actual_packets);
		
		if (result == TEST_PASS)
		{
			++pass_cnt;
			return_val = 0;
		}
		
		if (result == TEST_FAIL)
		{
			plugin_info[counter]->plugin_err_msg(err_msg, MAX_STR_LEN);
			printf ("%s %"PRIu64": %s\n", plugin_info[counter]->test_name, actual_packets, err_msg);
			++fail_cnt;
			return_val = 1;
		}

		if (result == TEST_WARNING)
		{
			++warning_cnt;
			
			if (warning)
			{
				plugin_info[counter]->plugin_err_msg(err_msg, MAX_STR_LEN);
				printf ("%s: %s\n", plugin_info[counter]->test_name, err_msg);
				return_val = 1;
			}
			else
				return_val = 0;
		}

		ret |= return_val;
		++counter;
	}

		
	if ((tests & TEST_DECODE) || (tests & TEST_PRINT))
		print_rec(rec, len, pc, offset);
	
	/* Check if we should be waiting for so many records before testing */
	if ( (delay_test_count != -1) && (actual_packets < delay_test_count) )
		return ret;
	
	if (tests & TEST_DELTA)
		ret |= print_delta(rec, len);
	
	if (tests & TEST_MONO)
		ret |= test_mono(rec, len);
	
	if (tests & TEST_FLAGS)
		ret |= test_flags(rec, len);

	if (tests & TEST_IP)
		ret |= test_ip(rec, len);
	
	if (tests & TEST_RAWSEND)
		ret |= test_rawsend(rec, len);
	
	if (tests & TEST_JITTER)
		ret |= test_jitter(rec, len);
	
	if (tests & TEST_CCELL)
		ret |= test_ccell(rec, lastrec[intf]);
	
	if (tests & TEST_CIPH)
		ret |= test_ciph(rec, lastrec[intf]);
	
	if (tests & TEST_IPID)
		ret |= test_ipid(rec, lastrec[intf]);

	if (tests & TEST_BTX)
		ret |= test_btx(rec, len);

	if (tests & TEST_LFSR)
		ret |= test_lfsr(rec, len);

	if (tests & TEST_LCTR)
		ret |= test_lctr(rec, len);

	if (tests & TEST_FCS)
		ret |= test_fcs(rec, len);

	/* TESTING!!! */
	if (tests & TEST_CPACKET)
	//{
	//	printf ("\nFUNCTION: %s, Rec pointer: %p, Last rec pointer: %p\n", __FUNCTION__, rec, lastrec[intf]);
		ret |= test_cpacket(rec, lastrec[intf]);
	//}

	if (tests & TEST_CPACKET2)
		ret |= test_cpacket2(rec, lastrec[intf]);

	if (tests & TEST_SIG)
		ret |= test_sig(rec, len);

	if (tests & TEST_WLEN)
		ret |= test_wlen(rec, len);

	if (tests & TEST_RLEN)
		ret |= test_rlen(rec, len);

	if (tests & TEST_DGE)
		ret |= test_dge(rec, lastrec[intf]);

	if (tests & TEST_TIME)
		ret |= test_time(rec, len);

	if (tests & TEST_AAL5)
		ret |= test_aal5(rec, len);

	if (tests & TEST_BOPT)
		ret |= test_bopt(rec, len);

	if (tests & TEST_TYPE)
		ret |= test_type(rec, len);

	if (tests & TEST_ALIGN64)
		ret |= test_align64(rec, len);

	if (tests & TEST_INCR_DATA)
		ret |= test_incr_data(rec, len);
	
	if (tests & TEST_IMA_CNT)
		ret |= test_ima_cnt(rec, len);
	
	if (tests & TEST_MODIFY_TIMESTAMPS )
		ret |= test_modify_timestamps(rec, len);
	
	if (tests & TEST_STEERING )
		ret |= test_steering(rec, len );

	if (tests & TEST_MAAL )
		ret |= test_maal(rec, len);

	if (tests & TEST_DELTA_VARIANCE )
		ret |= test_vci_delta_variance(rec, len);

	if (tests & TEST_IPF_TP)
		ret |= test_ipf_tp(rec, len);

	if (tests & TEST_STATIC_DATA)
		ret |= test_static_data(rec, len);
	
	if (tests & TEST_SEQUENCE)
		ret |= test_sequence(rec, len);
    if (tests & TEST_ALIGN)
        ret |= test_align(rec, len);


	if (ret)
		total_errors++;

	return ret;
}


static void
print_ext_headers_network_rate (uint32_t rate)
{
	switch (rate)
	{
		case 0x0:
			printf ("oc1/stm0 ");
			break;
		case 0x1:
			printf ("oc3/stm1 ");
			break;
		case 0x2:
			printf ("oc12/stm4 ");
			break;
		case 0x3:
			printf ("oc48/stm16 ");
			break;
		case 0x4:
			printf ("oc192/stm64 ");
			break;
		case 0x5:
			printf ("oc768/stm256 ");
			break;
		case 0x6:
			printf ("ds3 ");
			break;
		default:
			printf ("unknown ");
			break;
	}
}

static void
print_ext_headers_network_type (uint32_t type)
{
	switch (type)
	{
		case 0x0:
			printf ("raw_sonet ");
			break;
		case 0x1:
			printf ("raw_sdh ");
			break;
		case 0x2:
			printf ("sonet_spe ");
			break;
		case 0x3:
			printf ("sdh_spe ");
			break;
		case 0x4:
			printf ("ds3_cbit ");
			break;
		case 0x5:
			printf ("sonet_spe_w/o_poh ");
			break;
		case 0x6:
			printf ("sonet_spe_w/_poh ");
			break;
		case 0x7:
			printf ("sonet_line_mode_2 ");
			break;
		case 0x8:
			printf ("sdh_line_mode_2 ");
			break;
		case 0x9:
			printf ("raw_bits_unaligned ");
			break;
		case 0xa:
			printf ("raw_10ge_66b ");
			break;
		default:
			printf ("unknown ");
			break;
	}
}

static void
print_ext_headers_sonet_channel_size (uint32_t size)
{
	switch (size)
	{
		case 0x0:
			printf ("oc1/sts1/vc3 ");
			break;
		case 0x1:
			printf ("oc3c/sts3/vc4 ");
			break;
		case 0x2:
			printf ("oc12c/sts12/vc4-4c ");
			break;
		case 0x3:
			printf ("oc48c/sts48/vc4-16c ");
			break;
		case 0x4:
			printf ("oc192c/sts192/vc4-64c ");
			break;
		case 0x5:
			printf ("oc768/sts768/vc4-256c ");
			break;
		default:
			printf ("unknown ");
			break;
	}
}


/**
 * This function prints the DAG2 extension header fields out to stdout, it
 * is called by the print_rec function when an extension header is 
 * detected in a packet. This function assumes that the existance of the
 * DAG2 extension headers has already been verified.
 *
 * @param[in]  rec_p     Pointer to the complete ERF record.
 * @param[in]  len       The length of the complete ERF record in bytes.
 *
 */
static void
print_ext_headers(uint8_t *rec_p, int len)
{
	uint64_t hdr;
	uint8_t  hdr_type;
	uint32_t hdr_num;
    int i = 0;
    uint8_t *dump_ptr = rec_p;


	/* sanity check we have enough bytes for at least one header */
	if ( len < 24 )
		return;


	/* loop over the extension headers */
	hdr_num = 0;
	do {
	
		/* sanity check we have enough bytes */
		if ( len <= (24 + (hdr_num * 8)) )
			return;

		/* get the header type */
		hdr_type = rec_p[(16 + (hdr_num * 8))];
		
		/* get the header value */
		hdr = *((uint64_t*) (&rec_p[(16 + (hdr_num * 8))]));
		hdr = bswap_64(hdr);

		/* display the header */
		switch ( hdr_type & 0x7F )
		{
			case EXT_HDR_TYPE_CLASSIFICATION:
				printf ("np40g1 header=0x%016"PRIx64" { match=%u multi_match=%u tag=%u drop=%u class=%u }\n",
					(uint64_t)(hdr),
					(uint32_t)((hdr >> 55) & 0x0001),
					(uint32_t)((hdr >> 54) & 0x0001),
					(uint32_t)((hdr >> 36) & 0xffff),
					(uint32_t)((hdr >> 34) & 0x0001),
					(uint32_t)((hdr >> 32) & 0x0003));
				break;

			case EXT_HDR_TYPE_RAW_LINK:
				printf ("raw link header=0x%016"PRIx64" { more_fragment=%u ",
					(uint64_t)(hdr),
				        (uint32_t)((hdr >> 55) & 0x0001));
				printf ("sequence_number=%u ", (uint32_t)((hdr >> 16) & 0xffff));
				printf ("vc_id=%u ", (uint32_t)((hdr >> 32) & 0x3ff));
				printf ("rate=");
				print_ext_headers_network_rate((uint32_t)((hdr >> 8) & 0x00ff));
				printf ("type=");
				print_ext_headers_network_type((uint32_t)(hdr & 0x00ff));
				printf ("}\n");
				break;

			case EXT_HDR_TYPE_BFS:
				printf ("bfs header=0x%016"PRIx64"\n{ color=0x%04x hash=0x%02x raw_hash=0x%08x }\n",
					(uint64_t)(hdr),
					(uint32_t)((hdr >> 32) & 0x0ffff),
					(uint32_t)((hdr >> 48) & 0xff),
					(uint32_t)(hdr & 0xffffffff));
				break;

			case EXT_HDR_TYPE_SIGNATURE:
				printf ("signature header=0x%016"PRIx64"\n{ color=0x%02x payload_hash=0x%06x flow_hash=0x%06x }\n",
					(uint64_t)(hdr),
					(uint32_t)((hdr >> 24) & 0xff),
					(uint32_t)((hdr >> 32) & 0x0ffffff),
					(uint32_t)(hdr & 0xffffff));
				break;

			case EXT_HDR_TYPE_SDH_FRAME_INFO:
				printf ("sdh frame info header=0x%016"PRIx64"\n{ ", (uint64_t)(hdr));
				printf ("type=");
				switch(((uint32_t)((hdr >> 31) & 0x0001)))
				{
					case 0x0:
						printf ("sonet ");
						break;
					case 0x1:
						printf ("sdh ");
						break;
					default:
						printf ("unknown ");
						break;
				}
				printf ("rate=");
				print_ext_headers_network_rate((uint32_t)((hdr >> 28) & 0x0007));
				printf ("flags[rdi-l:%u rdi-p:%u rei-l:%u rei-p:%u ais-l:%u ais-p:%u oof:%u lof:%u lop:%u los:%u] ",
					(uint32_t)((hdr >> 20) & 0x0001),
					(uint32_t)((hdr >> 18) & 0x0001),
					(uint32_t)((hdr >> 21) & 0x0001),
					(uint32_t)((hdr >> 19) & 0x0001),
					(uint32_t)((hdr >> 22) & 0x0001),
					(uint32_t)((hdr >> 27) & 0x0001),
					(uint32_t)((hdr >> 26) & 0x0001),
					(uint32_t)((hdr >> 25) & 0x0001),
					(uint32_t)((hdr >> 24) & 0x0001),
					(uint32_t)((hdr >> 23) & 0x0001));
				printf ("valid_b1/b2=%u ", (uint32_t)((hdr >> 16) & 0x0001));
				printf ("b1_errors_counter=%u b1_error_event=%u ",
					(uint32_t)((hdr >> 12) & 0x000f),
					(uint32_t)((hdr >> 11) & 0x0001));
				printf ("b2_errors_counter=%u b2_error_event=%u ",
					(uint32_t)((hdr >> 4) & 0x007f),
					(uint32_t)(hdr & 0x0001));
				printf ("}\n");
				break;

			case EXT_HDR_TYPE_SPE_FRAME_INFO:
				printf ("spe frame info header=0x%016"PRIx64"\n{ ", (uint64_t)(hdr));
				printf ("last_segment=%u sequence_number=%u b3=0x%x bip8=0x%x ptr_activity=%u ",
					(uint32_t)((hdr >> 55) & 0x0001),
					(uint32_t)((hdr >> 48) & 0x007f),
					(uint32_t)((hdr >> 40) & 0x00ff),
					(uint32_t)((hdr >> 32) & 0x00ff),
					(uint32_t)((hdr >> 29) & 0x0007));
				printf ("vc_size=");
				print_ext_headers_sonet_channel_size((uint32_t)((hdr >> 26) & 0x0007));
				printf ("vc_id=%u ", (uint32_t)((hdr >> 16) & 0x03ff));
				printf ("h1=0x%02x h2=0x%02x ",
					(uint32_t)((hdr >> 8) & 0x00ff),
					(uint32_t)(hdr & 0x00ff));
				printf ("}\n");
				break;

			case EXT_HDR_TYPE_DS3_RAW_LINK:
				printf ("ds3 raw link header=0x%016"PRIx64"\n{ ", (uint64_t)(hdr));
				printf ("bip8=0x%02x bip1=%u ",
					(uint32_t)((hdr >> 48) & 0x00ff),
					(uint32_t)((hdr >> 40) & 0x0001));
				printf ("cp1=%u cp2=%u cp3=%u p1=%u p2=%u ",
					(uint32_t)((hdr >> 39) & 0x0001),
					(uint32_t)((hdr >> 38) & 0x0001),
					(uint32_t)((hdr >> 37) & 0x0001),
					(uint32_t)((hdr >> 36) & 0x0001),
					(uint32_t)((hdr >> 35) & 0x0001));
				printf ("bip64(63:56)=0x%02x ", (uint32_t)((hdr >> 16) & 0x00ff));
				printf ("match_flag=%u match_ptr=%u ",
					(uint32_t)((hdr >> 31) & 0x0001),
					(uint32_t)(hdr & 0x0fff));
				printf ("}\n");
				break;

			case EXT_HDR_TYPE_DS3_OVERHEAD:
				printf ("ds3 overhead header=0x%016"PRIx64"\n", (uint64_t)(hdr));
				printf ("M-subframe1: x=%u f1=%u aic=%u f0=%u na=%u f0=%u feac=%u f1=%u\n",
					(uint32_t)((hdr >> 55) & 0x0001),
					(uint32_t)((hdr >> 54) & 0x0001),
					(uint32_t)((hdr >> 53) & 0x0001),
					(uint32_t)((hdr >> 52) & 0x0001),
					(uint32_t)((hdr >> 51) & 0x0001),
					(uint32_t)((hdr >> 50) & 0x0001),
					(uint32_t)((hdr >> 49) & 0x0001),
					(uint32_t)((hdr >> 48) & 0x0001));
				printf ("M-subframe2: x=%u f1=%u dl=%u f0=%u dl=%u f0=%u dl=%u f1=%u\n",
					(uint32_t)((hdr >> 47) & 0x0001),
					(uint32_t)((hdr >> 46) & 0x0001),
					(uint32_t)((hdr >> 45) & 0x0001),
					(uint32_t)((hdr >> 44) & 0x0001),
					(uint32_t)((hdr >> 43) & 0x0001),
					(uint32_t)((hdr >> 42) & 0x0001),
					(uint32_t)((hdr >> 41) & 0x0001),
					(uint32_t)((hdr >> 40) & 0x0001));
				printf ("M-subframe3: p=%u f1=%u cp=%u f0=%u cp=%u f0=%u cp=%u f1=%u\n",
					(uint32_t)((hdr >> 39) & 0x0001),
					(uint32_t)((hdr >> 38) & 0x0001),
					(uint32_t)((hdr >> 37) & 0x0001),
					(uint32_t)((hdr >> 36) & 0x0001),
					(uint32_t)((hdr >> 35) & 0x0001),
					(uint32_t)((hdr >> 34) & 0x0001),
					(uint32_t)((hdr >> 33) & 0x0001),
					(uint32_t)((hdr >> 32) & 0x0001));
				printf ("M-subframe4: p=%u f1=%u febe=%u f0=%u febe=%u f0=%u febe=%u f1=%u\n",
					(uint32_t)((hdr >> 31) & 0x0001),
					(uint32_t)((hdr >> 30) & 0x0001),
					(uint32_t)((hdr >> 29) & 0x0001),
					(uint32_t)((hdr >> 28) & 0x0001),
					(uint32_t)((hdr >> 27) & 0x0001),
					(uint32_t)((hdr >> 26) & 0x0001),
					(uint32_t)((hdr >> 25) & 0x0001),
					(uint32_t)((hdr >> 24) & 0x0001));
				printf ("M-subframe5: m0=%u f1=%u dl=%u f0=%u dl=%u f0=%u dl=%u f1=%u\n",
					(uint32_t)((hdr >> 23) & 0x0001),
					(uint32_t)((hdr >> 22) & 0x0001),
					(uint32_t)((hdr >> 21) & 0x0001),
					(uint32_t)((hdr >> 20) & 0x0001),
					(uint32_t)((hdr >> 19) & 0x0001),
					(uint32_t)((hdr >> 18) & 0x0001),
					(uint32_t)((hdr >> 17) & 0x0001),
					(uint32_t)((hdr >> 16) & 0x0001));
				printf ("M-subframe6: m1=%u f1=%u dl=%u f0=%u dl=%u f0=%u dl=%u f1=%u\n",
					(uint32_t)((hdr >> 15) & 0x0001),
					(uint32_t)((hdr >> 14) & 0x0001),
					(uint32_t)((hdr >> 13) & 0x0001),
					(uint32_t)((hdr >> 12) & 0x0001),
					(uint32_t)((hdr >> 11) & 0x0001),
					(uint32_t)((hdr >> 10) & 0x0001),
					(uint32_t)((hdr >> 9) & 0x0001),
					(uint32_t)((hdr >> 8) & 0x0001));
				printf ("M-subframe7: m0=%u f1=%u dl=%u f0=%u dl=%u f0=%u dl=%u f1=%u\n",
					(uint32_t)((hdr >> 7) & 0x0001),
					(uint32_t)((hdr >> 6) & 0x0001),
					(uint32_t)((hdr >> 5) & 0x0001),
					(uint32_t)((hdr >> 4) & 0x0001),
					(uint32_t)((hdr >> 3) & 0x0001),
					(uint32_t)((hdr >> 2) & 0x0001),
					(uint32_t)((hdr >> 1) & 0x0001),
					(uint32_t)(hdr & 0x0001));
				break;

			case EXT_HDR_TYPE_DS3_CALCULATED_BIP64:
				printf ("ds3 calculated bip64 header=0x%016"PRIx64"\n{ ", (uint64_t)(hdr));
				printf ("bip64(55:0)=0x%014"PRIx64" }\n", (hdr & (uint64_t)0xffffffffffffffULL));
				break;

			default:
				printf ("ext header:  Unknown extension header type (0x%02X)\n", (hdr_type & 0x7F));
				dump_ptr = (&rec_p[(16 + (hdr_num * 8))]) ;
				printf("0x");
				for ( i = 0; i < 8 ; i++)
				{
					printf("%02x ", dump_ptr[i]);
				}
				printf("\n");
				break;
		}

		hdr_num++;
	
	} while ( hdr_type & 0x80 );
	
}


static void
print_rec(char *rec, int len, uint64_t index, uint64_t boffset)
{
	int i;
	int errs;
	time_t time;
	struct tm* dtime;
	char dtime_str[80];
	dag_record_t* drec = (dag_record_t*) rec;
	legacy_atm_t* arec = (legacy_atm_t*) rec;
	legacy_eth_t* erec = (legacy_eth_t*) rec;
	legacy_pos_t* prec = (legacy_pos_t*) rec;
	tcp_ip_counter_t* tcp_ip_counter = (tcp_ip_counter_t*)rec;
	uint8_t* pload = NULL;
	char abuf[17];
	uint32_t atm_header;
	unsigned int num_ext_hdrs;
	uint8_t rec_type;
	int adj_len = 0;
	uint8_t* tp;

	if ( (!rec) || (!index) || (!len) ||
		(max_print_errors && (total_errors > max_print_errors)) )
		return;

	printf("print %"PRIu64": file offset 0x%"PRIx64"\n", index, boffset);
	time = (time_t)(TS(drec->ts) >> 32);

	dtime=gmtime((time_t *)(&time));
	if (dtime == NULL)
	{
		printf("Error in timestamp time: %"PRIu64"\n", (uint64_t) time);
	}
	else
	{
		/* Note: windows does not support %T - use %H:%M:%S instead. */
		strftime(dtime_str, sizeof(dtime_str), "%Y-%m-%d %H:%M:%S", dtime);

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

		printf("ts=0x%.16"PRIx64" %s.%09.0f UTC\n",
			TS(drec->ts),
			dtime_str,
			(double)(TS(drec->ts) & 0xffffffffll) / 0x100000000ll * 1000000000);

#elif defined(_WIN32)

	/* Printf under windows crashes when trying to print a large integer, 
	 * so we have to split ts in two 32-bit integers
	 */
		printf("ts=0x%.16I64x %s.%09.0f UTC\n",
			TS(drec->ts),
			dtime_str,
			(double)(ULONG)(TS(drec->ts) & 0xffffffff) / 0x100000000i64 * 1000000000);

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */
	}
	printf("type: %s\n", dagerf_type_to_string(drec->type, tt));

	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		switch (tt) {
		case TT_ATM:
			printf("crc=0x%.8x atm=0x%.8x gfc=%2u vpi=%3u vci=%5u pti=%1u clp=%1u\n",
				(int32_t) ntohl(arec->crc),
				(int32_t) ntohl(arec->header),
				(uint32_t) ntohl(arec->header) >> 28,
				(uint32_t) (ntohl(arec->header) >> 20) & 0xff,
				(uint32_t) (ntohl(arec->header) >> 4) & 0xffff,
				(uint32_t) (ntohl(arec->header) >> 1) & 0x7,
				(uint32_t)(ntohl(arec->header) & 0x1));
			pload = (uint8_t*) arec->pload;
			break;
		case TT_ETH:
			printf("wlen=%u etype=0x%.4x\n"
				"dst=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x "
				"src=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
				ntohs(erec->wlen),
				ntohs(erec->etype),
				erec->dst[0], erec->dst[1], erec->dst[2], erec->dst[3], erec->dst[4], erec->dst[5],
				erec->src[0], erec->src[1], erec->src[2], erec->src[3], erec->src[4], erec->src[5]);
			pload = (uint8_t*) erec->pload;
			break;
		case TT_POS:
			printf("slen=%u loss=%u wlen=%u\n",
				(uint32_t)ntohl(prec->slen),
				ntohs(prec->loss),
				ntohs(prec->wlen));
			pload = (uint8_t*) prec->pload;
			break;

		default:
			if (tests & TEST_TYPE)
			{
				/* Unknown format, print in hex. */
				int pos;
				for (pos = 0; pos < 256; pos++)
				{
					printf("0x%02x ", rec[pos]);
					if (0 == (pos % 4))
					{
						printf("\n");
					}
				}
				printf("\n");
			}
			break;
		}
	}
	else if (dagerf_is_modern_type((uint8_t*) rec))
	{
		/* Get the record type minus the extension header  bit */
		rec_type = rec[8] & 0x7F;
	
		/* Print the standard header fields */
		printf("dserror=%d rxerror=%d trunc=%d vlen=%d iface=%d rlen=%d",
				(int)drec->flags.dserror, (int)drec->flags.rxerror,
				(int)drec->flags.trunc, (int)drec->flags.vlen,
				(int)drec->flags.iface, ntohs(drec->rlen));

		
		/* Print loss counter field if it is not type color */
		if ( !dagerf_is_color_type((uint8_t*) rec) )
		{
			printf(" lctr=%d", ntohs(drec->lctr));
		} else if ((drec->type == ERF_TYPE_COLOR_HDLC_POS) || (drec->type == ERF_TYPE_COLOR_ETH))
		{
			printf(" ipf=0x%04x", ntohs(drec->lctr));
		}
		else if ((drec->type == ERF_TYPE_DSM_COLOR_ETH) || (drec->type == ERF_TYPE_DSM_COLOR_HDLC_POS))
		{
			printf(" dsm=0x%04x", ntohs(drec->lctr));
		}
		printf(" wlen=%d\n", ntohs(drec->wlen));
	


		/* Print IPF or DSM color information for ethernet and PoS types */
		if (ERF_TYPE_COLOR_HDLC_POS == rec_type || ERF_TYPE_COLOR_ETH == rec_type)
		{
			uint16_t color = ntohs(drec->lctr);
			printf("color=%u dststream=%u\n", (color >> 2), (color & 0x03));
		}
		else if (ERF_TYPE_DSM_COLOR_HDLC_POS == rec_type || ERF_TYPE_DSM_COLOR_ETH == rec_type)
		{
			uint16_t color = ntohs(drec->lctr);
			printf("hlb=%u%u filters=%u%u%u%u%u%u%u%u stream=%u\n", (color >> 15) & 0x01, (color >> 14) & 0x01, 
				(color >> 13) & 0x01, (color >> 12) & 0x01, (color >> 11) & 0x01, (color >> 10) & 0x01,
				(color >> 9) & 0x01, (color >> 8) & 0x01, (color >> 7) & 0x01, (color >> 6) & 0x01,
				(color & 0x3f));
		}
		else if (ERF_TYPE_COLOR_HASH_POS == rec_type || ERF_TYPE_COLOR_HASH_ETH == rec_type)
		{
			uint16_t color = ntohs(drec->lctr);
			printf("color=%u hlb=%u\n", (color >> 4), (color & 0x0f));
		}


		/* If the ERF record has extension headers then we need to iterate over them and
		 * also display their fields. We also need to update the payload pointer by the
		 * number of extension headers in the record.
		 */
		if ( (num_ext_hdrs = dagerf_ext_header_count((uint8_t*)rec, len)) > 0 )
		{
			/* print out the headers */
			print_ext_headers((uint8_t*)rec, (size_t)len);
			
			/* HACK: update the record pointer so that the rest of the fields after the extension header line up */
			drec = (dag_record_t*) ((uintptr_t)rec + (num_ext_hdrs * 8));
		}



		/* Print the rest of the record */
		switch ( rec_type ) {
		case ERF_TYPE_HDLC_POS:
		case ERF_TYPE_COLOR_HDLC_POS:
		case ERF_TYPE_DSM_COLOR_HDLC_POS:
		case ERF_TYPE_COLOR_HASH_POS:
			
			printf("hdlc header=0x%08x\n", (uint32_t) ntohl(drec->rec.pos.hdlc));
			pload = drec->rec.pos.pload;
			break;
	
		case ERF_TYPE_ETH:
		case ERF_TYPE_COLOR_ETH:
		case ERF_TYPE_DSM_COLOR_ETH:
		case ERF_TYPE_COLOR_HASH_ETH:
			{
				int ethertype = 0;

				printf("pad=0x%02x offset=0x%02x\n", drec->rec.eth.pad, drec->rec.eth.offset);
				printf("dst=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x src=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
						drec->rec.eth.dst[0], drec->rec.eth.dst[1], drec->rec.eth.dst[2], drec->rec.eth.dst[3], drec->rec.eth.dst[4], drec->rec.eth.dst[5],
						drec->rec.eth.src[0], drec->rec.eth.src[1], drec->rec.eth.src[2], drec->rec.eth.src[3], drec->rec.eth.src[4], drec->rec.eth.src[5]);

				ethertype = ntohs(drec->rec.eth.etype);
				if (ethertype == 0x8100)
				{
					uint16_t* overlay16 = (uint16_t*) drec->rec.eth.pload;
					uint16_t length_type = ntohs(overlay16[1]);
					
					/* VLAN ethernet. */
					printf("vlan=0x%04x ", ntohs(overlay16[0]));
					
					if (length_type >= 0x600)
					{
						/* Non-VLAN Type-encoded ethernet. */
						printf("etype=0x%04x\n", length_type);
						pload = (uint8_t*) &overlay16[2];
					}
					else
					{
						/* Non-VLAN Length-encoded ethernet. */
						printf("length=0x%04x\n", length_type);
						pload = (uint8_t*) &overlay16[2];
					}
				}
				else if (ethertype >= 0x600)
				{
					/* Non-VLAN Type-encoded ethernet. */
					printf("etype=0x%04x\n", ethertype);
					pload = drec->rec.eth.pload;
				}
				else
				{
					/* Non-VLAN Length-encoded ethernet. */
					printf("length=0x%04x\n", ethertype);
					pload = drec->rec.eth.pload;
				}
		
				if (tests & TEST_BOPT)
				{
					/* Display BOPT timestamp. */
					uint64_t timestamp;
					
					memcpy(&timestamp, &drec->rec.eth.pload[36], 8);
					timestamp = bswap_64(timestamp);
					
					time = (time_t)(timestamp >> 32);
					dtime = gmtime((time_t *)(&time));
					strftime(dtime_str, sizeof(dtime_str), "%Y-%m-%d %T", dtime);
					printf("bopt: ts=0x%.16"PRIx64" %s.%07.0f UTC\n",
						   timestamp,
						   dtime_str,
						   (double)(timestamp & 0xffffffffll) / 0x100000000ll * 10000000);
		
					if (bopt_last_timestamp != 0)
					{
						uint64_t ts_diff = timestamp - bopt_last_timestamp;
		
						printf("bopt: ts diff = %u.%07.0f seconds\n", 
							   (uint32_t) (ts_diff >> 32),
							   (double) (ts_diff & 0xffffffffll) / 0x100000000ll * 10000000);
					}
				}
			}
			break;
		
		case ERF_TYPE_ATM:
			atm_header = ntohl(drec->rec.atm.header);
                        if(set_atm_nni)
                        {
                            printf("atm header=0x%08x vpi=%4d vci=%5d pti=%1d clp=%1d\n",
                                    (int32_t)atm_header,
                                    (uint32_t)(atm_header>>20)&0xfff,
                                    (uint32_t)(atm_header>>4)&0xffff,
                                    (uint32_t)(atm_header>>1)&0x7,
                                    (uint32_t)(atm_header&0x1));
                        }
                        else 
                        {
                            printf("atm header=0x%08x gfc=%2d vpi=%3d vci=%5d pti=%1d clp=%1d\n",
				   (int32_t)atm_header,
				   (uint32_t)atm_header>>28,
				   (uint32_t)(atm_header>>20)&0xff,
				   (uint32_t)(atm_header>>4)&0xffff,
				   (uint32_t)(atm_header>>1)&0x7,
				   (uint32_t)(atm_header&0x1));
                        }
			pload = drec->rec.atm.pload;
			break;

		case ERF_TYPE_AAL2:
			errs = (uint32_t)ntohl(drec->rec.aal2.ext_header)>>8;
			printf("ext header CID=%-3u maal error %d maal number %d 1st frame %d \n",
				   (uint32_t)ntohl(drec->rec.aal2.ext_header)&0xff,
				   (errs & 0x100)!=0,
				   (errs & 0xFF),
				   (errs & 0x200)!=0);
			atm_header = ntohl(drec->rec.aal2.header);
			printf("atm header=0x%08x gfc=%2d vpi=%3d vci=%5d pti=%1d clp=%1d\n",
				   (int32_t)atm_header,
				   (uint32_t)atm_header>>28,
				   (uint32_t)(atm_header>>20)&0xff,
				   (uint32_t)(atm_header>>4)&0xffff,
				   (uint32_t)(atm_header>>1)&0x7,
				   (uint32_t)(atm_header&0x1));
			pload = drec->rec.aal2.pload;
			break; 
		case ERF_TYPE_AAL5:
			atm_header = ntohl(drec->rec.aal5.header);
			printf("atm header=0x%08x gfc=%2d vpi=%3d vci=%5d pti=%1d clp=%1d\n",
				   (int32_t)atm_header,
				   (uint32_t)atm_header>>28,
				   (uint32_t)(atm_header>>20)&0xff,
				   (uint32_t)(atm_header>>4)&0xffff,
				   (uint32_t)(atm_header>>1)&0x7,
				   (uint32_t)(atm_header&0x1));
			pload = drec->rec.aal5.pload;
			break;
		case ERF_TYPE_MC_HDLC:
			errs = (uint32_t)ntohl(drec->rec.mc_hdlc.mc_header)>>24;
			printf("channel=%-3d errors x0%02x: fcs %d short %d long %d abort %d octet %d lostbyte %d\n",
				   (uint32_t)ntohl(drec->rec.mc_hdlc.mc_header)&0x1ff,
				   errs,
				   (errs & MC_HDLC_FCS)!=0,
				   (errs & MC_HDLC_SHORT)!=0,
				   (errs & MC_HDLC_LONG)!=0,
				   (errs & MC_HDLC_ABORT)!=0,
				   (errs & MC_HDLC_OCTET)!=0,
				   (errs & MC_HDLC_LOST)!=0);
			pload = drec->rec.mc_hdlc.pload;
			connection = (uint32_t)ntohl(drec->rec.mc_hdlc.mc_header)&0x1ff;
			break; 
		case ERF_TYPE_MC_RAW:
			printf("interface=%-3d errors=0x%02x\n",
				   (uint32_t)dagutil_37t_line_get_logical(ntohl(drec->rec.mc_raw.mc_header)&0x1ff),
				   (uint32_t)ntohl(drec->rec.mc_raw.mc_header)>>24);
			connection = (uint32_t)ntohl(drec->rec.mc_raw.mc_header)&0x1ff;
			pload = drec->rec.mc_raw.pload;
			break; 
		case ERF_TYPE_MC_ATM:
            printf("multichannel header=0x%08x\n", (uint32_t)ntohl(drec->rec.mc_atm.mc_header));
			if ((uint32_t)ntohl(drec->rec.mc_atm.mc_header) & 0x8000)
			{
				printf("ImaId");
			}
			else
			{
				printf("channel");
			}
			errs = (uint32_t)ntohl(drec->rec.mc_atm.mc_header)>>24;
			printf("=%-3u ifc=%-2u flags 0x%02x: oam %d lost %d hec corrected %d oamcrcerr %d\n",
				   (uint32_t)ntohl(drec->rec.mc_atm.mc_header)&0x1ff,
				   (uint32_t)dagutil_37t_line_get_logical((ntohl(drec->rec.mc_atm.mc_header) >> 16) & 0x0f),
				   errs,
				   (errs & MC_ATM_OAM)!=0,
				   (errs & MC_ATM_LOST)!=0,
				   (errs & MC_ATM_HECC)!=0,
				   (errs & MC_ATM_OAMCRC)!=0);
			tp = drec->rec.mc_aal2.pload;
			atm_header = ntohl( *((uint32_t*) tp) );
			if(set_atm_nni)
                        {
				printf("atm header=0x%08x vpi=%4d vci=%5d pti=%1d clp=%1d\n",
					(int32_t)atm_header,
                                    	(uint32_t)(atm_header>>20)&0xfff,
                                    	(uint32_t)(atm_header>>4)&0xffff,
                                    	(uint32_t)(atm_header>>1)&0x7,
                                    	(uint32_t)(atm_header&0x1));
                        }
                        else 
                        {

				printf("atm header=0x%08x gfc=%2d vpi=%3d vci=%5d pti=%1d clp=%1d\n",
				   	(int32_t)atm_header,
				   	(uint32_t)atm_header>>28,
				   	(uint32_t)(atm_header>>20)&0xff,
				   	(uint32_t)(atm_header>>4)&0xffff,
				   	(uint32_t)(atm_header>>1)&0x7,
				   	(uint32_t)(atm_header&0x1));
			}

			pload = &(drec->rec.mc_atm.pload[4]);
			connection = (uint32_t)ntohl(drec->rec.mc_atm.mc_header)&0x1ff;
			break; 

		case ERF_TYPE_MC_RAW_CHANNEL:
			printf("channel=%-3d errors=0x%02x\n",
				  (uint32_t)ntohl(drec->rec.mc_raw_channel.mc_header)&0x1ff,
				  (uint32_t)ntohl(drec->rec.mc_raw_channel.mc_header)>>29);
			pload = drec->rec.mc_raw_channel.pload;
			connection = (uint32_t)ntohl(drec->rec.mc_raw_channel.mc_header)&0x1ff;
			break;      

		case ERF_TYPE_MC_AAL5:
			errs = (uint32_t)ntohl(drec->rec.mc_aal5.mc_header)>>16;
			printf("mc header=0x%08x channel=%-3u ifc=%-2u \n",
				   (uint32_t) ntohl(drec->rec.mc_aal5.mc_header),
				   (uint32_t)ntohl(drec->rec.mc_aal5.mc_header)&0x1ff,
				   (uint32_t)dagutil_37t_line_get_logical((ntohl(drec->rec.mc_aal5.mc_header) >> 16) & 0x0f));
			printf("flags 0x%02x: CRC chk %d error %d len chk %d error %d 1st frame %d \n", 
				   errs,
				   (errs & MC_AAL5_CRC_CHECK)!=0,
				   (errs & MC_AAL5_CRC_ERROR)!=0,
				   (errs & MC_AAL5_LEN_CHECK)!=0,
				   (errs & MC_AAL5_LEN_ERROR)!=0,
				   ((((uint32_t)ntohl(drec->rec.mc_aal5.mc_header)>>24) & MC_AAL5_1ST_CELL)) != 0);
			tp = drec->rec.mc_aal5.pload;
			atm_header = ntohl( *((uint32_t*) tp) );
			printf("atm header=0x%08x gfc=%2d vpi=%3d vci=%5d pti=%1d clp=%1d\n",
				   (uint32_t)atm_header,
				   (uint32_t)atm_header>>28,
				   (uint32_t)(atm_header>>20)&0xff,
				   (uint32_t)(atm_header>>4)&0xffff,
				   (uint32_t)(atm_header>>1)&0x7,
				   (uint32_t)atm_header&0x1);
			pload = &(drec->rec.mc_aal5.pload[4]);
			connection = (uint32_t)ntohl(drec->rec.mc_aal5.mc_header)&0x1ff;
			break; 
	
		case ERF_TYPE_MC_AAL2:
			errs = (uint32_t)ntohl(drec->rec.mc_aal2.mc_header)>>16;
			printf("mc header=0x%08x channel=%-3u ifc=%-2u CID=%-3u\n",
				   (uint32_t)ntohl(drec->rec.mc_aal2.mc_header),
				   (uint32_t)ntohl(drec->rec.mc_aal2.mc_header)&0x1ff,
				   (uint32_t)dagutil_37t_line_get_logical((ntohl(drec->rec.mc_aal2.mc_header) >> 16) & 0x0f),
				   (uint32_t)(((ntohl(drec->rec.mc_aal2.mc_header))&0xff000000)>>24));
			printf("flags 0x%02x: maal error %d 1st frame %d \n", 
				   errs,
				   (errs & MC_AAL2_MAAL)!=0,
				   (errs & MC_AAL2_1ST_CELL)!=0);
			tp = drec->rec.mc_aal2.pload;
			atm_header = ntohl( *((uint32_t*) tp) );
			printf("atm header=0x%08x gfc=%2d vpi=%3d vci=%5d pti=%1d clp=%1d\n",
				   (uint32_t)atm_header,
				   (uint32_t)atm_header>>28,
				   (uint32_t)(atm_header>>20)&0xff,
				   (uint32_t)(atm_header>>4)&0xffff,
				   (uint32_t)(atm_header>>1)&0x7,
				   (uint32_t)(atm_header&0x1));
			pload = &(drec->rec.mc_aal2.pload[4]);
			connection = (uint32_t)ntohl(drec->rec.mc_aal2.mc_header)&0x1ff;
			break; 


		case ERF_TYPE_IP_COUNTER:
		case ERF_TYPE_TCP_FLOW_COUNTER:
			printf("flags = %u tcp_ip_flag = %u\n", tcp_ip_counter->flags, (uint32_t)ntohl(tcp_ip_counter->tcp_ip_flag));
			pload = tcp_ip_counter->pload;
			break;

		case TYPE_PAD:
			return;	

		case ERF_TYPE_INFINIBAND:
			print_infiniband_headers(drec->rec.infiniband);
			pload = (uint8_t*) &(drec->rec);
			break;
        	case ERF_TYPE_INFINIBAND_LINK:
			pload = (uint8_t*) &(drec->rec);
			break;
		case ERF_TYPE_RAW_LINK:
			pload = drec->rec.raw_link.pload;
			break;
		case ERF_TYPE_IPV6:
		case ERF_TYPE_IPV4:
			pload = (uint8_t*) &(drec->rec);
			break;
		default:
			pload = (uint8_t*) rec;
			printf("print %"PRIu64": record printing not implemented for ERF type %d\n", index, rec_type);
	
			if (tests & TEST_TYPE)
			{
				/* Unknown format, print in hex. */
				int pos;
				for (pos = 0; pos < 256; pos++)
				{
					printf("0x%02x ", rec[pos]);
					if (0 == (pos % 4))
					{
						printf("\n");
					}
				}
				printf("\n");
			}
			break;
		}

	} else {
        /* Unknown record type */
        pload = (uint8_t*) rec;
    }
	
	
	
	/*
     * TODO: This will work properly only with ETH and
     * other traffic with ERF header 16
     */
	if (erase_pad)
	{
		adj_len = ntohs(drec->rlen) - 16 - ntohs(drec->wlen) - 2;
		if (adj_len < 0)
		{
			adj_len = 0; 
		}
	}
	
	if (tests & TEST_DECODE)
	{	
		dagpd_decode_protocols((uint8_t*) rec, len);
		return;
	}

	if (dagutil_get_verbosity() == 1)
	{
		if (!print_tcp_ip_counter)
		{
			for (i = 0; pload < (uint8_t*) (rec+len-adj_len); pload++, i++)
			{ 
				printf("%02x%s", (*pload) & 0xff, ((i%16)==15) ? "\n" : " ");
			}
		}
		else
		{
			while (pload < (uint8_t*)(rec + len))
			{
				if (tcp_ip_counter->type == 13)
				{
                    int diff = 0;
                    if ((pload + 12) > (uint8_t*)(rec + len))
                    {
                        diff = ((uint8_t*)(rec + len) - pload);
                        pload = pload + diff;
                        continue;
                    }
					/* print ip counter addresses*/
					printf("IP Address = %u.%u.%u.%u\n", pload[0], pload[1], pload[2], pload[3]);
					pload += 4;
					printf("Source Count = %u\n", (uint32_t)ntohl(*(uint32_t*)pload));
					pload += 4;
					printf("Dest Count = %u\n", (uint32_t)ntohl(*(uint32_t*)pload));
					pload += 4;
					printf("\n");
				}
				else if (tcp_ip_counter->type == 14)
				{
                    int diff = 0;
                    if ((pload + 20) > (uint8_t*)(rec + len))
                    {
                        diff = ((uint8_t*)(rec + len) - pload);
                        pload = pload + diff;
                        continue;
                    }
					/* print tcp flow addresses */
					printf("Source IP Address = %u.%u.%u.%u\n", pload[0], pload[1], pload[2], pload[3]);
					pload += 4;
					printf("Dest IP Address = %u.%u.%u.%u\n", pload[0], pload[1], pload[2], pload[3]);
					pload += 4;
					printf("IP Protocol = %u\n", pload[0]);
					pload += 4;
					printf("Destination Port = %u\n", ntohs(*(uint16_t*)pload));
					pload += 2;
					printf("Source Port = %u\n", ntohs(*(uint16_t*)pload));
					pload += 2;
					printf("Count = %u\n", (uint32_t)ntohl(*(uint32_t*)pload));
					pload += 4;
					printf("\n");
				}
				else
				{
					break;
				}
			}
		}
	}
	else if (dagutil_get_verbosity() > 1)
	{
		memset(abuf, 0, 17);
		for (i = 0; pload < (uint8_t*) (rec+len-adj_len); pload++, i++)
		{
			abuf[i%16] = isprint((*pload) & 0xff) ? ((*pload) & 0xff) : '.';
			printf("%02x ", (*pload) & 0xff);
			if ((i%16)==15)
			{
				printf("        %s\n", abuf);
				memset(abuf, 0, 17);
			}
		}
		if (i % 16 != 15)
		{
			while (0 != (i % 16))
			{
				printf("   ");
				i++;
			}
			printf("        %s\n", abuf);
		}
	}
	printf("\n");
}

static int
print_delta(char *rec, int len)
{
	static int64_t delta_oldts[MAX_INTERFACES];
	
	int64_t delta=0;
	int print=0;
	dag_record_t * drec = (dag_record_t *)rec;
	int ret_val=0;
#if defined(_WIN32)
    double second;
#endif
	
	if (delta_oldts[intf]) {
		delta = TS(drec->ts) - delta_oldts[intf];
		if(delta_print)
			print=1;
		if(delta_min&&delta<delta_min)
		{
			delta_min_fail ++;
			ret_val = 1;
			print=1;
		}
		if(delta_max&&delta>delta_max)
		{
			print=1;
			ret_val = 1;
			delta_max_fail ++;	
		}

		if( (delta < delta_min_seen) || (delta_min_seen == 0) )
			delta_min_seen = delta;

		if(delta > delta_max_seen)
			delta_max_seen = delta;

	}

	delta_oldts[intf] = TS(drec->ts);

	if (print)
	{
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

		printf("delta %" PRIu64 ": 0x%016" PRIx64 " %.9f\n", pc, delta, (double)delta/0x100000000ll);

#elif defined(_WIN32)

        second = (double)(ULONG)(delta & 0xffffffff) / (0x100000000i64 );

        second += ((delta & 0xffffffff00000000) >>32);
		printf("delta %" PRIu64 ": 0x%016x ", pc, delta);
        printf("%.9f \n",  second );

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */
	}
	return ret_val;
}



/* Test sequence numbers between ERF records on oc48 and oc192 raw traffic.
   Packets of these traffic types have to broken up into multiple ERF records.
   An oc48 packet is encapsulated into 4 ERF records, while an oc192 packet 
   is encapsulated into 16 ERF records.

   Note that to indicate an oc48 or 0c192 raw packet, the payload has the byte 
   'f6' repeated 48 and 192 times respectively.
*/
static int
test_sequence(char *rec, int len)
{
	dag_record_t *drec = (dag_record_t *)rec;
	uint64_t ext_hdr;
	uint8_t type;
	uint8_t rate;
	uint8_t sequence_max = 0;
	static uint16_t sequence_num = 0;
	uint16_t current_sequence_num = 0;
	uint8_t more_fragment = 0;
	static uint8_t first_pkt = 1;

	// Sanity check for enough bytes for at least one ERF header and ERF extension header
	if (len < ERF_HEADER + ERF_EXT_HEADER)
	{
		error_printf("sequence %"PRIu64": Insufficient bytes in record: %d \n",pc, len);
		return 1;
	}
	
	// Check for the presence of extension headers
	if (dagerf_ext_header_count((uint8_t*)drec, len) == 0)
	{
		error_printf("sequence %"PRIu64": No extension header found!\n",pc);
		sequence_fail++;
		return 1;
	}

	// Get the extension header
	ext_hdr = *((uint64_t *)(&rec[ERF_HEADER]));
	ext_hdr = bswap_64(ext_hdr);

	//printf ("TEST: Extension header: 0x%"PRIx64"\n", ext_hdr);	// NOTE: Remove later!

	// Check that the correct extension header is present
	type = (uint8_t)((ext_hdr >> 56) & 0x7f);
	if (type != 0x05)
	{
		error_printf("sequence %"PRIu64": Incorrect extension header type: 0x%x (expected: 0x%x)\n",pc, type, 0x05);
		sequence_fail++;
		return 1;
	}

	// Check for either oc48 or oc192 traffic
	rate = (uint8_t)((ext_hdr >> 8) & 0xff);
	if (rate == 3)		// oc48
		sequence_max = 4;
	else if (rate == 4)	// oc192
		sequence_max = 16;
	else
	{
		error_printf("sequence %"PRIu64": Sequence test invalid at this packet rate: %d\n",pc, rate);
		sequence_fail++;
		return 1;
	}

	// Check for acceptable sequence numbers	
	current_sequence_num= (uint16_t)((ext_hdr >> 16) & 0xffff);
	if (current_sequence_num > sequence_max)
	{
			error_printf("sequence %"PRIu64": Invalid sequence number detected: %d (maximum: %d)\n",pc, current_sequence_num, sequence_max);
			sequence_fail++;
			sequence_num = 0;	// NOTE: Reset the sequence number
			return 1;
	}

	// For the first packet just store the sequence number, as we need a valid previous sequence number to compare the sequence number of the next packet against
	if (first_pkt)
	{
		sequence_num = current_sequence_num;
		first_pkt = 0;
		return 0;
	}

	// Check for start of frame
	more_fragment = (uint8_t)((ext_hdr >> 55) & 0x1);
	if (more_fragment == 0)		// Start of new record
	{
		// Make sure current sequence number is 0
		if (current_sequence_num != 0)		
		{
			error_printf("sequence %"PRIu64": Start of new record with incorrect sequence number: %d (expected: 0, previous: %d)\n",pc, current_sequence_num, sequence_num);
			sequence_fail++;
			sequence_num = current_sequence_num;
			return 1;
		}
	}
	else	// Another fragment
	{	
		if (current_sequence_num != sequence_num + 1)
		{
			error_printf("sequence %"PRIu64": Sequence number broken: %d (expected: %d)\n",pc, current_sequence_num, sequence_num+1);
			sequence_fail++;
			sequence_num = current_sequence_num;
			return 1;
		}
	}

	sequence_num = current_sequence_num;
	return 0;
}


static int
test_vci_delta_variance(char *rec, int len)
{
    // look at the rate of change in timestamps per virtual channel
    // this test is useful for testing multiple constant rate streams
	int64_t delta = 0;
    int64_t delta_variance = 0;
	dag_record_t * drec = (dag_record_t *)rec;
    uint32_t chan, vpi, vci;
  	mc_aal_vc_t* pVC = NULL;
    int ret_val=0;
    double second;


    // don't try doing this to non MC_ATM cells
    if( drec->type != ERF_TYPE_MC_ATM)
        return 0;

    // get the last timestamp and delta and the standard delta variance from this vci
	chan = (uint32_t) ntohl(drec->rec.mc_atm.mc_header)&0x1ff;

	vpi  = (uint32_t) (drec->rec.mc_atm.pload[0]<<4);
	vpi |= (uint32_t)((drec->rec.mc_atm.pload[1] & 0xf0)>>4);
	vci  = (uint32_t)((drec->rec.mc_atm.pload[1] & 0x0f)<<12);
	vci |= (uint32_t) (drec->rec.mc_atm.pload[2] << 4);
	vci |= (uint32_t)((drec->rec.mc_atm.pload[3] & 0xf0)>>4);

	/*check for a previous entry on this connection, VCI and VPI */
	pVC = RetrieveVCFromMemory(vci ,vpi ,chan);
	if (pVC == NULL)
	{
		pVC = dagutil_malloc(sizeof(mc_aal_vc_t));
		pVC->last_ts = TS(drec->ts);
		pVC->vci = vci;
		pVC->vpi = vpi;
		pVC->connection_num = chan;
		pVC->pNext = NULL;
        pVC->last_delta = 0;
        pVC->std_delta_variance = 0;
		StoreNewVirtualChannel(pVC);
		return 0;
	}



    // calculate the delta between this and the last timestamp

    if (pVC->last_ts != 0) {
		delta = TS(drec->ts) - pVC->last_ts;
        if(pVC->last_delta != 0)
        {
            if(delta >= pVC->last_delta)
                delta_variance = delta - pVC->last_delta;
            else
                delta_variance = pVC->last_delta - delta;

        }
        // if delta is not within +/- 500uS of the standard variance print all info to screen
        if(delta_variance != 0 ){

            if(pVC->std_delta_variance == 0) {
                if(pc > 5)
                    pVC->std_delta_variance = delta_variance;
            }
            else {
                if((delta_variance > (pVC->std_delta_variance + 0x22BBEC)) || (delta_variance < (pVC->std_delta_variance - 0x22BBEC)))
                {
                    // error in the timestamp variation. Display the error.
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

                    second = (double)(delta_variance & 0xffffffff) / (0x100000000ll );
                    second += (delta_variance >>32);

#elif defined(_WIN32)

                    second = (double)(ULONG)(delta_variance & 0xffffffff) / (0x100000000i64 );
                    second += (((ULONG)delta_variance & 0xffffffff00000000) >>32);

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

                    printf("Error in delta variance %" PRIu64 ": previous delta:0x%"PRIx64" \n", pc,  pVC->last_delta);
                    printf("                                  new delta:0x%"PRIx64" \n", delta);
                    printf("Difference in deltas on vci %d vpi %d : %.9f seconds 0x%"PRIx64" \n\n",  vci, vpi,  second, delta_variance);
                    ret_val = 1;

                }
            
            }
        }
    }

    // store all the new information as required
    pVC->last_delta = delta;
    pVC->last_ts = TS(drec->ts);
    StoreTimestamp(pVC);

	return ret_val;
}


static int
test_mono_mc_aal(char* rec, int len)
{
	int retval = 0;
	uint64_t oldts = 0;
	uint32_t chan, vci, vpi;
	dag_record_t* drec = NULL;
	mc_aal_vc_t* pVC = NULL;

	drec = (dag_record_t*)rec;
	chan = (uint32_t) ntohl(drec->rec.mc_atm.mc_header)&0x1ff;
	connection = chan;

	vpi  = (uint32_t) (drec->rec.mc_atm.pload[0]<<4);
	vpi |= (uint32_t)((drec->rec.mc_atm.pload[1] & 0xf0)>>4);
	vci  = (uint32_t)((drec->rec.mc_atm.pload[1] & 0x0f)<<12);
	vci |= (uint32_t) (drec->rec.mc_atm.pload[2] << 4);
	vci |= (uint32_t)((drec->rec.mc_atm.pload[3] & 0xf0)>>4);

	/*check for a previous entry on this connection, VCI and VPI */
	pVC = RetrieveVCFromMemory(vci ,vpi ,chan);
	if (pVC == NULL)
	{
		pVC = dagutil_malloc(sizeof(mc_aal_vc_t));
		pVC->last_ts = TS(drec->ts);
		pVC->vci = vci;
		pVC->vpi = vpi;
		pVC->connection_num = chan;
		pVC->pNext = NULL;
		StoreNewVirtualChannel(pVC);
		return 0;
	}


	oldts = pVC->last_ts;
	if ((strict && (TS(drec->ts) <= oldts)) || ((TS(drec->ts)) < oldts))
	{
		mono_fail++;
		chan_fail = chan;

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

		error_printf("mono %"PRIu64": dir %d weird: 0x%.16"PRIx64", "
					 "previous: 0x%.16"PRIx64"  , diff %"PRIu64" (-0x%"PRIx64")\n",
					 pc, intf, TS(drec->ts), oldts,
					 TS(drec->ts) - oldts,
					 (int64_t)oldts - TS(drec->ts));

#elif defined(_WIN32)

		error_printf("mono %"PRIu64": dir %d weird: 0x%.16I64x, "
					 "previous: 0x%.16I64x  , diff %"PRIu64" (-0x%"PRIx64")\n",
					 pc, chan, TS(drec->ts), oldts,
					 TS(drec->ts) - oldts,
					 oldts - TS(drec->ts));
			
#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif  /* Platform specific code. */

		retval = 1;
	}

	pVC->last_ts = TS(drec->ts);
	StoreTimestamp(pVC);

	return retval;
}

static int
test_mono_mc_atm(char* rec, int len)
{
	static uint64_t mono_atmoldts[4][MAX_CHANNELS] = { {0}, {0} };
	static uint64_t mono_imaoldts[MAX_INTERFACES] = { 0 };

	uint32_t      chan;
	uint32_t      iface;
	int           retval = 0;
	uint64_t      oldts = 0;
	dag_record_t* drec = NULL;


	drec = (dag_record_t*)rec;
	if ((uint32_t)(ntohl(drec->rec.mc_atm.mc_header)&0x8000))
	{
		chan = ((uint32_t)ntohl(drec->rec.mc_atm.mc_header) >> 16) & 0x0f;
		oldts = mono_imaoldts[chan];
		mono_imaoldts[chan] = TS(drec->ts);
	}
	else
	{
		chan = (uint32_t)ntohl(drec->rec.mc_atm.mc_header)&0x1ff;
		iface = drec->flags.iface;
		oldts = mono_atmoldts[iface][chan];
		mono_atmoldts[iface][chan] = TS(drec->ts);
	}

	connection = chan;

	if ((strict && (TS(drec->ts) <= oldts)) || (TS(drec->ts) < oldts))
	{
		mono_fail++;
		chan_fail = chan;

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

		error_printf("mono %"PRIu64": dir %d weird: 0x%.16"PRIx64", "
		             "previous: 0x%.16"PRIx64"  , diff %"PRIu64" (-0x%"PRIx64")\n",
		             pc, intf, TS(drec->ts), oldts,
		             TS(drec->ts) - oldts,
		             (int64_t)oldts - TS(drec->ts));

#elif defined(_WIN32)

		error_printf("mono %"PRIu64": dir %d weird: 0x%.16I64x, "
		             "previous: 0x%.16I64x  , diff %"PRIu64" (-0x%"PRIx64")\n",
		             pc, chan, TS(drec->ts), oldts,
		             TS(drec->ts) - oldts,
		             oldts - TS(drec->ts));

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif  /* Platform specific code. */

		retval = 1;
	}
	else
	{
		lastchanrec[chan] = rec;
		lastchanindex[chan] = pc;
		lastchanoffset[chan] = offset;
	}

	return retval;
}

static int
test_mono_hdlc(char* rec, int len)
{
	static int64_t mono_oldts[MAX_CHANNELS][MAX_INTERFACES];
	
	uint32_t chan;
	int intf;
	int retval = 0;
	uint64_t oldts = 0;
	dag_record_t* drec = NULL;

	intf = dir(rec);
	drec = (dag_record_t*)rec;
	chan = (uint32_t)ntohl(drec->rec.mc_hdlc.mc_header)&0x1ff;
	connection = chan;
	oldts = mono_oldts[chan][intf];
	mono_oldts[chan][intf] = TS(drec->ts);
	if ((strict && (TS(drec->ts) <= (uint64_t)oldts)) || (TS(drec->ts) < (uint64_t)oldts))
	{
		mono_fail++;
		chan_fail = chan;
		error_printf("mono %"PRIu64": dir %u weird: 0x%.16"PRIx64", "
					 "previous: 0x%.16"PRIx64" %s, diff %"PRId64" (-0x%"PRIx64")\n",
					 pc, dir(rec), TS(drec->ts), oldts,
					 (TS(drec->ts) < oldts) ? " <" :
					 ((TS(drec->ts) == oldts) ? "==" : " >"),
					 (int64_t)TS(drec->ts) - oldts,
					 (int64_t)oldts - TS(drec->ts));
		retval = 1;
	}
	
	return retval;
}

static int
test_mono(char *rec, int len)
{
	static int64_t mono_oldts[MAX_INTERFACES];

	int retval = 0;
	dag_record_t *drec = (dag_record_t *)rec;
	int64_t oldts;
    static int old_intf; /* used for globally ordered timestamp check*/
    uint8_t mono_check_failed = 0; 
#if defined(_WIN32)
	LARGE_INTEGER tts, toldts;
#endif /* _WIN32 */

	switch (drec->type & 0x7f)
	{
		case TYPE_PAD:
			break;

		case ERF_TYPE_MC_HDLC:
			return test_mono_hdlc(rec, len);
		
		case ERF_TYPE_MC_ATM:
			return test_mono_mc_atm(rec, len);
		
		case ERF_TYPE_MC_AAL5:
		case ERF_TYPE_MC_AAL2:
			return test_mono_mc_aal(rec, len);
		
		default:
            if ( global_ts_order_check)
            {
                oldts = mono_oldts[old_intf];
                mono_oldts[intf] = TS(drec->ts);
                if( (TS(drec->ts) < (uint64_t)oldts) || (strict && (TS(drec->ts) == (uint64_t)oldts) && (old_intf == intf) ) )
                    mono_check_failed = 1;
                /* remember the interface for then next call */
                old_intf = intf;
            }
            else
            {
                oldts = mono_oldts[intf];
                mono_oldts[intf] = TS(drec->ts);
                
                if ((strict && (TS(drec->ts) <= (uint64_t)oldts)) || (TS(drec->ts) < (uint64_t)oldts)) 
                    mono_check_failed = 1;
            } 
            if( mono_check_failed)
            {
                mono_fail++;
				
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))
			
			error_printf("mono %"PRIu64": dir %u weird: 0x%.16"PRIx64", "
				     "previous: 0x%.16"PRIx64" %s, diff %"PRId64" (-0x%"PRIx64")\n",
				     pc, dir(rec), TS(drec->ts), oldts,
				     (TS(drec->ts) < oldts) ? " <" :
				     ((TS(drec->ts) == oldts) ? "==" : " >"),
				     (int64_t)TS(drec->ts) - oldts,
				     (int64_t)oldts - TS(drec->ts));
			
#elif defined(_WIN32)
			
			/* Printf under windows crashes when trying to print a large integer, 
			 * so we have to split some variables in two 32-bit integers
			 */
			
			tts.QuadPart = drec->ts; 
			toldts.QuadPart = oldts;
			
			
			printf("mono %I64u: dir %u weird: 0x%.16I64x, "
			       "previous: 0x%.16I64x %s, diff %I64d (-0x%I64x)\n",
			       pc, dir(rec), TS(drec->ts), oldts,
			       (TS(drec->ts) < (uint64_t)oldts) ? " <" :
			       ((TS(drec->ts) == oldts) ? "==" : " >"),
			       (_int64)TS(drec->ts) - oldts,
			       (_int64)oldts - TS(drec->ts));
			
#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* _WIN32 */
			
			retval = 1;
		}
	}
	return retval;
}

static int
test_ip(char *rec, int len)
{
	int retval = 0;
	char* r1 = find_iph(rec); /* start of ip header */

	if (r1 == NULL) 
		return warning;
	
	if ((*r1 & 0xf0) != 0x40)
	{
		if (warning)
		{
			error_printf("ip %"PRIu64": non-IPv4 (0x%.2x)\n", pc, *r1);
			ip_fail++;
		}
		retval = warning;
	}
	else
	{
#if !defined(__NetBSD__)
		if (IN_CHKSUM(r1))
		{
			ip_fail++;
			error_printf("ip %"PRIu64": IP checksum failed\n", pc);
			retval = 1;
		}
	}
#endif /* !__NetBSD__ */
	
	return retval;
}


static int
test_rawsend(char *rec, int len)
{
	static int raw_seq[MAX_INTERFACES];
	static int seen[MAX_INTERFACES];
	
	legacy_eth_t * ep = (legacy_eth_t *) rec;
	dag_record_t * drec = (dag_record_t *) rec;
	int retval=0, seq;
	uint32_t crc1;
	uint32_t crc2;
	int * pload = NULL;
	uint16_t etype = 0;

	if (tt==TT_ETH) 
	{
		etype = ep->etype;
		pload = (int*) ep->pload;
	}
	else if (dagerf_is_ethernet_type((uint8_t*) rec))
	{
		etype = drec->rec.eth.etype;
		pload = (int *)drec->rec.eth.pload;
	}
	else
	{
		error_printf("ip %"PRIu64": rawsend test not implemented for ERF type %d\n", pc, ((dag_record_t*)rec)->type);
		return 1;
	}
	/* first record */
	if (!seen[intf])
	{
		raw_seq[intf] = pload[1];
		seen[intf] = 1;
	}

	if (ntohs(etype) != 0x8888) {
		rawsend_etype_fail++;
		error_printf("rawsend %"PRIu64": etype=0x%.4x, expecting 8888\n",
					 pc, ntohs(etype));
		retval = 1;
	} else {
		seq = raw_seq[intf] + ntohs(drec->lctr);
		raw_seq[intf] = pload[1] + 1;

		if (seq != pload[1]) {
			rawsend_seq_fail++;
			error_printf("rawsend %"PRIu64": %7d expected, now %7d, missing %d\n",
						 pc, seq, pload[1], pload[1]-seq);
			retval = 1;
		}
		if (len - dag_record_size - 16 >= 40) { /* check there is enough data first! */
			crc1 = pload[0];
			pload[0] = 0;
			crc2 = dagcrc_ethernet_crc32_r(0UL, (uint8_t*) &pload[0], 40);
			if (crc1 != crc2) {
				rawsend_crc_fail++;
				error_printf("rawsend %"PRIu64": %7d crc mismatch 0x%.8x 0x%.8x\n",
							 pc, pload[1], crc1, crc2);
				retval = 1;
			}
		}
	}

	return retval;
}

static int
test_jitter(char *rec, int len)
{
	static int64_t    jitter[MAX_INTERFACES][4];
	
	dag_record_t *drec = (dag_record_t *)rec;
	int             retval=0;
	int64_t            ts_diff_new;

	enum {
		old  = 0,
		diff = 1,
		ind2 = 2,
		ind1 = 3
	};

	ts_diff_new = TS(drec->ts) - jitter[intf][old];
	if ( (abs((int32_t)(jitter[intf][diff] - ts_diff_new)) > jthresh) && (jitter[intf][diff] != 0) ) {
		jitter_fail++;
		error_printf("jitter ts diff between rec %"PRIu64" and %"PRIu64": %"PRId64"\n"
					 , jitter[intf][ind2], jitter[intf][ind1], jitter[intf][diff]);
		error_printf("jitter ts diff between rec %"PRIu64" and %"PRIu64": %"PRId64"\n"
					 , jitter[intf][ind1], pc, ts_diff_new);
		retval = 1;
	}
	if (jitter[intf][old])
		jitter[intf][diff] = ts_diff_new;
	jitter[intf][old] = TS(drec->ts);
	jitter[intf][ind2] = jitter[intf][ind1];
	jitter[intf][ind1] = pc;
	
	return retval;
}

static int
test_ccell(char *rec, char *lastrec)
{
	int i;
	legacy_atm_t       *crec = (legacy_atm_t *)rec;
	dag_record_t *drec = (dag_record_t*)rec;
	legacy_atm_t       *lastcrec = (legacy_atm_t *)lastrec;
	dag_record_t *lastdrec = (dag_record_t*)lastrec;
	char         *payload = (char *)(&drec->rec);
	char         *lastpayload = (char *)(&lastdrec->rec);
	static uint8_t	store_lastrec = 1;
	uint16_t	index;
	uint32_t	channel_mask = 0x3ff;
	
	/* wait for the second record */
	if (lastrec == NULL)
		return 0;

	switch ( ((dag_record_t*)rec)->type )
	{
		case ERF_TYPE_LEGACY:
			switch (tt)
			{
				case TT_ATM:
					if (crec->crc != lastcrec->crc) {
						ccell_fail++;
						error_printf("ccell %"PRIu64": crc difference!\n", pc);
						return 1;
					}
					if (crec->header != lastcrec ->header) {
						ccell_fail++;
						error_printf("ccell %"PRIu64": header difference!\n", pc);
						return 1;
					}
					for ( i = 0 ; i < (48/sizeof(uint32_t)) ; i++)
						if (crec->pload[i] != lastcrec->pload[i]){
							ccell_fail++;
							error_printf("ccell %"PRIu64": payload difference! %d\n",
										 pc, i);
							return 1;
						}
					return 0;
				
				default:
					error_printf("ccell %"PRIu64": Compare Cell test is only valid for ATM records!\n", pc);
					return 1;
			}
			break;
		
		case ERF_TYPE_ATM:
			if (drec->rec.atm.header != lastdrec->rec.atm.header) {
				ccell_fail++;
				error_printf("ccell %"PRIu64": header difference!\n", pc);
				return 1;
			}
			for ( i = 0 ; i < 48 ; i++)
				if (drec->rec.atm.pload[i] != lastdrec->rec.atm.pload[i]){
					ccell_fail++;
					error_printf("ccell %"PRIu64": payload difference!\n", pc);
					return 1;
				}
			return 0;
		
		case ERF_TYPE_MC_RAW:

			// Store previous record info, only need to do this once
			if (store_lastrec == 1)			
			{
				index = ((ntohl(lastdrec->rec.mc_raw.mc_header) & channel_mask) << 2) | ntohs(lastdrec->flags.iface);
				mc_raw_rec_info[index].rlen = ntohs(lastdrec->rlen);
				mc_raw_rec_info[index].wlen = ntohs(lastdrec->wlen);
				for ( i = 0 ; i < (ntohs(lastdrec->rlen) - 20); i++)
					mc_raw_rec_info[index].pload[i] = lastdrec->rec.mc_raw.pload[i];

#if 0
				printf("FIRST RECORD! iface: 0x%x, mc_header: 0x%x, index: 0x%x, rlen: %d, wlen: %d, payload[0]: 0x%x, payload[32]: 0x%x\n",
					lastdrec->flags.iface,
					ntohl(lastdrec->rec.mc_raw.mc_header),
					index,
					mc_raw_rec_info[index].rlen,
					mc_raw_rec_info[index].wlen,
					mc_raw_rec_info[index].pload[0],
					mc_raw_rec_info[index].pload[32]);
#endif
				store_lastrec = 0;
			}

			// Calculate index of current record
			index = ((ntohl(drec->rec.mc_raw.mc_header) & channel_mask) << 2) | ntohs(drec->flags.iface);
			
			// Check if there is already an entry in the array
			if (mc_raw_rec_info[index].rlen != 0)
			{
#if 0
				printf ("RECORD EXISTS! iface: 0x%x, mc_header: 0x%x, index: 0x%x, rlen: %d, wlen: %d, payload[0]: 0x%x, payload[32]: 0x%x\n",
					drec->flags.iface,
					ntohl(drec->rec.mc_raw.mc_header),
					index,
					mc_raw_rec_info[index].rlen,
					mc_raw_rec_info[index].wlen,
					mc_raw_rec_info[index].pload[0],
					mc_raw_rec_info[index].pload[32]);
#endif

				if (ntohs(drec->rlen) != mc_raw_rec_info[index].rlen)
				{
					ccell_fail++;
					error_printf("ccell %"PRIu64": mc_raw rlen difference!\n", pc);
					
					// Save the current packet
					mc_raw_rec_info[index].rlen = ntohs(drec->rlen);
					mc_raw_rec_info[index].wlen = ntohs(drec->wlen);
					for ( i = 0 ; i < (ntohs(drec->rlen) - 20); i++)
						mc_raw_rec_info[index].pload[i] = drec->rec.mc_raw.pload[i];

					return 1;
				}

				if (ntohs(drec->wlen) != mc_raw_rec_info[index].wlen)
				{
					ccell_fail++;
					error_printf("ccell %"PRIu64": mc_raw wlen difference!\n", pc);
	
					// Save the current packet
					mc_raw_rec_info[index].rlen = ntohs(drec->rlen);
					mc_raw_rec_info[index].wlen = ntohs(drec->wlen);
					for ( i = 0 ; i < (ntohs(drec->rlen) - 20); i++)
						mc_raw_rec_info[index].pload[i] = drec->rec.mc_raw.pload[i];

					return 1;
				}

				for ( i = 0 ; i < MC_RAW_MAX_PLOAD; i++)
					if (drec->rec.mc_raw.pload[i] != mc_raw_rec_info[index].pload[i])
					{
						ccell_fail++;
						error_printf("ccell %"PRIu64": mc_raw payload difference! drec->rec.mc_raw.pload[%d]: 0x%x, mc_raw_rec_info[0x%x].pload[%d]: 0x%x\n",
							pc,
							i,
							drec->rec.mc_raw.pload[i],
							index,
							i,
							mc_raw_rec_info[index].pload[i]);

						// Save the current packet
						mc_raw_rec_info[index].rlen = ntohs(drec->rlen);
						mc_raw_rec_info[index].wlen = ntohs(drec->wlen);
						for ( i = 0 ; i < (ntohs(drec->rlen) - 20); i++)
							mc_raw_rec_info[index].pload[i] = drec->rec.mc_raw.pload[i];

						return 1;
					}

			}
			else	// There was no previous entry so store the current record
			{
				mc_raw_rec_info[index].rlen = ntohs(drec->rlen);
				mc_raw_rec_info[index].wlen = ntohs(drec->wlen);
				for ( i = 0 ; i < (ntohs(drec->rlen) - 20); i++)
					mc_raw_rec_info[index].pload[i] = drec->rec.mc_raw.pload[i];

#if 0
				printf ("RECORD NEW! iface: 0x%x, mc_header: 0x%x, index: 0x%x, rlen: %d, wlen: %d, payload[0]: 0x%x, payload[32]: 0x%x\n",
					drec->flags.iface,
					ntohl(drec->rec.mc_raw.mc_header),
					index,
					mc_raw_rec_info[index].rlen,
					mc_raw_rec_info[index].wlen,
					mc_raw_rec_info[index].pload[0],
					mc_raw_rec_info[index].pload[32]);
#endif

				return 0;
			}

			break;

		default:
			if (reclen(rec) != reclen(lastrec) ) {
				ccell_fail++;
				error_printf("ccell %"PRIu64": record length change! %d last %d\n",
							 pc, reclen(rec), reclen(lastrec));
				return 1;
			}
	
			for ( i = 0 ; i < (reclen(rec) - dag_record_size) ; i++)
				if (payload[i] != lastpayload[i]){
					ccell_fail++;
					error_printf("ccell %"PRIu64": record difference! %d\n",
								 pc, i);
					return 1;
				}
			return 0;
	}
	return 0;
}

static int
test_ciph(char *rec, char *lastrec)
{
	char * r1 = NULL;
	char * r2 = NULL;

	/* wait for (at least) the second record */
	if (lastrec == NULL)
		return 0;

	r1 = find_iph(rec);
	if (r1 == NULL)
		return warning;
		
	r2 = find_iph(lastrec);
	if (r2 == NULL)
		return warning;

	if (ipcmp(r1, r2))
	{
		ciph_fail++;
		error_printf("ciph %"PRIu64": IP header differs from previous\n",pc);
		return 1;
	}
	return 0;
}

static int
test_cpacket2(char *rec, char *lastrec)
{
	char * r1 = NULL;
	char * r2 = NULL;
	char * p1 = NULL;
	char * p2 = NULL;
	uint16_t ip1_hlen = 0;
	uint16_t ip2_hlen = 0;
	uint32_t ip1_len = 0;
	uint32_t rec1_erf_data_length = 0;
	uint32_t ip2_len = 0;
	uint32_t rec2_erf_data_length = 0;
	uint32_t rec1_data_length = 0;
	uint32_t rec2_data_length = 0;
	uint32_t r1len;
	uint32_t r2len;
	uint32_t iplen;
	uint32_t i;
	uint32_t link_header_size = 0;

#if defined(__FreeBSD__) || defined(__NetBSD__) || (defined (__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

	struct ip* ip_hdr1;
	struct ip* ip_hdr2;

#elif defined(__linux__)

	struct iphdr* ip_hdr1;
	struct iphdr* ip_hdr2;

#elif defined(_WIN32)

	iphdr* ip_hdr1;
	iphdr* ip_hdr2;

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

	/* wait for (at least) the second record */
	if (lastrec == NULL)
		return 0;

	r1 = find_iph(rec);
	if (r1 == NULL)
		return warning;

	r2 = find_iph(lastrec);
	if (r2 == NULL)
		return warning;

	if (0 != ipcmp2(r1, r2))
	{
		cpacket2_fail++;
		error_printf("cpacket2 %"PRIu64": IP header differs from previous\n",pc);
		return 1;
	}

	/* Extract IP header length. */
#if defined (__FreeBSD__) || defined(__NetBSD__) || (defined (__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

	ip_hdr1 = (struct ip *) r1;
	ip_hdr2 = (struct ip *) r2;
	ip1_hlen = 4 * ip_hdr1->ip_hl;
	ip2_hlen = 4 * ip_hdr2->ip_hl;

#elif defined(__linux__)

	ip_hdr1 = (struct iphdr*) r1;
	ip_hdr2 = (struct iphdr*) r2;
	ip1_hlen = 4 * ip_hdr1->ihl;
	ip2_hlen = 4 * ip_hdr2->ihl;

#elif defined(_WIN32)

	ip_hdr1 = (iphdr*) r1;
	ip_hdr2 = (iphdr*) r2;

	// NOTE: The header length field of the IP header is from bit4-bit7, but on little endian machines this is swapped with the version field (bit0-bit3)
	ip1_hlen = 4 * (ip_hdr1->ip_ver_hl & 0x0F);
	ip2_hlen = 4 * (ip_hdr2->ip_ver_hl & 0x0F);

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

	if (dagerf_is_ethernet_type((uint8_t*) rec))
	{
		link_header_size = 16; /* gige-hack 2 + 12 MAC + 2 etype */
	}
	else
	{
		link_header_size = 4; /* ATM header/HDLC header */
	}

	/* Remember to check for end of IP packet! */
	ip1_len = ntohs(*(uint16_t*)(r1+2));
	if (ip1_len < 20)
	{
		/* Illegal IP length - must be at least the size of a minimal IP header. */
		return warning;
	}
	rec1_erf_data_length = (reclen(rec) - dag_record_size - link_header_size);
	r1len = dagutil_min(ip1_len, rec1_erf_data_length); /* XXX assumes one word of Link layer header, EG HDLC */

	ip2_len = ntohs(*(uint16_t*)(r2+2));
	if (ip2_len < 20)
	{
		/* Illegal IP length - must be at least the size of a minimal IP header. */
		return warning;
	}
	rec2_erf_data_length = (reclen(lastrec) - dag_record_size - link_header_size);
	r2len = dagutil_min(ip2_len, rec2_erf_data_length); /* XXX assumes one word of Link layer header, EG HDLC */
	
	rec1_data_length = r1len - ip1_hlen;
	rec2_data_length = r2len - ip2_hlen;
	iplen = dagutil_min(rec1_data_length, rec2_data_length);

	p1 = r1 + ip1_hlen;
	p2 = r2 + ip2_hlen;

	for (i = 0; i < iplen; i++) {
		if (p1[i] != p2[i]) {
			cpacket2_fail++;
			error_printf("cpacket2 %"PRIu64": IP payload differs from previous, %d\n", pc, i);
			return 1;
		}
	}
	return 0;
}

static int
test_cpacket(char *rec, char *lastrec)
{
	char * r1 = NULL;
	char * r2 = NULL;
	char * p1 = NULL;
	char * p2 = NULL;
	uint16_t ip1_hlen = 0;
	uint16_t ip2_hlen = 0;
	uint32_t ip1_len = 0;
	uint32_t rec1_erf_data_length = 0;
	uint32_t ip2_len = 0;
	uint32_t rec2_erf_data_length = 0;
	uint32_t rec1_data_length = 0;
	uint32_t rec2_data_length = 0;
	uint32_t r1len, r2len, iplen, i, link_header_size=0;

#if defined(__FreeBSD__) || defined(__NetBSD__) || (defined (__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

	struct ip* ip_hdr1;
	struct ip* ip_hdr2;

#elif defined(__linux__)

	struct iphdr* ip_hdr1;
	struct iphdr* ip_hdr2;

#elif defined(_WIN32)

	iphdr* ip_hdr1;
	iphdr* ip_hdr2;

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

	/* wait for (at least) the second record */
	if (lastrec == NULL)
		return 0;

	r1 = find_iph(rec);
	if (r1 == NULL)
		return warning;

	r2 = find_iph(lastrec);
	if (r2 == NULL)
		return warning;

	if (0 != ipcmp(r1, r2))
	{
		cpacket_fail++;
		error_printf("cpacket %"PRIu64": IP header differs from previous\n",pc);
		return 1;
	}

	/* Extract IP header length. */
#if defined (__FreeBSD__) || defined(__NetBSD__) || (defined (__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

	ip_hdr1 = (struct ip *) r1;
	ip_hdr2 = (struct ip *) r2;
	ip1_hlen = 4 * ip_hdr1->ip_hl;
	ip2_hlen = 4 * ip_hdr2->ip_hl;

#elif defined(__linux__)

	ip_hdr1 = (struct iphdr*) r1;
	ip_hdr2 = (struct iphdr*) r2;
	ip1_hlen = 4 * ip_hdr1->ihl;
	ip2_hlen = 4 * ip_hdr2->ihl;

#elif defined(_WIN32)

	ip_hdr1 = (iphdr*) r1;
	ip_hdr2 = (iphdr*) r2;

	// NOTE: The header length field of the IP header is from bit4-bit7, but on little endian machines this is swapped with the version field (bit0-bit3)
	ip1_hlen = 4 * (ip_hdr1->ip_ver_hl & 0x0F);
	ip2_hlen = 4 * (ip_hdr2->ip_ver_hl & 0x0F);

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

	if (dagerf_is_ethernet_type((uint8_t*) rec))
	{
		link_header_size = 16; /* gige-hack 2 + 12 MAC + 2 etype */
	}
	else
	{
		link_header_size = 4; /* ATM header/HDLC header */
	}

	/* Remember to check for end of IP packet! */
	ip1_len = ntohs(*(uint16_t*)(r1+2));
	if (ip1_len < 20)
	{
		/* Illegal IP length - must be at least the size of a minimal IP header. */
		return warning;
	}
	rec1_erf_data_length = (reclen(rec) - dag_record_size - link_header_size);
	r1len = dagutil_min(ip1_len, rec1_erf_data_length); /* XXX assumes one word of Link layer header, EG HDLC */

	ip2_len = ntohs(*(uint16_t*)(r2+2));
	if (ip2_len < 20)
	{
		/* Illegal IP length - must be at least the size of a minimal IP header. */
		return warning;
	}
	rec2_erf_data_length = (reclen(lastrec) - dag_record_size - link_header_size);
	r2len = dagutil_min(ip2_len, rec2_erf_data_length); /* XXX assumes one word of Link layer header, EG HDLC */

	rec1_data_length = r1len - ip1_hlen;
	rec2_data_length = r2len - ip2_hlen;

	iplen = dagutil_min(rec1_data_length, rec2_data_length);

	p1 = r1 + ip1_hlen;
	p2 = r2 + ip2_hlen;

	for (i = 0; i < iplen; i++) {
		if (p1[i] != p2[i]) {
			cpacket_fail++;
			error_printf("cpacket %"PRIu64": IP payload differs from previous, %d\n", pc, i);
			return 1;
		}
	}
	return 0;
}

static int
test_steering(char* rec, int len)
{
	dag_record_t *drec = (dag_record_t*)rec;
	uint16_t *r1 = NULL;
	uint32_t *destip = NULL;
	uint32_t *srcip = NULL;
	uint32_t resxor = 0;
	uint32_t rescrc = 0;
	uint32_t parity = 0;
	bool not_enough_bytes = false;
		
	if (!numrxstreams)
		numrxstreams = 4;

	switch (steer)
	{
		case STEER_FORCE0:
		{
			switch ( drec->type)
			{
				case ERF_TYPE_HDLC_POS:
				case ERF_TYPE_ETH:
				case ERF_TYPE_ATM:
					if ( dagstream != 0 )
						type_steerfail++; 
					break;
				
				default:
					break;
			}
		}
		break;
		
		case STEER_PARITY:
		case STEER_HLB_RANGE:
		case STEER_CRC:
		{
			if (ntohs(drec->wlen) < 20) {
				if (warning)
					error_printf("steer %"PRIu64": Record too short\n");
				return warning;
			}

			if (dagerf_is_pos_type((uint8_t*) rec))
			{
				r1 = (uint16_t*) drec->rec.pos.pload;
			}
			else if (dagerf_is_ethernet_type((uint8_t*) rec))
			{
				r1 = (uint16_t*) drec->rec.eth.pload;
			}
			else if (ERF_TYPE_ATM == drec->type)
			{
				/* assuming RFC2225 Classical ATM over IP LLC/SNAP */
				r1 = (uint16_t*)(drec->rec.atm.pload + 8);
			}
			else
			{
				type_steerfail++;
				error_printf("Steer test cannot handle erf type %d\n", drec->type);
				break;
			}
			
			if (len < 8 * 2 + 4)
			{
				/* then there are not enough bytes in the captured payload to get an entire src ip and dest ip pair */
				not_enough_bytes = true;
			}
			else
			{
				srcip = (uint32_t*)(r1+6);
				destip = (uint32_t*)(r1+8);
				resxor = *destip ^ *srcip;
			}
			
			switch (steer)
			{
				case STEER_PARITY:
					parity = calc_parity( resxor );
					switch (dagstream)
					{
						case 0:
							if ( 0 != parity )
							{
								type_steerfail++;
								error_printf("steer %"PRIu64": parity error: srcip: 0x%x, destip: 0x%x, xor: 0x%x gives odd parity in stream %d\n",
												pc, *srcip, *destip, resxor, dagstream );
							}
							break;
						case 2:
							if ( 0 == parity )
							{
								type_steerfail++;
								error_printf("steer %"PRIu64": parity error: srcip: 0x%x, destip: 0x%x, xor: 0x%x gives even parity in stream %d\n",
												pc, *srcip, *destip, resxor, dagstream );
							}
							break;
						default:
							dagutil_panic("steer: invalid stream number %d for steering test\n", dagstream );
							break;
					}
					break;
					
				case STEER_CRC:
					if (!not_enough_bytes)
					{
						rescrc = dagcrc_aal5_crc( CRC_INIT, (char*)&resxor, 4 ); /* 32bit CRC */
					}
					else
					{
						/* If there were not enough bytes to form a src ip dst ip pair then we send it to stream 0.*/
						rescrc = 0;
					}
					switch ((rescrc & 0x07) % numrxstreams)
					{
						case 0: 
//							printf( "DEBUG : case 0 : crc: 0x%x, select %d, dagstream %d\n", rescrc, (rescrc & 0x07) % numrxstreams, dagstream );
							if (dagstream != 0)
							{
								type_steerfail++;
								error_printf("steer %"PRIu64": CRC error: srcip: 0x%x, destip: 0x%x, xor: 0x%x gives crc 0x%x and stream select %d in stream %d\n",
											pc, *srcip, *destip, resxor, rescrc, (rescrc & 0x07) % numrxstreams, dagstream );
							}
							break;
	
						case 1: 
//							printf( "DEBUG : case 1 : crc: 0x%x, select %d, dagstream %d\n", rescrc, (rescrc & 0x07) % numrxstreams, dagstream );
							if (dagstream != 2)
							{
								type_steerfail++;
								error_printf("steer %"PRIu64": CRC error: srcip: 0x%x, destip: 0x%x, xor: 0x%x gives crc 0x%x and stream select %d in stream %d\n",
											pc, *srcip, *destip, resxor, rescrc, (rescrc & 0x07) % numrxstreams, dagstream );
							}
							break;
	
						case 2: 
							if (dagstream != 4)
							{
								type_steerfail++;
								error_printf("steer %"PRIu64": CRC error: srcip: 0x%x, destip: 0x%x, xor: 0x%x gives crc 0x%x and stream select %d in stream %d\n",
											pc, *srcip, *destip, resxor, rescrc, (rescrc & 0x07) % numrxstreams, dagstream );
							}
							break;
	
						case 3: 
							if (dagstream != 6)
							{
								type_steerfail++;
								error_printf("steer %"PRIu64": CRC error: srcip: 0x%x, destip: 0x%x, xor: 0x%x gives crc 0x%x and stream select %d in stream %d\n",
											pc, *srcip, *destip, resxor, rescrc, (rescrc & 0x07) % numrxstreams, dagstream );
							}
							break;
						default : dagutil_panic("steer: test not supported for > 4 rx streams\n" );
					}
					break;
				case STEER_HLB_RANGE:
					if (!not_enough_bytes)
					{
						rescrc = dagcrc_aal5_crc( CRC_INIT, (char*)&resxor, 4 ); /* 32bit CRC */
					}
					else
					{
						/* If there were not enough bytes to form a src ip dst ip pair then we send it to stream 0.*/
						rescrc = 0;
					}
					{
						int crc_hlb_bit_value = rescrc&0x3ff;
						int test_loop_temp;
						int hlb_test_fail_temp;
						hlb_test_fail_temp=1;
						for(test_loop_temp=0;test_loop_temp<steer_hlb_count;test_loop_temp++)
						{
							if(crc_hlb_bit_value>=steer_hlb_min_reg[test_loop_temp]&&
								crc_hlb_bit_value<=steer_hlb_max_reg[test_loop_temp])
								hlb_test_fail_temp=0;
						}
						if(hlb_test_fail_temp)
						{
							type_steerfail++;
						    error_printf("steer %"PRIu64": srcip:0x%08x, dstip:0x%08x, "
                                         "gives xor:0x%08x crc:0x%08x hlb:%4d, error out of range:",
											pc, *srcip, *destip, resxor, rescrc, crc_hlb_bit_value);
                            for(test_loop_temp=0;test_loop_temp<steer_hlb_count;test_loop_temp++)
                            	error_printf(" %d(%d.%1d)-%d(%d.%1d) ",
                                            steer_hlb_min_reg[test_loop_temp],
                                            steer_hlb_min[test_loop_temp]/10,
                                            steer_hlb_min[test_loop_temp]%10,
                                            steer_hlb_max_reg[test_loop_temp],
                                            steer_hlb_max[test_loop_temp]/10,
                                            steer_hlb_min[test_loop_temp]%10);
                           error_printf("\n");
						}
					}
					break;
				default:
					dagutil_panic("steer: invalid steering option: %d\n", steer );
					break;
			}
		}
		break;
		
		case STEER_IFACE:
			switch (drec->type)
			{
				case ERF_TYPE_HDLC_POS:
				case ERF_TYPE_ETH:
				case ERF_TYPE_ATM:
					switch ( dagstream )
					{
						case 0: 
							if ( drec->flags.iface != 0 )
							{	type_steerfail++; 
								error_printf("steer %"PRIu64": IFACE error: iface %d in stream %d\n",
										pc, drec->flags.iface, dagstream );
							}
							break;
	
						case 2:
							if ( drec->flags.iface != 1 )
							{	type_steerfail++; 
								error_printf("steer %"PRIu64": IFACE error: iface %d in stream %d\n",
										pc, drec->flags.iface, dagstream );
							}
							break;
	
						case 4:
							if ( drec->flags.iface != 2 )
							{	type_steerfail++; 
								error_printf("steer %"PRIu64": IFACE error: iface %d in stream %d\n",
										pc, drec->flags.iface, dagstream );
							}
							break;
	
						case 6:
							if ( drec->flags.iface != 3 )
							{	type_steerfail++; 
								error_printf("steer %"PRIu64": IFACE error: iface %d in stream %d\n",
										pc, drec->flags.iface, dagstream );
							}
							break;
                            
						default:
							dagutil_panic("steer: test not supported for > 4 rx streams\n" );
							break;
					}
					break;
				
				default:
					break;
			}
			break;
			
		default:
			dagutil_panic("steer: invalid steering option: %d\n", steer );
			break;
	}
	
	return 0;
}

/* calc_parity : returns 0 or 1 depending on if we have even or odd parity respectively */
static int
calc_parity( uint32_t resxor )
{
	uint32_t parity = 0;
	
	do
	{
		parity += (resxor & 1);
	} while( resxor >>= 1 );

	return (parity & 1);
}

static int
test_ipid(char *rec, char *lastrec)
{
	static uint16_t oldipid[MAX_INTERFACES];
	
	uint16_t * r1 = NULL;
	uint16_t actual_ipid;
	uint16_t expected_ipid; /* For SmartBits modules that generate full 16-bit IP Id fields. */
	uint16_t expected_8bit_ipid; /* For SmartBits modules that generate only 8-bit IP Id fields. */
	dag_record_t * drec = (dag_record_t*)rec;

	r1 = (uint16_t *) find_iph(rec);
	if (r1 == NULL)
	{
		return warning;
	}
	
	/* offset to ipid field */
	r1 += 2; /* 2*sizeof(uint16_t) = 4bytes */

	/* Retrieve the record's IP Id. */
	actual_ipid = ntohs(*r1);
	
	/* Calculate what the record's IP Id should be. */
	expected_ipid = oldipid[intf] + 1;
	expected_8bit_ipid = expected_ipid & 0x00ff;
	
	/* Store the actual IP Id for next time. */
	oldipid[intf] = actual_ipid;

	/* Wait for (at least) the second record. */
	if (lastrec == NULL)
	{
		return 0;
	}

	/* Some SmartBits modules reset fields to 0 every 8 hours.  Don't fail because of this. */
	if (0 == actual_ipid)
	{
		return 0;
	}
	
	if (dagerf_is_modern_type((uint8_t*) rec))
	{
		/* Adjust for loss counter. */
		if (drec->lctr)
		{
			if (drec->lctr == 0xffff)
			{
				return 0;
			}

			expected_ipid += ntohs(drec->lctr);
			expected_8bit_ipid = expected_ipid & 0x00ff;
		}
	}
	else if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		/* No loss counter, but still supports the IP Id test. */
	}
	else
	{
		error_printf("ip %"PRIu64": IP Id test not supported for ERF type %d\n", pc, ((dag_record_t*) rec)->type);
		return 1;
	}
	
	if ((actual_ipid != expected_ipid) && (actual_ipid != expected_8bit_ipid))
	{
		ipid_fail++;
		error_printf("ipid %"PRIu64": IP-Id expected %d (16-bit IP Id) or %d (8-bit IP Id), got %d\n", pc, expected_ipid, expected_8bit_ipid, actual_ipid);
		return 1;
	}

	return 0;
}

enum {
	BUGS_MIN    = 0x01,
	BUGS_FIXED  = 0x01,
	BUGS_PKT    = 0x02,
	BUGS_WLEN   = 0x04,
	BUGS_PLOAD  = 0x08,
	BUGS_SLEN   = 0x10,
	BUGS_MAX    = 0x20
};


/* Test that the raw data is static.
 */
static int
test_static_data(char* rec, int len)
{

	dag_record_t* drec = (dag_record_t*)rec;
	uint8_t* pload = drec->rec.mc_raw_channel.pload;
	uint32_t payload_len;
	int i = 0;
	int retval = 0;

	switch ( drec->type )
	{
		case ERF_TYPE_MC_RAW:
			payload_len = ntohs(drec->wlen);
			for (i = 1; i < (signed)payload_len; ++i)
			{
		
				if (pload[i] != (pload[i-1]))
				{
					static_data_fail++;
					return 1;
				}
			}
			break;
		default:
			error_printf("static %"PRIu64": static_data not available in this record type\n", pc);
			return warning;
	}
	
	return retval;
}


/* Test that the raw data is incrementing. When using this test
 * dagmap should be configured to generate data that increments.
 */
static int
test_incr_data(char* rec, int len)
{
	static int hdlc_7e_count[MAX_CHANNELS];

	dag_record_t* drec = (dag_record_t*)rec;
	uint8_t* pload = drec->rec.mc_raw_channel.pload;
	uint32_t payload_len;
	uint16_t rlen, wlen;
	int i = 0;
	int retval = 0;
	uint32_t chan_id = (uint32_t) ntohl(drec->rec.mc_raw_channel.mc_header) & 0x1ff;
	connection = chan_id;

	switch ( drec->type )
	{
		case ERF_TYPE_MC_RAW:
			payload_len = ntohs(drec->rlen) - 20;
			for (i = 1; i < (signed)payload_len; ++i)
			{
				if (i == 1 && pload[i-1] == 0x7e)
					hdlc_7e_count[chan_id]++;
		
				if (pload[i] == 0x7e)
					hdlc_7e_count[chan_id]++;
				if (pload[i] != (pload[i-1] + 1))
				{
					/* Check if the difference is due to the fcs */
					if (i+2 < (signed)payload_len && pload[i+2] == 0x7e)
						continue;
				
					if (i+1 < (signed)payload_len && pload[i+1] == 0x7e)
						continue;
				
					/* If there have been a string of 0x7e then we can continue */
					if (hdlc_7e_count[chan_id] > 1)
						continue;
		
					/* Check for wrap around from 0xff to 0x0 */
					if (pload[i] == 0 && pload[i-1] == 0xff)
						continue;
		
					/* If we are at the end of the packet and there is a difference assume that
					 * this is caused by the fcs. We can't check for 0x7e because that will occur
					 * at the start of the next packet */
					if (i == payload_len - 2 || i == payload_len - 1)
						continue;

					/* Check if the difference is due to a bunch of 7e moving onto the start of a frame.
					 * If so then the difference can be ignored. */
//					if (i-2 > -1 && pload[i-1] == 0x7e && pload[i-2] == 0x7e)
//						continue;

					/* Check if the difference is due to 0xff preceding the start of a frame */
//					if (i-2 > -1 && pload[i-1] == 0xff && pload[i-2] == 0xff)
//						continue;

/*
					if (!((pload[i] == 0x7e && pload[i-1] == 0x7e) ||
						(pload[i] == 0xff && pload[i-1] == 0x0) ||
						(pload[i] == 0 && pload[i-1] == 0xff) ||
						(pload[i] == 0xff && pload[i-1] == 0xff)))
					{
						error_printf("current = %x prev = %x \n", pload[i]&0xff, pload[i-1]&0xff);
						return 1;
					}
*/
					error_printf("incr %"PRIu64": sequence broken - current = %x prev = %x \n", pc, pload[i]&0xff, pload[i-1]&0xff);
					incr_data_fail++;
					return 1;
				}
				if (pload[i] != 0x7e)
					hdlc_7e_count[chan_id] = 0;
			}
			break;

		case ERF_TYPE_MC_HDLC:
			payload_len = (uint32_t)(ntohs(drec->wlen)-(crclen>>3)) > (ntohl(drec->rlen) - 20) ?
				(ntohl(drec->rlen) - 20) : ntohs(drec->wlen)-(crclen>>3);
			
			test_incr_array(pload, payload_len);
			for (i = 1; i < (signed)payload_len; ++i) {
				if (pload[i] != (pload[i-1] + 1)) {
			
					/* Check for wrap around from 0xff to 0x0 */
					if (pload[i] == 0 && pload[i-1] == 0xff)
						continue;
					
					/* Otherwise Error */
					error_printf("incr %"PRIu64": sequence broken - current = %x prev = %x \n", pc, pload[i]&0xff, pload[i-1]&0xff);
					incr_data_fail++;
					return 1;
				}
			}
			break;


			
		case ERF_TYPE_AAL2:
			/* Check for this not being the first frame for this VC  */
			if(IsFirstFrame(drec, drec->type))
				return 0;
			
			/* Sanity check to see if we have any payload */
			rlen = ntohs(drec->rlen);
			wlen = ntohs(drec->wlen);
			if(rlen <= 24)
				return 0;
			
			/* Check if the record has been snapped */
			payload_len = (uint32_t)(wlen > (rlen - 20) ? (rlen - 24) : (wlen - 4));

			/* Test the actual payload */
			retval = test_incr_array(drec->rec.aal2.pload, payload_len);
			if ( retval != 0 )
				incr_data_fail++;

			break;
			
		case ERF_TYPE_AAL5:
			/* Sanity check to see if we have any payload */
			rlen = ntohs(drec->rlen);
			wlen = ntohs(drec->wlen);
			if(rlen <= 20)
				return 0;

			/* Check if the record has been snapped */
			payload_len = (uint32_t)(wlen > (rlen - 16) ? (rlen - 20) : (wlen - 4));
				
			/* Test the actual payload */
			retval = test_incr_array(drec->rec.aal5.pload, payload_len);
			if ( retval != 0 )
				incr_data_fail++;

			break;
		
		case ERF_TYPE_ATM:
			/* Sanity check to see if we have any payload */
			rlen = ntohs(drec->rlen);
			wlen = ntohs(drec->wlen);
			if(rlen <= 20)
				return 0;

			/* Check if the record has been snapped */
			payload_len = (uint32_t)(wlen > (rlen - 16) ? (rlen - 20) : (wlen - 4));

			/* Test the actual payload */
			retval = test_incr_array(drec->rec.atm.pload, payload_len);
			if ( retval != 0 )
				incr_data_fail++;

			break;
		
		


			
		
		case ERF_TYPE_MC_AAL2:	/*These drop through to TYPE_MC_ATM for the actual test */
		case ERF_TYPE_MC_AAL5:
			/*Check for this not being the first frame for this VC  */
			if(IsFirstFrame(drec, drec->type))
				return 0;

		case ERF_TYPE_MC_ATM:	/* This drop through is on purpose, the system should be the same */
			payload_len = (uint32_t)(ntohs(drec->wlen)) > (ntohl(drec->rlen) - 20) ?
				(ntohl(drec->rlen) - 20) : ntohs(drec->wlen);
			
			for (i = 5; i < (signed)payload_len; ++i) {
				if (pload[i] != (pload[i-1] + 1)) {
			
					
					/* Check for wrap around from 0xff to 0x0 */
					if (pload[i] == 0)
						continue;
		

					/* Otherwise Error */
					error_printf("incr %"PRIu64": sequence broken - current = %x prev = %x \n", pc, pload[i]&0xff, pload[i-1]&0xff);
					incr_data_fail++;
					return 1;
				}
			}
			break;

		case TYPE_PAD:
			break;

		default:
			error_printf("incr %"PRIu64": incr not available in this record type\n", pc);
			return warning;
	}
	
	return retval;
}

static int
test_ima_cnt(char* rec, int len)
{
	dag_record_t* drec = (dag_record_t*)rec;
	uint8_t* pload = drec->rec.mc_raw_channel.pload;
	uint32_t conn_num;
	uint32_t errs;
	
	errs = (uint32_t)ntohl(drec->rec.mc_atm.mc_header)>>24;
	conn_num = (uint32_t)ntohl(drec->rec.mc_raw_channel.mc_header) & 0x1ff;
	connection = conn_num;
	
	if (errs & MC_ATM_OAM)
	{
		if ((pload[5] & 0x80) && ((pload[4] == 3) || (pload[4] == 1)) 
			&& (pload[48] == 0x6a) && ((pload[50] & 0xfc) == 0))
		{
			mc_ima_count[conn_num].icp_cells++;
		}
		else
		{
			mc_ima_count[conn_num].idle_cells++;
		}
	}
	else
	{
		mc_ima_count[conn_num].data_cells++;
	}
	
	return 0;
}

typedef struct btx {
	int             seen, resync;
	uint32_t        pkt, word;
} btx_t;

static int
test_btx(char *rec, int len)
{
	static btx_t btxi[MAX_INTERFACES];
	static uint32_t drop;
	
	legacy_pos_t* prec = (legacy_pos_t*) rec;
	dag_record_t* drec = (dag_record_t*)rec;
	int i;
	int mask;
	int bugs = 0;
	uint32_t* pload;
	uint32_t payload_len;
	int do_btx_test = 0;
	int padding = 2;	// bytes	

	if (!btxi[intf].seen) {
		btxi[intf].seen = 1;
		btxi[intf].resync = 1;
	}

	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		switch (tt)
		{
			case TT_POS:
				pload = prec->pload;
				if (btxi[intf].resync) {
					btxi[intf].resync = 0;
					btxi[intf].pkt  = ntohl(pload[1]);
					btxi[intf].word = ntohl(pload[3]);
					/*error_printf("*** %d resync, word: %08x\n", btxi[intf].pkt, btxi[intf].word);*/
					drop = ntohs(prec->loss);
				}
				bugs = 0;
				
				if (ntohl(pload[0]) != 0x0f000800)
				{
					bugs |= BUGS_FIXED;
				}
				
				if (ntohl(pload[1]) - btxi[intf].pkt != (ntohs(prec->loss)) - drop  )
				{
					bugs |= BUGS_PKT;
				}
				else
				{
					btxi[intf].pkt++;
					drop = ntohs(prec->loss);
				}
				
				if (ntohl(pload[2]) != (ntohs(prec->wlen)-(crclen>>3)))
				{
					/* wire length */
					bugs |= BUGS_WLEN;
				}
				/* Calculate the length of the record payload */
				payload_len = (uint32_t)(ntohs(prec->wlen)-(crclen>>3)) > ntohl(prec->slen) - 16 ?
					(ntohl(prec->slen) - 16) : ntohs(prec->wlen)-(crclen>>3);
				
				/* Check payload length */
				if ((payload_len > 0x40)-(crclen>>3))
				{
					bugs |= BUGS_SLEN;
	
					/* Check fill words */
					for ( i = 3 ; (uint16_t)i < (payload_len/sizeof(uint32_t)) ; i++, btxi[intf].word++)
					{
						if (ntohl(pload[i]) != btxi[intf].word)
						{
							/*
							  error_printf( ": %08x %08x %i\n", ntohl(cell.pload[i]), word, ntohl(cell.pload[i]) - word ); 
							*/
							bugs |= BUGS_PLOAD;
						}
					}
					
					/* Compensate for the words truncated by the packet processor */
					btxi[intf].word += (ntohs(prec->wlen) - payload_len ) / 4;
					
					/* Compensate for the extra word present in the partial word 
					   at the end of the packet */
					if ( ntohs((uint16_t)prec->slen) % 4 != 0 )
						btxi[intf].word++;
				}
				break;
			
			default:
				error_printf("btx %"PRIu64": btx test not implemented for this type yet\n", pc);
				return 1;
		}
	}
	else if (dagerf_is_ethernet_type((uint8_t*) rec))
	{
		pload = (uint32_t *)(drec->rec.eth.pload + 4);
		if (ntohl(*(pload-1)) != 0x0f000800)
		{
			bugs |= BUGS_FIXED;
		}
		do_btx_test = 1;
	}
	else if (dagerf_is_pos_type((uint8_t*) rec))
	{
		pload = (uint32_t *)drec->rec.pos.pload;

		if (ntohl(drec->rec.pos.hdlc) != 0x0f000800)
		{
			bugs |= BUGS_FIXED;
		}
		do_btx_test = 1;
	}
	else if (ERF_TYPE_ATM == drec->type)
	{
		pload = (uint32_t *)drec->rec.atm.pload;
		do_btx_test = 1;
	}
	else
	{
		error_printf("btx %"PRIu64": btx test not implemented for ERF type %d\n", pc, ((dag_record_t*)rec)->type);
		return 1;
	}

	if (do_btx_test)
	{
		/* NOTE: Current packet = sequence # - loss counter
		   If there's no lost packets, current packet = sequence #
		   Also, pload[0] = sequence #
			 pload[1] = payload length (less 4-byte CRC and 64-bit alignment)
			 pload[2]... = 32-bit word of the payload
			 btxi[intf].pkt = current packet pointer
		*/ 

		if (ntohs(drec->lctr) != 0)	// If packets have been lost
		{
			btxi[intf].word = ntohl(pload[2]);
			if (ntohs(drec->lctr) == 0xffff)
			{
				btxi[intf].pkt  = ntohl(pload[0]) - 0xffff;	// current packet = sequence number - lctr
			}
		}
		
		if (btxi[intf].resync)	// A resynchronise needs to be done as the equation is no longer true based on the previous packet that got tested
		{
			btxi[intf].resync = 0;
			btxi[intf].pkt  = ntohl(pload[0]) - ntohs(drec->lctr);
			btxi[intf].word = ntohl(pload[2]);
		}
		
		bugs = 0;
		if (ntohl(pload[0]) < btxi[intf].pkt)
			btxi[intf].resync = 1;
		else if (ntohl(pload[0]) - btxi[intf].pkt != (ntohs(drec->lctr) ))	// Check the equation, if no longer true then resynchronise
		{
			bugs |= BUGS_PKT;
			btxi[intf].resync = 1;
		}
		
		/* now testing of pkt is done, set it up for next time */
		btxi[intf].pkt  = ntohl(pload[0]) + 1;	
	
		if (drec->type == ERF_TYPE_ETH)
		{
			if (ntohl(pload[1]) != ntohs(drec->wlen)-(crclen>>3)-14-(crc_correction>>3))   /* ethernet header = 14 bytes */
				bugs |= BUGS_WLEN;	
		}
		else
		{
			if (ntohl(pload[1]) != ntohs(drec->wlen)-(crclen>>3)-(crc_correction>>3))
				bugs |= BUGS_WLEN;
			//printf ("TEST! pload: %d, ntohs(drec->wlen): %d, crclen>>3: %d, crc_correction>>3: %d\n", ntohl(pload[1]), ntohs(drec->wlen), crclen>>3 ,crc_correction>>3);

		}

		/* Calculate the length of the record payload */
		payload_len = ntohs(drec->rlen) - dag_record_size - (crclen>>3);
		if ((uint32_t)(ntohs(drec->wlen)-(crclen>>3)) < payload_len)		// for fixed length records
			payload_len = ntohs(drec->wlen)-(crclen>>3);
		payload_len -= 4; /* adjust for PoS HDLC header or ATM header */
		if (drec->type == ERF_TYPE_ETH)
			payload_len -= 14; /* another 14 for Ethernet to make 18 */
		
		/* Check fill words in the packet payload */
		for ( i = 2 ; i < (int)(payload_len/sizeof(uint32_t)) ; i++, btxi[intf].word++)
		{
			if (ntohl(pload[i]) != btxi[intf].word)
			{
				bugs |= BUGS_PLOAD;
				btxi[intf].word = ntohl(pload[i]);	// resync the tested payload word to the current payload word

			}
		}

		/* Check remaining partial word, if any */
		switch (payload_len % 4)
		{
			case 0:
				break;
			
			case 1:
				if ((ntohl(pload[i])&0xff000000) != (btxi[intf].word&0xff000000))
					bugs |= BUGS_PLOAD;
				break;
			
			case 2:
				if ((ntohl(pload[i])&0xffff0000) != (btxi[intf].word&0xffff0000))
					bugs |= BUGS_PLOAD;
				break;
			
			case 3:
				if ((ntohl(pload[i])&0xffffff00) != (btxi[intf].word&0xffffff00))
					bugs |= BUGS_PLOAD;
				break;
			
			default:
				dagutil_panic("internal error in %s line %u\n", __FILE__, __LINE__);
				break;
		}

		/* Compensate for the words truncated by the packet processor */
		if (drec->type == ERF_TYPE_ETH)
			btxi[intf].word += (ntohs(drec->wlen) - (crclen>>3) - (payload_len+4+14) + padding) / 4;  // Remember to take into account padding during align64!
														  // And remove adjustment for PoS HDLC/ATM and/or Ethernet headers 
		else
			btxi[intf].word += (ntohs(drec->wlen) - (crclen>>3) - (payload_len+4) ) / 4;


		/* Compensate for the extra word present in the partial word at the end of the packet */
		if (drec->type == ERF_TYPE_ETH)
		{
			if (((ntohs(drec->wlen)- 14 -(crclen>>3)) % 4) != 0 )	// Remove adjustment for Ethernet header 
				btxi[intf].word++;
		}
		else
		{
			if (((ntohs(drec->wlen) - (crclen>>3)) % 4) != 0 )
				btxi[intf].word++;
		}
		//printf ("\nTEST: btxi[intf].word: 0x%x\n\n", btxi[intf].word);

	}

	if (bugs)
	{
		error_printf("btx %"PRIu64": seq %lu bugs: ", pc, (int32_t)ntohl(prec->pload[1]));
		for ( mask = BUGS_MIN ; mask < BUGS_MAX ; mask <<= 1 )
		{
			switch (bugs&mask)
			{
				case 0:
					continue;
				
				case BUGS_FIXED:
					btx_fixed_fail++;
					error_printf("fixed ");
					break;
				
				case BUGS_PKT:
					btx_pkt_fail++;
					error_printf("pktcount ");
					break;
				
				case BUGS_WLEN:
					btx_wlen_fail++;
					error_printf("wlenmatch ");
					break;
				
				case BUGS_PLOAD:
					btx_pload_fail++;
					error_printf("payloadcount ");
					break;
				
				case BUGS_SLEN:
					btx_slen_fail++;
					error_printf("snaplen ");
					break;
				
				default:
					dagutil_panic("internal error %s line %u\n", __FILE__, __LINE__);
					break;
			}
		}
		error_printf("\n");
		return 1;
	}
	return 0;
}

#define lfsr(X) ( (X >> 1) | (( ((X & 0x01) != 0) ^ ((X & 0x03) != 0)) << 7) )

static int
test_lfsr(char *rec, int len)
{
	static uint8_t bseq[MAX_INTERFACES], bseed[MAX_INTERFACES];
	
	uint8_t c;
	int retval = 0, i;

	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		switch (tt)
		{
			case TT_ATM:
				break;
			
			default:
				error_printf("lfsr %"PRIu64": lfsr test not implemented for this type yet\n", pc);
				return 1;
		}
	}
	else
	{
		error_printf("lfsr %"PRIu64": lfsr test not implemented for ERF type %d\n", pc, ((dag_record_t*)rec)->type);
		return 1;
	}

	if (rec[12] != (bseq[intf] + 1)) {
		lfsr_fail++;
		error_printf("lfsr %"PRIu64": expected seq %u got %u\n",
					 pc, (bseq[intf] + 1), rec[12]);
		retval = 1;
	}
	bseq[intf] = rec[12];
	
	for (i=13; i<=len; i++) {
		c = rec[i] & 0xff;
		if ( c != lfsr(bseed[intf])) {
			lfsr_fail++;
			error_printf("lfsr %"PRIu64": byte %u expected 0x%02x got 0x%02x\n",
						 pc, i, lfsr(bseed[intf])&0xff, c);
			retval = 1;	
		}
		bseed[intf] = c;
	}

	return retval;
}

static int
test_lctr(char *rec, int len)
{
	static int last_loss;
	
	legacy_pos_t * prec = (legacy_pos_t*) rec;
	dag_record_t * drec = (dag_record_t*) rec;
	int loss;

	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		switch (tt)
		{
			case TT_POS:
				if (ntohs(prec->loss) != last_loss)
				{
					if (ntohs(prec->loss) > last_loss)
						loss = ntohs(prec->loss) - last_loss;
					else
						loss = ntohs(prec->loss) + 0x10000 - last_loss;
					
					total_loss += loss;
					last_loss = loss;
					error_printf("lctr %"PRIu64": loss %u total loss %"PRIu64"\n",
								 pc, loss, total_loss);
					return warning;
				}
				return 0;

			default:
				error_printf("lctr %"PRIu64": lctr not available in this record type\n", pc);
				return warning;
		}
	}
	else if (dagerf_is_modern_type((uint8_t*) rec) && (!dagerf_is_color_type((uint8_t*) rec)))
	{
		if (drec->lctr)
		{
			total_loss += ntohs(drec->lctr);
			if (drec->lctr == 0xffff)
				greater = 1;
			error_printf("lctr %"PRIu64": loss %u total loss %s%"PRIu64"\n",
						 pc, ntohs(drec->lctr), greater ? ">= " : "", total_loss);
			return warning;
		}
		
		return 0;
	}
	else
	{
		error_printf("lctr %"PRIu64": lctr not available in ERF type %d\n", pc, drec->type);
		return warning;
	}
	
	return 0;
}

static int
test_fcs(char *rec, int len)
{
	dag_record_t    *drec = (dag_record_t*)rec;
	uint32_t   acc = 0x00;
	uint16_t             ppp16;
	uint32_t             ppp32;
	static uint32_t   crc_accum[MAX_INTERFACES];
	static int      ignore_frame[MAX_INTERFACES];
	int             ignore_cell = 0;
	int             retval = 0, count;
	uint8_t rec_type ;
	uint32_t vpi, vci, pti, crc_tmp;
	uint16_t erf_wlen = 0;
	uint32_t ext_hdrs = 0;
	uint8_t* tp;
	dag_record_t    *ext_hdr_offsetted_drec = NULL;/*to accces fields after ERF header in case of extn hdr*/


	if (pc==1)
	{
		for (count=0;count<MAX_INTERFACES;count++)
		{
			crc_accum[count] = CRC_INIT;
			ignore_frame[count] = 1;
		}
	}
	rec_type = drec->type & 0x7f;
	ext_hdrs = dagerf_ext_header_count ((uint8_t*)rec, len);
	erf_wlen = ntohs(drec->wlen);
	ext_hdr_offsetted_drec = (dag_record_t*)((uintptr_t)rec + (ext_hdrs * 8));
	if (dagerf_is_ethernet_type((uint8_t*) rec))
	{
		if ( ext_hdrs > 0)
		{
			/* hack . dont use any field of ERF header using 'drec' */
			drec = (dag_record_t *) ((uintptr_t)rec + (ext_hdrs * 8));
		}
		if ( erf_wlen > (len - dag_record_size - 2 - ( 8 *ext_hdrs))) { /* rlen - hdr - pad - extn hdr size*/
			if (warning)
				error_printf("fcs %"PRIu64": insufficient captured packet length %d needed %d\n",
					     pc, (len - dag_record_size - 2 - ( 8 * ext_hdrs)), erf_wlen);
			fcs_slen++;
			return warning;
		}
		
		if (erf_wlen - 4 < 0) {
			if (warning)
				error_printf("fcs %"PRIu64": insufficient captured packet length %d\n",
					     pc, erf_wlen);
			fcs_slen++;
			return warning;
		}
		
		acc = dagcrc_ethernet_crc32_r( 0x0UL, drec->rec.eth.dst, erf_wlen - 4 ); /* 4 CRC */
//			error_printf("DEBUG fcs %"PRIu64": Ethernet FCS check calc: 0x%08lx frame: 0x%08lx\n",
//				   pc, acc, *(uint32_t*)(drec->rec.eth.dst+ntohs(drec->wlen)-4));
		if (acc != *(uint32_t*)(drec->rec.eth.dst+ erf_wlen -4)) {
			fcs_fail++;
			error_printf("fcs %"PRIu64": Ethernet FCS failure calc: 0x%08lx frame: 0x%08lx\n",
				     pc, acc, *(uint32_t*)(drec->rec.eth.dst+ erf_wlen -4));
			return 1;
		} else {
			fcs_pass++;
		}
		
		return 0;
	}
	else if (ERF_TYPE_ATM == (rec_type&0x7f))
	{
		vpi = (uint32_t)(ntohl(ext_hdr_offsetted_drec->rec.atm.header)>>20)&0xff;
		vci = (uint32_t)(ntohl(ext_hdr_offsetted_drec->rec.atm.header)>>4)&0xffff;
		pti = (uint32_t)(ntohl(ext_hdr_offsetted_drec->rec.atm.header)>>1) & 0x7;
		/* Don't try to calculate CRC on OAM cells, they're not AAL5! */
		if (((vpi == 0) && (vci < 32)) || ((pti & 0x4) == 0x4)) 
			ignore_cell = 1;
	
		if (drec->lctr)
			ignore_frame[intf]++;
		
		ignore_cell += ignore_frame[intf];
	
		crc_accum[intf] = dagcrc_aal5_crc(crc_accum[intf], (char *)ext_hdr_offsetted_drec->rec.atm.pload, 48);
		if (ntohl(ext_hdr_offsetted_drec->rec.atm.header) & 0x02) {
			/* end of frame */
			if ( (!ignore_cell) && (crc_accum[intf] != CRC_PASS)) {
				fcs_fail++;
				error_printf("fcs %"PRIu64": ATM AAL5 FCS failure calc: 0x%08lx\n", pc, crc_accum[intf]);
				retval = 1;
			} else if (ignore_frame[intf])
				fcs_ignored++;
			else
				fcs_pass++;
			ignore_frame[intf] = 0;
			crc_accum[intf] = CRC_INIT;
		}
		return retval;
	}
	else if (dagerf_is_pos_type((uint8_t*) rec))
	{
		if ( ext_hdrs > 0)
		{
			/* hack . dont use any field of ERF header using 'drec' */
			drec = (dag_record_t *) ((uintptr_t)rec + (ext_hdrs * 8));
		}

		if ( erf_wlen > (len - dag_record_size - ( 8 * ext_hdrs))) { /* rlen - hdr - extn hdr size*/
			if (warning)
				error_printf("fcs %"PRIu64": insufficient captured packet length %d needed %d\n",
					     pc, (len - dag_record_size -( 8 * ext_hdrs) ), erf_wlen);
			fcs_slen++;
			return warning;
		}
		
		switch (crclen)
		{
		case 0:
			if (warning)
				error_printf("fcs %"PRIu64": crc not available to test!", pc);
			return warning;
			
		case 16:
			ppp16 = dagcrc_ppp_fcs16( PPPINITFCS16, (uint8_t*)(&(drec->rec.pos.hdlc)), erf_wlen);
			if ( ppp16 != PPPGOODFCS16 ) {
				fcs_fail++;
				error_printf("fcs %"PRIu64": POS FCS16 failure calc: 0x%04x\n", pc, ppp16);
				return 1;
			} else {
				fcs_pass++;
			}
			return 0;
			
		case 32:
			ppp32 = dagcrc_ppp_fcs32( PPPINITFCS32, (uint8_t*)(&(drec->rec.pos.hdlc)), erf_wlen);
			if ( ppp32 != PPPGOODFCS32 ) {
				fcs_fail++;
				error_printf("fcs %"PRIu64": POS FCS32 failure calc: 0x%08lx\n", pc, ppp32);
				return 1;
			} else {
				fcs_pass++;
			}
			return 0;
			
		default:
			assert(0); /* Invalid CRC length, shouldn't have got here. */
			break;
		}
	}
	else if (ERF_TYPE_AAL5 == rec_type)
	{
		if ( (ntohs(drec->wlen)-4) > (len - dag_record_size)) { /* rlen - hdr - ATM header */
			if (warning)
				error_printf("fcs %"PRIu64": insufficient captured packet length %d needed %d\n",
					     pc, (len - dag_record_size), ntohs(drec->wlen)+4);
			fcs_slen++;
			return warning;
		}
		
		if (dagcrc_aal5_crc(CRC_INIT, (char *)drec->rec.aal5.pload, ntohs(drec->wlen)-4) == CRC_PASS) {
			fcs_pass++;
			return retval;
		}
		
		crc_tmp = ntohl(*(uint32_t *)(drec->rec.aal5.pload + ntohs(drec->wlen)-56));
		if (dagcrc_aal5_crc(dagcrc_aal5_crc(CRC_INIT, (char *)drec->rec.aal5.pload, ntohs(drec->wlen)-56), (char *) &crc_tmp, 4) == CRC_PASS) {
			/* Need to check PTI of last cell */
			if (((uint32_t)(ntohl(*(uint32_t *)(drec->rec.aal5.pload + ntohs(drec->wlen)-44))>>1)&0x7)==1) { 
				error_printf("fcs %"PRIu64": AAL5 AAL5 FCS partial pass (payload before PTI cell passed progessive CRC)\n", pc);
				fcs_pass++;
				return retval;
			}
		}
		
		fcs_fail++;
		error_printf("fcs %"PRIu64": AAL5 AAL5 FCS failure\n", pc);
		return 1;
	}
	else if (ERF_TYPE_MC_HDLC == rec_type)
	{
		connection = (uint32_t)ntohl(drec->rec.mc_hdlc.mc_header)&0x1ff;
		if ( ntohs(drec->wlen) > (len - dag_record_size - 4)) { /* rlen - hdr */
			if (warning)
				error_printf("fcs %"PRIu64": insufficient captured packet length %d needed %d\n",
					     pc, (len - dag_record_size - 4), ntohs(drec->wlen));
			fcs_slen++;
			return warning;
		}
		switch (crclen)
		{
		case 0:
			if (warning)
				error_printf("fcs %"PRIu64": crc not available to test!", pc);
			return warning;
			
		case 16:
			ppp16 = dagcrc_ppp_fcs16( PPPINITFCS16, (uint8_t*)(&(drec->rec.mc_hdlc.pload)), ntohs(drec->wlen));
			if ( ppp16 != PPPGOODFCS16 ) {
				chan_fail = connection;
				fcs_fail++;
				error_printf("fcs %"PRIu64": POS FCS16 failure calc: 0x%04x\n", pc, ppp16);
				return 1;
			} else
				fcs_pass++;
			return 0;
			
		case 32:
			ppp32 = dagcrc_ppp_fcs32( PPPINITFCS32, (uint8_t*)(&(drec->rec.mc_hdlc.pload)), ntohs(drec->wlen));
			if ( ppp32 != PPPGOODFCS32 ) {
				chan_fail = connection;
				fcs_fail++;
				error_printf("fcs %"PRIu64": POS FCS32 failure calc: 0x%08lx\n", pc, ppp32);
				return 1;
			} else
				fcs_pass++;
			return 0;
			
		default:
			assert(0); /* Should never get here. */
			break;
		}
	}
	else if (ERF_TYPE_MC_ATM == rec_type)
	{
		tp = drec->rec.mc_atm.pload;
		vpi = (uint32_t)(ntohl(*(int*) tp)>>20)&0xff;
		vci = (uint32_t)(ntohl(*(int*) tp)>>4)&0xffff;
		pti = (uint32_t)(ntohl(*(int*) tp)>>1) & 0x7;
		connection = (uint32_t)ntohl(drec->rec.mc_atm.mc_header)&0x1ff;
		/* Don't try to calculate CRC on OAM cells, they're not AAL5! */
		if (((vpi == 0) && (vci < 32)) || ((pti & 0x4) == 0x4)) 
			ignore_cell = 1;

		if (drec->lctr)
			ignore_frame[intf]++;
	
		ignore_cell += ignore_frame[intf];

		crc_accum[intf] = dagcrc_aal5_crc(crc_accum[intf], (char *)drec->rec.mc_atm.pload+4, 48);
		tp = drec->rec.mc_atm.pload;
		if (ntohl(*(int*) tp) & 0x02) {
			/* end of frame */
			if ( (!ignore_cell) && (crc_accum[intf] != CRC_PASS)) {
				fcs_fail++;
				chan_fail = connection;
				error_printf("fcs %"PRIu64": ATM AAL5 FCS failure calc: 0x%08lx\n", pc, crc_accum[intf]);
				retval = 1;
			} else if (ignore_frame[intf])
				fcs_ignored++;
			else
				fcs_pass++;
			ignore_frame[intf] = 0;
			crc_accum[intf] = CRC_INIT;
		}
		return retval;
	}
	else if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		if (warning) {
			error_printf("fcs %"PRIu64": fcs not supported in legacy ERF types\n", pc);
		}
		fcs_unsupported++;
		return warning;
	}
	else if ( ERF_TYPE_INFINIBAND == rec_type )
	{
		if ( ntohs(drec->wlen) > (len - dag_record_size )) /* rlen - hdr  */
		{
			if (warning)
				error_printf("fcs %"PRIu64": insufficient captured packet length %d needed %d\n", pc, (len - dag_record_size ), ntohs(drec->wlen));
			fcs_slen++;
			return warning;
		}
		return test_infiniband_crc(drec);
	}
	else if (0 == dagerf_is_known_type((uint8_t*) rec))
	{
		if(warning)
			error_printf("fcs %"PRIu64": unsupported ERF type %s (%d)\n", pc, dagerf_type_to_string(drec->type, 0),drec->type);
		fcs_unsupported++;
		return warning;
	}
	
	return 0;
}

static int
test_flags(char *rec, int len)
{
	uint32_t mch_flags;
	dag_record_t    *drec = (dag_record_t*)rec;
	int retval = 0, chan;
	
	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		error_printf("flags %"PRIu64": flags not available in this record type\n", pc);
		return 0;
	}


	/* Assumes trunc, dserror and rxerror exist for ALL erf types */
	/* Prevents code replication in switch */
	if (drec->flags.trunc)
	{
		truncated++;
		if (warning)
			error_printf("flags %"PRIu64": trunc set   total %"PRId64"\n", pc, truncated);
		retval = warning;
	}
	
	if (drec->flags.dserror)
	{
		dserror++;
		error_printf("flags %"PRIu64": dserror set total %"PRId64"\n", pc, dserror);
		retval = 1;
	}
	
	if (drec->flags.rxerror)
	{
		rxerror++;
		error_printf("flags %"PRIu64": rxerror set total %"PRId64"\n", pc, rxerror);
		retval = 1;
	}

	switch (drec->type & 0x7F)	// NOTE: We need to mask off the extension header bit here
	{
		case ERF_TYPE_ETH:
		case ERF_TYPE_COLOR_ETH:
		case ERF_TYPE_COLOR_HASH_ETH:
		case ERF_TYPE_DSM_COLOR_ETH:
		case ERF_TYPE_ATM:
		case ERF_TYPE_HDLC_POS:
		case ERF_TYPE_COLOR_HDLC_POS:
		case ERF_TYPE_COLOR_HASH_POS:
		case ERF_TYPE_DSM_COLOR_HDLC_POS:
		case ERF_TYPE_AAL5:
		case ERF_TYPE_AAL2:
		case ERF_TYPE_MC_RAW:
		case TYPE_PAD:
        	case ERF_TYPE_INFINIBAND:
		case ERF_TYPE_RAW_LINK:
			break;
		
		case ERF_TYPE_MC_HDLC:
			chan = (uint32_t)ntohl(drec->rec.mc_hdlc.mc_header)&0x1ff;
			connection = chan;
			mch_flags = ((uint32_t)ntohl(drec->rec.mc_hdlc.mc_header)>>24)&0x3f;
			
			mc_hdlc_total.frames++;
			mc_hdlc_channel[chan].frames++;
			
			if (mch_flags) {
				if (mch_flags & MC_HDLC_FCS) {
					chan_fail = connection;
					mc_hdlc_total.fcs_errors++;
					mc_hdlc_channel[chan].fcs_errors++;
					error_printf("flags %"PRIu64": mc_hdlc fcs error set total %"PRId64"\n", pc, mc_hdlc_total.fcs_errors);
				}
				if (mch_flags & MC_HDLC_SHORT) {
					chan_fail = connection;
					mc_hdlc_total.short_errors++;
					mc_hdlc_channel[chan].short_errors++;
					error_printf("flags %"PRIu64": mc_hdlc short error set total %"PRId64"\n", pc, mc_hdlc_total.short_errors);
				}
				if (mch_flags & MC_HDLC_LONG) {
					chan_fail = connection;
					mc_hdlc_total.long_errors++;
					mc_hdlc_channel[chan].long_errors++;
					error_printf("flags %"PRIu64": mc_hdlc long error set total %"PRId64"\n", pc, mc_hdlc_total.long_errors);
				}
				if (mch_flags & MC_HDLC_ABORT) {
					chan_fail = connection;
					mc_hdlc_total.abort_errors++;
					mc_hdlc_channel[chan].abort_errors++;
					error_printf("flags %"PRIu64": mc_hdlc abort error set total %"PRId64"\n", pc, mc_hdlc_total.abort_errors);
				}
				if (mch_flags & MC_HDLC_OCTET) {
					chan_fail = connection;
					mc_hdlc_total.octet_errors++;
					mc_hdlc_channel[chan].octet_errors++;
					error_printf("flags %"PRIu64": mc_hdlc octet error set total %"PRId64"\n", pc, mc_hdlc_total.octet_errors);
				}
				if (mch_flags & MC_HDLC_LOST) {
					chan_fail = connection;
					mc_hdlc_total.lost_errors++;
					mc_hdlc_channel[chan].lost_errors++;
					error_printf("flags %"PRIu64": mc_hdlc lost error set total %"PRId64"\n", pc, mc_hdlc_total.lost_errors);
				}
				retval = 1;
			}
			break;
		
		case ERF_TYPE_MC_ATM:
			chan = (uint32_t)ntohl(drec->rec.mc_atm.mc_header)&0x1ff;
			connection = chan;
			mch_flags = ((uint32_t)ntohl(drec->rec.mc_atm.mc_header)>>24)&0x3f;
			
			mc_atm_total.cells++;
			mc_atm_channel[chan].cells++;
			
			if (mch_flags) {
				if (mch_flags & MC_ATM_LOST) {
					chan_fail = connection;
					mc_atm_total.lost_errors++;
					mc_atm_channel[chan].lost_errors++;
					retval = 1;
					error_printf("flags %"PRIu64": mc_atm lost error set total %"PRId64"\n", pc, mc_atm_total.lost_errors);
				}
				if (mch_flags & MC_ATM_HECC) {
					chan_fail = connection;
					mc_atm_total.hecs_corrected++;
					mc_atm_channel[chan].hecs_corrected++;
					/* not an error */
				}
				if (mch_flags & MC_ATM_OAMCRC) {
					chan_fail = connection;
					mc_atm_total.oam_crc_errors++;
					mc_atm_channel[chan].oam_crc_errors++;
					error_printf("flags %"PRIu64": mc_atm OAM crc error set total %"PRId64"\n", pc, mc_atm_total.oam_crc_errors);
					retval = 1;
				}
				if (mch_flags & MC_ATM_OAM) {
					mc_atm_total.oam_cells++;
					mc_atm_channel[chan].oam_cells++;
					/* not an error*/
				}
			}
			break;

		case ERF_TYPE_MC_AAL5:
			chan = (uint32_t)ntohl(drec->rec.mc_aal5.mc_header)&0x1ff;
			connection = chan;
			mch_flags = ((uint32_t)ntohl(drec->rec.mc_aal5.mc_header)>>16);

			mc_aal5_total.cells++;
			mc_aal5_channel[chan].cells++;
			
			if (mch_flags) {
				if (mch_flags & MC_AAL5_CRC_ERROR) {
					chan_fail = connection;
					mc_aal5_total.crc_errors++;
					mc_aal5_channel[chan].crc_errors++;
					retval = 1;
					error_printf("flags %"PRIu64": mc_aal5 CRC error set total %"PRId64"\n", pc, mc_aal5_total.crc_errors);
				}
				if (mch_flags & MC_AAL5_LEN_ERROR) {
					chan_fail = connection;
					mc_aal5_total.length_error++;
					mc_aal5_channel[chan].length_error++;
					error_printf("flags %"PRIu64": mc_aal5 length error set total %"PRId64"\n", pc, mc_aal5_total.length_error);
					retval = 1;
				}
			}

			/* If this is the first frame, dont return 1 */
			if(((uint32_t)ntohl(drec->rec.mc_aal5.mc_header)>>24) & MC_AAL5_1ST_CELL)
				retval = 0;
			break; 

		case ERF_TYPE_MC_AAL2:
			chan = (uint32_t)ntohl(drec->rec.mc_aal2.mc_header)&0x1ff;
			connection = chan;
			mch_flags = ((uint32_t)ntohl(drec->rec.mc_aal2.mc_header)>>16);
			
			mc_aal2_total.cells++;
			mc_aal2_channel[chan].cells++;
			
			if (mch_flags) {
				if (mch_flags & MC_AAL2_MAAL) {
					chan_fail = connection;
					mc_aal2_total.maal_errors++;
					mc_aal2_channel[chan].maal_errors++;
					retval = 1;
					error_printf("flags %"PRIu64": mc_aal2 MAAL error set total %"PRId64"\n", pc, mc_aal2_total.maal_errors);
				}
			}

			/* If this is the first frame, dont return 1 */
			if(mch_flags & MC_AAL2_1ST_CELL)
				retval = 0;
			break; 

		default:
            if (0 == dagerf_is_known_type((uint8_t*) rec))
            {
                error_printf("flags %"PRIu64": unknown ERF type %d\n", pc, drec->type);
                retval = 1;
            }
			break;
	}
	return retval;
}

static int
test_sig(char *rec, int len)
{
	static int lseq[MAX_INTERFACES];
	
	dag_record_t    *drec = (dag_record_t*)rec;
	int retval = 0;
	char *seq;

	if (dagerf_is_ethernet_type((uint8_t*) rec))
	{
		if ( ntohs(drec->wlen) > (len - dag_record_size -2) )
		{
			if (warning)
				error_printf("sig %"PRIu64": insufficient captured packet length %d needed %d\n", 
							 pc, (len - dag_record_size -2), ntohs(drec->wlen));
			return warning;
		}
		seq = rec + ntohs(drec->wlen) +2 - 12;
	}
	else if (dagerf_is_pos_type((uint8_t*) rec))
	{
		if ( ntohs(drec->wlen) > (len - dag_record_size) )
		{
			if (warning)
				error_printf("sig %"PRIu64": insufficient captured packet length %d needed %d\n", 
							 pc, (len - dag_record_size), ntohs(drec->wlen));
			fcs_slen++;
			return warning;
		}
		seq = rec + ntohs(drec->wlen) - 12;
	}
	else if (dagerf_is_known_type((uint8_t*) rec))
	{
		error_printf("sig %"PRIu64": sig not available in this record type\n", pc);
		return warning;
	}
	else
	{
		error_printf("sig %"PRIu64": unknown ERF type %d\n", pc, drec->type);
		return warning;
	}
	
	if (*(int*)(seq+4) != ~*(int*)(seq+8))
	{
		error_printf("sig %"PRIu64": sig not detected, TS %08x CP %08x\n", pc, *(int*)(seq+4), *(int*)(seq+8) );
		return warning;
	}
	
	
	if ( (lseq[intf]) && (lseq[intf]+1 != *(int*)(seq)) )
	{
		sig_fail++;
		error_printf("sig %"PRIu64": expecting %d, now %d, diff %d", pc, lseq[intf]+1, *(int*)(seq), *(int*)(seq) - lseq[intf]+1);
		retval = 1;
	}
	lseq[intf] = *(int*)(seq);
	
	return retval;
}

static int
test_wlen(char *rec, int len)
{
	dag_record_t *drec = (dag_record_t*)rec;
	
	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
			error_printf("wlen %"PRIu64": wlen not available in this record type\n", pc);
			return warning;
	}
	else if (dagerf_is_modern_type((uint8_t*) rec))
	{
		if (ntohs(drec->wlen) != wlen_exp)
		{
			wlen_fail++;
			error_printf("wlen %"PRId64": expecting %d got %d\n",
						 pc, wlen_exp, ntohs(drec->wlen));
			return 1;
		}
	}
	else
	{
		error_printf("wlen %"PRIu64": unknown ERF type %d\n", pc, drec->type);
	}
	return 0;
}

static int
test_rlen(char *rec, int len)
{
	dag_record_t *drec = (dag_record_t*)rec;
	
	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		error_printf("rlen %"PRIu64": rlen not available in this record type\n", pc);
		return warning;
	}
	else if (dagerf_is_modern_type((uint8_t*) rec))
	{
		if (drec->flags.trunc) {
			if (warning)
				error_printf("rlen %"PRIu64": warning truncate set\n", pc);
			return warning;
		}
		if (ntohs(drec->rlen) != rlen_exp) {
			rlen_fail++;
			error_printf("rlen %"PRId64": expecting %d got %d\n",
						 pc, rlen_exp, ntohs(drec->rlen));
			return 1;
		}
	}
	else
	{
		error_printf("rlen %"PRIu64": unknown ERF type %d\n", pc, drec->type);
	}
	return 0;
}

static int
test_dge(char *rec, char *lastrec)
{
	static uint64_t osmac[MAX_INTERFACES], odmac[MAX_INTERFACES];
	
	uint8_t   *p, q;
	int error = 0, c, retval = 0, clen;
	uint64_t smac = 0, dmac = 0, tsmac, tdmac;
	dag_record_t * drec = (dag_record_t*)rec;

	if (drec->type != ERF_TYPE_ETH) {
		error_printf("dge %"PRIu64": dge not available in this record type\n", pc);
		return warning;
	}

	if ( (ntohs(drec->wlen)-4) < (ntohs(drec->rlen) - 18) ) {
		clen = ntohs(drec->wlen) - 4; /* subtract fcs */
	} else {
		clen = len - dag_record_size;
	}
	
	for ( p = drec->rec.eth.pload, q=*p ; p < (uint8_t*)(rec+clen) ; p++, q++ ) {
		if (*p != q) {
			error++;
			retval = 1;
			break;
		}
	}

	if (error)
		error_printf("dge %"PRIu64": dge content test failed\n", pc);

	for (c = 0; c<6; c++)
	{
		dmac += (uint64_t)drec->rec.eth.dst[c] << ((5-c)*8);
		smac += (uint64_t)drec->rec.eth.src[c] << ((5-c)*8);
	}

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

#define n0xFF_64BIT  (0xffffffffffffll)
#define n0x10_64BIT  (0x1000000000000ll)

#elif defined(_WIN32)

#define n0xFF_64BIT  (0xffffffffffffi64)
#define n0x10_64BIT  (0x1000000000000i64)

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

	if (drec->lctr) {
		if (drec->lctr == 0xffff) 
		{
			odmac[intf] = dmac - 1;
			osmac[intf] = smac + 1;
		}
		else 
		{
			odmac[intf] += ntohs(drec->lctr);
			osmac[intf] -= ntohs(drec->lctr);

			if (odmac[intf] > n0xFF_64BIT)
				odmac[intf] -= n0x10_64BIT;
			if (osmac[intf] > n0x10_64BIT)
				osmac[intf] += n0x10_64BIT;
		}
	}

	if (lastrec) {
		tsmac = osmac[intf] - 1;
		if (tsmac == -1)
			tsmac = n0xFF_64BIT;
		tdmac = odmac[intf] + 1;
		if (tdmac == n0x10_64BIT)
			tdmac = 0;

		if ( (smac != tsmac) || (dmac != tdmac) ) {
			error_printf("dge %"PRIu64": mac sequence broken dmac %012"PRIx64"->%012"PRIx64" smac %012"PRIx64"->%012"PRIx64"\n",
						 pc, odmac[intf], dmac, osmac[intf], smac);
			retval = 1;
		}
	}

	odmac[intf] = dmac;
	osmac[intf] = smac;
	
	if (retval)
		dge_fail++;

	return retval;
}

static int
test_time(char *rec, int len)
{
	dag_record_t    *drec = (dag_record_t*)rec;
	
	if (abs(now.tv_sec-(int)(TS(drec->ts)>>32)) > 60) {
		time_fail++;
		error_printf("time %"PRId64": now %d got %d diff %d\n",
					 pc, (int)now.tv_sec, (int)(TS(drec->ts)>>32), (int)now.tv_sec-(int)(TS(drec->ts)>>32));
		return 1;
	}
	return 0;
}

static int
test_aal5(char *rec, int len)
{
	static uint32_t last_frameseq[MAX_INTERFACES] = {-1};
	static int typeerrcnt = 1;

	dag_record_t *drec = (dag_record_t *)rec;
	uint32_t frameseq = 0;
	uint32_t cellnum, last_cellnum = 0;
	uint32_t frameid, last_frameid = -1;
	uint32_t payldnum, last_payldnum = -1;
	int ret = 0;
	char *ptr;
	uint32_t i, limit;

	/* Only print the AAL5 type message once, but increment the warnings */
	if (drec->type != ERF_TYPE_AAL5) {
		if (typeerrcnt-- > 0) {
			error_printf("aal5 %"PRIu64": aal5 not available in this record type\n", pc);		
		}
		return warning;
	}

	last_payldnum = *((uint8_t*)((long)drec + 27)) + 12;

	for (ptr = (char *)((long)drec + 20); (long)ptr < (long)drec + ntohs(drec->rlen); ptr += 48)
	{
		/* For each cell */

		frameseq = ntohl(*(uint32_t *)ptr);
		frameid = ntohl(*(uint32_t *)((long)ptr + 4));
		cellnum = *(char *)((long)ptr + 12);

		if ((last_frameid != -1) && (frameid != last_frameid)) {
			error_printf("aal5 %"PRIu64": aal5 frame ID error 0x%.8lx -> 0x%.8lx\n", pc, last_frameid, frameid);
			ret = 1;
			aal5_fiderr++;
		}
		if (cellnum != last_cellnum + 1) {
			error_printf("aal5 %"PRIu64": aal5 cell ordering error 0x%.2lx -> 0x%.2lx\n", pc, last_cellnum, cellnum);
			ret = 1;
			aal5_corderr++;
		}

		
		if (strict) {
			/* limit is 44 because the last 4 bytes are now the progressive CRC */
			
			limit = 44;

			if (((long)ptr + 48) == ((long)drec + ntohs(drec->rlen))) {
				/* 
				   Last 48 octets of frame, skip pad and checksum
				   Pull CPCS Length out of frame and work out the overlap.
				*/
				limit = ntohs(*(uint16_t *)((long)ptr + 42)) % 48;
			}
				
			
			for (i = 13; i < limit; i++) {
				/* 
				   Loop through remainder of cell payload checking for consistancy, 
				   remember we have a pad at the end of the CPCS frame  
				*/

				payldnum = *((uint8_t*)((long)ptr + i));
				if (payldnum != (0xFF & (last_payldnum + 1))) {
					error_printf("aal5 %"PRIu64": strict payload checking error 0x%.2lx -> 0x%.2lx (byte %.2lu)\n", pc, last_payldnum, payldnum, 
								 ((long)ptr - ((long)drec - 20)) + i);
					ret = 1;
					aal5_spcerr++;
				}
				last_payldnum = payldnum;
				
			}
		}
		last_cellnum = cellnum;
		last_frameid = frameid;
		last_payldnum =  0xFF & (last_payldnum + 13 + 4); /* 4 bytes skipped at the end of this packet, 13 at the start of the next */
	}

	if ((frameseq != 0) && 
		(last_frameseq[(int)drec->flags.iface] != -1) && 
		(frameseq != last_frameseq[(int)drec->flags.iface] + 1))
	{ 
		error_printf("aal5 %"PRIu64": aal5 frame ordering error 0x%.8lx -> 0x%.8lx\n", pc,  last_frameseq[(int)drec->flags.iface], frameseq);
		ret = 1;
		aal5_forderr++;
	}

	aal5_fail += ret;
	last_frameseq[(int)drec->flags.iface] = frameseq;

	return ret;
}

/* BOPT test constants. */
#define ETH_HEADER_BYTES 14

/* Length of UDP payload used by BOPT for fields. */
#define BOPT_UDP_BYTES 16


static int
test_bopt(char *rec, int len)
{
	static int inited = 0;
	static uint32_t bopt_last_burst_id; 

    /* Save values from first packet in each burst and use for within-burst consistency checking. */
	static uint8_t bopt_burst_eth[ETH_HEADER_BYTES];
	static uint32_t bopt_next_packet_id = 0;
	static uint8_t bopt_payload_fill_byte;
	static uint32_t bopt_packet_header[7]; /* [0]-[4] are IP header, [5]-[6] are UDP header. */
	static uint32_t bopt_packet_mask[7];   /* [0]-[4] are IP header, [5]-[6] are UDP header. */

	dag_record_t* drec = (dag_record_t*) rec;

#if defined (__FreeBSD__) || defined(__NetBSD__) || (defined (__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

	struct ip * ip_hdr;

#elif defined(__linux__)

	struct iphdr* ip_hdr;

#elif defined(_WIN32)

	iphdr* ip_hdr;

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

	struct udphdr* udp_hdr;
	uint64_t timestamp;
	uint32_t burst_id;
	uint32_t packet_id;
	uint16_t src_port;
	uint16_t dst_port;
	uint16_t udp_len;
	uint16_t udp_checksum;
	int index;
	int padding;
	int result = 0;

	if (ERF_TYPE_ETH != drec->type) 
	{
		error_printf("bopt %"PRIu64": only implemented for Ethernet ERF\n", pc);
		return 1;
	}

	/* Check received length: 
	 * 82 = dag_record_size (18) + ERF-ethernet (14) + IP hdr (20) + UDP hdr (8) + BOPT payload (18) + FCS (4) 
	 */
	if (len < 82) 
	{
		error_printf("bopt %"PRIu64": received less than the minimum size required for bopt test (82 bytes)\n", pc);
		bopt_min_len_fail++;
		return 1;
	}

#if defined (__FreeBSD__) || defined(__NetBSD__) || (defined (__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

	ip_hdr = (struct ip *) find_iph(rec);

#elif defined(__linux__)

	ip_hdr = (struct iphdr *) find_iph(rec);

#elif defined(_WIN32)

	ip_hdr = (iphdr *) find_iph(rec);

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

	if (NULL == ip_hdr)
	{
		error_printf("bopt %"PRIu64": could not find IP header\n", pc);
		bopt_ip_hdr_fail++;
		return 1;
	}

#if defined(__linux__)

	if (5 != ip_hdr->ihl)
	{
		error_printf("bopt %"PRIu64": expected an IP header length of 5 (20 bytes), got %d (%d bytes)\n",
					 pc, ip_hdr->ihl, 4 * ip_hdr->ihl);

#elif defined(__FreeBSD__) || defined(__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

	if (5 != ip_hdr->ip_hl)
	{
		error_printf("bopt %"PRIu64": expected an IP header length of 5 (20 bytes), got %d (%d bytes)\n",
					 pc, ip_hdr->ip_hl, 4 * ip_hdr->ip_hl);

#elif defined(_WIN32)

	// NOTE: The header length field of the IP header is from bit4-bit7, but on little endian machines this is swapped with the version field (bit0-bit3)
	if (5 != (ip_hdr->ip_ver_hl & 0x0F))
	{
		error_printf("bopt %"PRIu64": expected an IP header length of 5 (20 bytes), got %d (%d bytes)\n",
					 pc, (ip_hdr->ip_ver_hl & 0x0F), 4 * (ip_hdr->ip_ver_hl & 0x0F));


#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif

		bopt_ip_hdr_len_fail++;
		return 1;
	}

	udp_hdr = (struct udphdr*) &((char*) ip_hdr)[20];
	src_port = ntohs(((uint16_t*) udp_hdr)[0]);
	dst_port = ntohs(((uint16_t*) udp_hdr)[1]);
	udp_len = ntohs(((uint16_t*) udp_hdr)[2]);

	/* ERF records are padded to multiples of 4 bytes. */
	padding = 0;
	if (udp_len % 4 != 0)
	{
		padding = 4 - (udp_len % 4);
	}

	/* Check received length for consistency: 
	 * 56 = dag_record_size (18) + ERF-ethernet (14) + IP hdr (20) + FCS (4)
	 */
	if (len != 56 + udp_len + padding)
	{
		error_printf("bopt %"PRIu64": inconsistent ERF rlen (should be %u bytes, was %u bytes)\n",
					 pc, 56 + udp_len + padding, len);
		bopt_rlen_fail++;
		result = 1;
	}

	/* Initialization. */
	if (0 == inited)
	{
		/* Set up mask. */
		bopt_packet_mask[0] = htonl(0xffff0000); /* IP total length is allowed to change. */
		bopt_packet_mask[1] = htonl(0x0000ffff); /* IP ID should increment. */
		bopt_packet_mask[2] = htonl(0xffff0000); /* IP checksum will change. */
		bopt_packet_mask[3] = htonl(0xffffffff);
		bopt_packet_mask[4] = htonl(0xffffffff);
		bopt_packet_mask[5] = htonl(0xffffffff);
		bopt_packet_mask[6] = htonl(0x0000ffff); /* UDP checksum is not used and should be 0. */

		/* Find timestamp for this burst. */
		memcpy(&bopt_last_timestamp, &((uint8_t*) udp_hdr)[16], 8);
		bopt_last_timestamp = bswap_64(bopt_last_timestamp);
	}

	udp_checksum = ntohs(((uint16_t*) udp_hdr)[3]);
	if (udp_checksum)
	{
		error_printf("bopt %"PRIu64": UDP checksum was %u (should be zero)\n", pc, udp_checksum);
		bopt_udp_checksum_fail++;
		result = 1;
	}

	burst_id = ntohl(((uint32_t*) udp_hdr)[2]);
	packet_id = ntohl(((uint32_t*) udp_hdr)[3]);

	if ((0 == inited) || (bopt_last_burst_id != burst_id))
	{
		/* Save values of fields that are supposed to remain constant within a burst. */

		/* Copy ethernet header for this burst. */
		memcpy(&bopt_burst_eth, &drec->rec.eth.dst[0], ETH_HEADER_BYTES);

		/* Copy IP and UDP headers for this burst. */
		memcpy(bopt_packet_header, ip_hdr, 20);

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined (__sun)) || (defined(__APPLE__) && defined(__ppc__))

		memcpy(&bopt_packet_header[5], udp_hdr, sizeof(struct udphdr));

		/* Find fill byte value for this burst. */
		bopt_payload_fill_byte = ((uint8_t*) udp_hdr)[sizeof(struct udphdr) + BOPT_UDP_BYTES];

#elif defined(_WIN32)

		memcpy(&bopt_packet_header[5], udp_hdr, sizeof(udphdr));

		/* Find fill byte value for this burst. */
		bopt_payload_fill_byte = ((uint8_t*) udp_hdr)[sizeof(udphdr) + BOPT_UDP_BYTES];

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform specific code. */

		/* Find packet ID. */
		bopt_next_packet_id = 1 + packet_id;

		bopt_last_burst_id = burst_id;
		inited = 1;
	}
	else
	{
		/* Check packet id. */
		if (packet_id != bopt_next_packet_id)
		{
			error_printf("bopt %"PRIu64": expected packet ID %u, got packet ID %u\n", pc, bopt_next_packet_id, packet_id);
			bopt_packet_id_fail++;
			bopt_next_packet_id = packet_id; /* Reset expected packet ID to re-sync with burst. */
			result = 1;
		}
		bopt_next_packet_id++;

		/* Check values of fields that are supposed to remain constant within a burst. */
		if (0 != memcmp(bopt_burst_eth, drec->rec.eth.dst, ETH_HEADER_BYTES))
		{
			error_printf("bopt %"PRIu64": ethernet header did not remain constant within a burst\n", pc);
			bopt_eth_hdr_fail++;
			result = 1;
		}

		for (index = 0; index < 7; index++)
		{
			uint32_t mask = bopt_packet_mask[index];
			uint32_t expected = bopt_packet_header[index] & mask;
			uint32_t actual = ((uint32_t*) ip_hdr)[index] & mask;

			if (actual != expected)
			{
				error_printf("bopt %"PRIu64": word %d in packet headers did not remain constant within a burst\n", 
							 pc, index);
				bopt_packet_hdr_fail++;
				result = 1;
			}
		}
	}

    /* Check timestamp. */
	memcpy(&timestamp, &((uint8_t*) udp_hdr)[16], 8);
	timestamp = bswap_64(timestamp);
	if (timestamp < bopt_last_timestamp)
	{
		error_printf("bopt %"PRIu64": timestamp is less than previous timestamp\n", pc);
		bopt_timestamp_fail++;
		bopt_last_timestamp = timestamp; /* Or else subsequent diffs are wrong. */
		result = 1;
	}
	bopt_last_timestamp = timestamp;

	/* Check payload bytes.
	 * 76 = dag_record_size (18) + ERF-ethernet (14) + IP hdr (20) + UDP header (8) + BOPT payload (16)
	 * 52 = dag_record_size (18) + ERF-ethernet (14) + IP hdr (20)
	 */
	for (index = 76; index < (52 + udp_len); index++)
	{
		if (rec[index] != bopt_payload_fill_byte)
		{
			error_printf("bopt %"PRIu64": byte %d in payload did not remain constant within a burst\n", 
						 pc, (index - 76));
			bopt_payload_byte_fail++;
			result = 1;
		}
	}
	
	return result;
}


static int
test_type(char *rec, int len)
{
	dag_record_t* drec = (dag_record_t*) rec;

	if (dagerf_is_legacy_type((uint8_t*) rec))
	{ 
		/* Legacy format */
		switch (tt)
		{
			case TT_ATM:
			case TT_ETH:
			case TT_POS:
				/* Known type. */
				return 0;
	
			default:
				break;
		}
	}
	else if (dagerf_is_modern_type((uint8_t*) rec))
	{
		return 0;
	}

	/* If we get here then the ERF type is unknown. */
	printf("type :%"PRIu64" found unknown ERF type %d\n", pc, drec->type);
	type_fail++;
	return 1;
}

static int
test_maal(char *rec, int len)
{
	dag_record_t* drec = (dag_record_t*) rec;
	int maal_flag;
	int payload_byte;

	if (ERF_TYPE_MC_AAL2 == drec->type)
	{ 
		maal_flag = ((uint32_t)ntohl(drec->rec.mc_aal2.mc_header)>>16)&MC_AAL2_MAAL;

		/* Check if the maal bit is set */
		if(maal_flag>0)
		{
			/* Check the payload for the maal error number */
			payload_byte = drec->rec.mc_aal2.pload[4];

			if(payload_byte == maal_num)
			{
				total_maal_errors++;
			
				error_printf("MAAL %"PRIu64": maal error seen type %d\n", pc, payload_byte);

				return 1;
			}
		}
	}
	
	else if ( ERF_TYPE_AAL2 == drec->type )
	{
		maal_flag = ((uint32_t)ntohl(drec->rec.aal2.ext_header)>>16)&1;
		payload_byte = ((uint32_t)ntohl(drec->rec.aal2.ext_header)>>8)&0xFF;
		
		/* Check if the maal bit is set */
		if(maal_flag>0)
		{
			/* Check the extension header for the maal error number */
			if(payload_byte == maal_num)
			{
				total_maal_errors++;
			
				error_printf("MAAL %"PRIu64": maal error seen type %d\n", pc, payload_byte);

				return 1;
			}
		}
	}

	return 0;
}

static int
test_modify_timestamps(char *rec, int len)
{
	return 0;
}


static int
test_align64(char *rec, int len)
{
	if (len % 8)
	{
		printf("align64 %"PRIu64": record length %d not 64-bit aligned\n", pc, len);
		align64_fail++;
		return 1;
	}
	
	return 0;
}

static int
test_align(char *rec, int len)
{
    if (len % align_bytes_len)
    {
        printf("align %"PRIu64": record length %d not %d-bit aligned\n", pc, len, align_bytes_len * 8  );
        align_fail++;
        return 1;
    }
    
    return 0;
}

static int
test_incr_array(uint8_t *parray, uint32_t byte_count)
{
	uint32_t i;
	
	for (i = 1; i < byte_count; i++) {
		if (parray[i] != ((parray[i-1] + 1) & 0xFF)) {
			
			/* Check for wrap around from 0xff to 0x0 */
			if (parray[i] == 0 && parray[i-1] == 0xff)
				continue;
					
			/* Otherwise Error */
			error_printf("incr %"PRIu64": sequence broken - current = %x prev = %x \n", pc, parray[i]&0xff, parray[i-1]&0xff);
			return 1;
		}
	}

	return 0;	
}

/* This test is meant to be machine readable, so although it's
   outputing text, it's all in one big line... */
static int
test_ipf_tp(char *rec, int len)
{
	
	int retval = 0;
	
	dag_record_t* drec = (dag_record_t*) rec;

	uint16_t color;
	uint16_t stream;
	uint16_t etype;
	uint8_t  ihl;
	uint8_t  ip_prot;
	uint8_t *pload;
	uint8_t *tp;


	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		ipf_tp_ignored++;
	} 
	else if (dagerf_is_modern_type((uint8_t*) rec))
	{

		printf("{'ipf_tp':'%"PRIu64"',", pc);

		switch (drec->type) {
		case ERF_TYPE_HDLC_POS:
		case ERF_TYPE_COLOR_HDLC_POS:
		case ERF_TYPE_COLOR_HASH_POS:
			if (drec->type == ERF_TYPE_COLOR_HDLC_POS)
			{
				uint32_t tmp;
				color = ntohs(drec->lctr);
				stream = color & 0x03;
				color = color >> 2;
				tmp = ntohl(drec->rec.pos.hdlc);
				etype = (uint16_t) (tmp & 0xffff);
			} 
			else if (drec->type == ERF_TYPE_COLOR_HASH_POS)
			{
				uint32_t tmp;
				color = ntohs(drec->lctr);
				stream = color & 0x0f;
				color = color >> 4;
				tmp = ntohl(drec->rec.pos.hdlc);
				etype = (uint16_t) (tmp & 0xffff);
			} 
			else 
			{
				uint32_t tmp;

				tmp = ntohl(drec->rec.pos.hdlc);
				color = (uint16_t) ((tmp>>16) & 0xffff);
				stream = color & 0x03;
				color = color >> 2;
				etype = (uint16_t) (tmp & 0xffff);
			}
			printf("'ifc':%u,'rectype':%u,'rlen':%u,'wlen':%u,'color':%u,'stream':%u,'etype':%u,",
			       drec->flags.iface,
			       drec->type, 
			       ntohs(drec->rlen),
			       ntohs(drec->wlen),
			       color,
			       stream,
			       etype);
			if ((etype == 0x0800) || (etype == 0x0021)) /* IP v4 */
			{
				pload = drec->rec.pos.pload;
				ihl = ((uint8_t) pload[0]) & 0x0f;
				ip_prot = pload[9];
				printf("'srcip':'%u.%u.%u.%u','dstip':'%u.%u.%u.%u','prot':%u",
				       pload[12], pload[13], pload[14], pload[15],
				       pload[16], pload[17], pload[18], pload[19],
				       ip_prot);
				pload = pload + 4*ihl;
				if (ip_prot == 1) /* ICMP */
				{
					printf(",'icmp_type':%u,'icmp_code':%u}\n",
					       pload[0],
					       pload[1]);
				}
				else if (ip_prot == 6)  /* TCP */
				{
					printf(",'srcport':%u,'dstport':%u,'tcp_flags':%u}\n",
					       (uint16_t) (pload[0] << 8 | pload[1]),
					       (uint16_t) (pload[2] << 8 | pload[3]),
					       (uint8_t) pload[13]);
				}
				else if (ip_prot == 17)  /* UDP */
				{
					printf(",'srcport':%u,'dstport':%u}\n",
					       (uint16_t) (pload[0] << 8 | pload[1]),
					       (uint16_t) (pload[2] << 8 | pload[3]));
				}
				else
				{
					printf(",'other':''}\n");
				}
			}
			else
			{
				printf("'notip':''}\n");
			}
			break;
			
		case ERF_TYPE_ETH:
		case ERF_TYPE_COLOR_ETH:
		case ERF_TYPE_COLOR_HASH_ETH:
			if (drec->type == ERF_TYPE_COLOR_ETH)
			{
				color = ntohs(drec->lctr);
				stream = color & 0x03;
				color = color >> 2;
				etype = ntohs(drec->rec.eth.etype);
			} 
			else if (drec->type == ERF_TYPE_COLOR_HASH_ETH)
			{
				color = ntohs(drec->lctr);
				stream = color & 0x0f;
				color = color >> 4;
				etype = ntohs(drec->rec.eth.etype);
			} 
			else 
			{
				tp = &drec->rec.eth.offset;
				color = ntohs(*((uint16_t*) tp));
				stream = color & 0x03;
				color = color >> 2;
				etype = ntohs(drec->rec.eth.etype);
			}
			pload = drec->rec.eth.pload - 2; /* We want to point to the VLAN label in case we're Q-in-Q */
			while (etype == 0x8100) /* Skip VLAN Tags */
			{
				pload += 4; /* Skip this VLAN */
				etype = ntohs(*pload);
			}
			pload += 2; /* Now we're 2 bytes behind, so we need to atch up */
			printf("'ifc':%u,'rectype':%u,'rlen':%u,'wlen':%u,'color':%u,'stream':%u,'etype':%u,", 
			       drec->flags.iface,
			       drec->type, 
			       ntohs(drec->rlen),
			       ntohs(drec->wlen),
			       color,
			       stream,
			       etype);
			if (etype == 0x0800) /* IP v4 */
			{
				ihl = pload[0] & 0x0f;
				ip_prot = (uint8_t)pload[9];
				printf("'srcip':'%u.%u.%u.%u','dstip':'%u.%u.%u.%u','prot':%u",
				       pload[12], pload[13], pload[14], pload[15],
				       pload[16], pload[17], pload[18], pload[19],
				       ip_prot);
				pload = pload + 4*ihl;
				if (ip_prot == 1) /* ICMP */
				{
					printf(",'icmp_type':%u,'icmp_code':%u}\n",
					       pload[0],
					       pload[1]);
				}
				else if (ip_prot == 6)  /* TCP */
				{
					printf(",'srcport':%u,'dstport':%u,'tcp_flags':%u}\n",
					       (uint16_t) (pload[0] << 8 | pload[1]),
					       (uint16_t) (pload[2] << 8 | pload[3]),
					       (uint8_t) pload[13]);
				}
				else if (ip_prot == 17)  /* UDP */
				{
					printf(",'srcport':%u,'dstport':%u}\n",
					       (uint16_t) (pload[0] << 8 | pload[1]),
					       (uint16_t) (pload[2] << 8 | pload[3]));
				}
				else
				{
					printf(",'other':''}\n");
				}
			}
			else
			{
				printf("'notip':''}\n");
			}
			break;

		default:
			ipf_tp_ignored++;
		}
	}

	return retval;
}

/* Unfortunately we can't replace this with dagpd_find_ip_header() in the dag_protocol_decode module because it has hooks into file-scope variables (tt, warning,) :( */
static char *
find_iph(char *rec)
{
	dag_record_t* drec = (dag_record_t *)rec;
    unsigned int ext_hdrs = 0;
    uint8_t       erf_type = 0x00;
    uint16_t      erf_rlen = 0;
	int verbose = dagutil_get_verbosity();

    erf_type = (drec->type & 0x7F);
    erf_rlen = ntohs(drec->rlen);

    ext_hdrs = dagerf_ext_header_count ((uint8_t*)rec, erf_rlen);
    if ( ext_hdrs > 0 )
    {
        /* Hack: because the following code only uses the data fields below the standard 16-byte
            *       ERF header, we can offset the "record" overlay by the number of extension headers
            *       and the rest of the fields should line up.
            */
        drec = (dag_record_t *) ((uintptr_t)rec + (ext_hdrs * 8));
    }

	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		switch (tt)
		{
			case TT_ATM:
				/* assuming RFC2225 Classical ATM over IP LLC/SNAP */
				if (ntohs(*(uint16_t*)(rec+22)) == 0x0800) 
					return (rec+24);
				else {
					if (verbose)
						error_printf("find_iph %"PRIu64": etype=0x%.4x, non-IP\n",
									 pc, ntohs(*(uint16_t*)(rec+22)) );
					return NULL;
				}
				break;
			
			case TT_ETH:
				if(ntohs(((legacy_eth_t*)rec)->etype) != 0x0800) {
					if (verbose)
						error_printf("find_iph %"PRIu64": etype=0x%.4x, non-IP\n", pc, ntohs(((legacy_eth_t*)rec)->etype));
					return NULL;
				}
				return (rec+24);
			
			case TT_POS:
				if (ntohs(*(uint16_t*)(rec+18)) == 0x0800) 
					return (rec+20);
				else {
					if (verbose)
						error_printf("find_iph %"PRIu64": etype=0x%.4x, non-IP\n",
									 pc, ntohs(*(uint16_t*)(rec+18)) );
					return NULL;
				}
				break;
			
			default:
				assert(0); /* Unknown type. */
				break;
		}
	}
	else if (dagerf_is_pos_type((uint8_t*) rec))
	{
		return (char*) drec->rec.pos.pload;
	}
	else if (dagerf_is_ethernet_type((uint8_t*) rec))
	{
		int ethertype = ntohs(drec->rec.eth.etype);
		
		if (ethertype == 0x8100)
		{
			/* VLAN ethernet. */
			uint16_t* overlay16 = (uint16_t*) drec->rec.eth.pload;
			int length_type = ntohs(overlay16[1]);
			
			if (0x0800 == length_type)
			{
				/* Found IP header. */
				return (char*) &overlay16[2];
			}
			
			if (verbose)
			{
				error_printf("find_iph %"PRIu64": etype=0x%04x, non-IP\n", pc, length_type);
			}
			
			return NULL;
		}
		else if (ethertype == 0x0800)
		{
			/* Non-VLAN Type-encoded ethernet carrying IP. */
			return (char*) drec->rec.eth.pload;
		}
		
		if (verbose)
		{
			error_printf("find_iph %"PRIu64": etype=0x%04x, non-IP\n", pc, ethertype);
		}
		return NULL;
	}
	else if (ERF_TYPE_ATM == erf_type)
	{
		/* assuming RFC2225 Classical ATM over IP LLC/SNAP */
		if (ntohs(*(uint16_t*)(drec->rec.atm.pload + 6)) != 0x0800)
		{
			if (verbose)
				error_printf("find_iph %"PRIu64": ATM etype=0x%.4x, non-IP\n",
					pc, ntohs(*(uint16_t*)(drec->rec.atm.pload + 6)));
			
			return NULL;
		}
		return (char*) drec->rec.atm.pload + 8;
	}
	else if(ERF_TYPE_IPV4 == erf_type)
	{
		return (char*) (rec + 16);
	}
	else
	{
		error_printf("find_iph %"PRIu64": not implemented for ERF type %d\n", pc, erf_type);
	}

	return NULL;
}

static int
ipcmp(char *r1, char *r2)
{
	uint16_t *s1, *s2, *ep;
	uint16_t *id, *cs;

	s1 = (uint16_t*)r1;
	s2 = (uint16_t*)r2;

	ep = (s1+10);

	//the following 2 fields will not be compared 
	//get IP header Identification field 
	id = s1 + 2;
	//get offest if the IP header checksum it will not be the same if the wlen respectivly IP length is differnt 
	cs = s1 + 5;

	while(s1 < ep) {
		if ((s1 == id) || (s1 == cs)) {
			/* ignore */
		} else if (*s1 != *s2) {
			return 1;
		}
		s1++;
		s2++;
	}
	return 0;
}

/* IP header compare routine that ignores ID, CRC and packet length. */
static int
ipcmp2(char *r1, char *r2)
{
	uint16_t *s1, *s2, *ep;
	uint16_t *wl, *id, *cs; 

	s1 = (uint16_t*)r1;
	s2 = (uint16_t*)r2;

	ep = (s1+10);

	//the following 3 fields will not be compared 
	//get offset of the IP total Length 
	wl = s1 + 1;
	
	//get IP header Identification field 
	id = s1 + 2;
	//get offest if the IP header checksum it will not be the same if the wlen respectivly IP length is differnt 
	cs = s1 + 5;

	while(s1 < ep) {
		if ((s1 == wl) || (s1 == id) || (s1 == cs)) {
			/* ignore */
		} else if (*s1 != *s2) {
			return 1;
		}
		s1++;
		s2++;
	}
	return 0;
}

static int
dir(char *rec)
{
	dag_record_t * drec = (dag_record_t *) rec;
	
	if ( (tt == TT_ETH) || (0 == drec->type))
		return (int)(TS(drec->ts) & 0x1);
	
	return (long) drec->flags.iface;  /* ERF direction bits */
}

static int
reclen(char *rec)
{
	int len;

	if (NULL == rec)
		return 0;

	if (tt == TT_ETH)
		return conf_len;
	
	if ( ((dag_record_t *)rec)->type )
	{
		/* ERF format */
		len = ntohs(((dag_record_t *)rec)->rlen);
	}
	else
	{
		len = conf_len;
	}
	
	if (len > MAXLEN)
		len = MAXLEN;
	
	return len;
}


static void
error_printf(char *fmt, ...)
{
	va_list ap;

	if ((max_print_errors && (total_errors > max_print_errors)))
		return;
	va_start(ap, fmt);
	vfprintf(stdout, fmt, ap);
	va_end(ap);
}

/* AAL stuff */

static bool 
IsFirstFrame(dag_record_t *drec, int type)
{
	if(type == ERF_TYPE_MC_AAL5)
	{
		return ((((uint32_t)ntohl(drec->rec.mc_aal5.mc_header)>>24) & MC_AAL5_1ST_CELL) != 0);
	}
	else if (type == ERF_TYPE_MC_AAL2)
	{
		return((((uint32_t)ntohl(drec->rec.mc_aal2.mc_header)>>16) & MC_AAL2_1ST_CELL) != 0);
	}
	else if (type == ERF_TYPE_AAL2)
	{
		return(((uint32_t)ntohl(drec->rec.aal2.ext_header) & 0x00020000) != 0);
	}
		

	return false;
}



uint32_t
StoreTimestamp(mc_aal_vc_t *pVC)
{
	mc_aal_vc_t** ppHashTablePtr = NULL; /* points to connection num entr in hash table array */
	mc_aal_vc_t* pHashPosPtr = NULL;     /* points to last position in hash table chain */
	uint16_t hash_result;

	/* find VC (error check that it exists) */
	ppHashTablePtr = mc_aal_conn[pVC->connection_num]; 

	if (NULL == ppHashTablePtr)
	{
		return 1;
	}

	hash_result = CalculateHashFunction(pVC->vci, pVC->vpi);
	pHashPosPtr = ppHashTablePtr[hash_result];

	if (NULL == pHashPosPtr)
	{
		return 1;
	}

	while(pHashPosPtr->vci != pVC->vci || pHashPosPtr->vpi != pVC->vpi)
	{
		pHashPosPtr = pHashPosPtr->pNext;
		if (NULL == pHashPosPtr )
			break;
	}

	if (NULL == pHashPosPtr)
	{
		return 1;
	}
	pHashPosPtr->last_ts = pVC->last_ts;
    pHashPosPtr->last_delta = pVC->last_delta;
    pHashPosPtr->std_delta_variance = pVC->std_delta_variance;

	return 0; 
}


uint32_t 
StoreNewVirtualChannel(mc_aal_vc_t *pVC)
{
	mc_aal_vc_t** ppHashPtr = NULL; /* points to hash table for connection */
	uint16_t hash_result;
	mc_aal_vc_t* pChain = NULL;
	
	ppHashPtr = mc_aal_conn[pVC->connection_num];
	
	/* check for a NULL value for this connection number */
	if (NULL == ppHashPtr)
	{
		/* allocate new hash table for this connection number */
		ppHashPtr = dagutil_malloc(HASH_TABLE_SIZE*sizeof(void*));
		if (ppHashPtr == NULL)
			return 1;
		
		memset(ppHashPtr, 0, HASH_TABLE_SIZE*sizeof(void*));
		mc_aal_conn[pVC->connection_num] = ppHashPtr;
	}

	/* calculate hash function for this entry from VCI & VPI */
	hash_result = CalculateHashFunction(pVC->vci, pVC->vpi);
	
	pChain = ppHashPtr[hash_result];
	/* check for a collision at this point*/
	if (NULL != pChain)
	{
		/* traverse list for first NULL Next pointer */
		while (NULL != pChain->pNext)
		{
			pChain = pChain->pNext;
		}
		/* point NULL pointer to this virtual channel */
		pChain->pNext = pVC;
	}
	else
	{
		/* point entry at this Virtual channel */
		ppHashPtr[hash_result] = pVC;
	}

	return 0;
}

mc_aal_vc_t* 
RetrieveVCFromMemory(uint32_t vci, uint32_t vpi, uint32_t connection_num)
{
	mc_aal_vc_t** ppHashTablePtr = NULL; 
	mc_aal_vc_t* pHashPosPtr = NULL; /* points to last position in hash table chain */
	uint16_t hash_result;

	/* find VC (error check that it exists) */
	ppHashTablePtr = mc_aal_conn[connection_num]; 

	if (NULL == ppHashTablePtr)
	{
		return NULL;
	}

	hash_result = CalculateHashFunction(vci, vpi);
	pHashPosPtr = ppHashTablePtr[hash_result];

	if (NULL == pHashPosPtr)
	{
		return NULL;
	}

	while(pHashPosPtr->vci != vci || pHashPosPtr->vpi != vpi)
	{
		pHashPosPtr = pHashPosPtr->pNext;
		if (NULL == pHashPosPtr )
			break;
	}

	return pHashPosPtr; /* this can be NULL */
}

uint16_t 
CalculateHashFunction(uint32_t vci, uint32_t vpi)
{
	uint16_t wHash = 0;
	uint32_t dwKey;
	
	dwKey = (vpi<<16) + vci; /* create one value with the key in it */

	/* below is the mix hash function as defined by Robert Jenkins for a 32 bit
	 * variable.  Published by Thomas Wang at http://www.concentric.net/~Ttwang/tech/inthash.htm
	 * More information can be found (including licensing information (free for all uses)
	 * at
	 *         http://burtleburtle.net/bob/hash/doobs.html
	 */

	/* this was used as it takes best advantage of the free shifts on the ARM and does not
	 * require a CRC table to be maintained.
	 */

	dwKey += (dwKey << 12);
	dwKey ^= (dwKey >> 22);
	dwKey += (dwKey << 4);
	dwKey ^= (dwKey >> 9);
	dwKey += (dwKey << 10);
	dwKey ^= (dwKey >> 2);
	dwKey += (dwKey << 7);
	dwKey ^= (dwKey >> 12);

	wHash = (uint16_t)(dwKey & HASH_TABLE_MASK);

	return wHash;
}

void print_infiniband_headers(infiniband_rec_t  rec)
{
    ib_lrh_t *this_lrh = NULL;
    ib_grh_t *this_grh = NULL;
//    uint32_t *this_grh =NULL;
    ib_bth_t  *this_bth = NULL;
    ib_ext_deth_t *this_deth = NULL;

    printf("Infiniband Header\n");
 
     /*print LRH here */
    this_lrh = & ( rec.ib_rec.ib_with_no_grh.lrh);
    printf("LRH \n");
    printf("VLane=0x%01x LVer=0x%01x SLevel=0x%01x LNH=0x%01x Packet Length=0x%03x DLID=0x%04x SLID=0x%04x\n", this_lrh->virtual_lane, this_lrh->link_version, this_lrh->service_level, this_lrh->lnh, ntohs( this_lrh->packet_length) & 0x7ff , ntohs (this_lrh->dest_local_id), ntohs (this_lrh->src_local_id));
    if ( this_lrh->lnh == 0x3)
    {
        /* next header is GRH */
        this_grh =   &(rec.ib_rec.ib_with_grh.grh);
    }
    else if ( this_lrh->lnh == 0x2 )
    {
        this_bth = &(rec.ib_rec.ib_with_no_grh.bth);
    }
    /* print GRH if it exists */
    if ( this_grh )
    {
          printf("GRH Header\n");
          printf("IP Ver=0x%02x Traffic Class=0x%03x Flow Label=0x%05x PayLen=0x%04x NxtHdr=0x%02x HopLmt=0x%2x\n",(ntohl(this_grh->word0) & 0xf0000000) >> 28, (ntohl(this_grh->word0) & 0x0ff00000 ) >> 16,( ntohl( this_grh->word0) & 0x000fffff) , ntohs( this_grh->pay_len) ,this_grh->next_header, this_grh->hop_limit) ;
         if ( this_grh->next_header == 0x1b ) 
            {
             this_bth  = &(rec.ib_rec.ib_with_grh.bth);
            }
          
    }
    /* print BTH , if it exists */
    if ( this_bth )
    {
         printf("BTH Header \n");
         printf("OpCode=0x%01x TVer=0x%02x Pad Count=0x%x Migration state=0x%x Solicited Event=0x%x Dest QP=0x%06x AckReq=0x%x PSN=0x%06x\n",this_bth->op_code,this_bth->t_header_version, this_bth->pad_count, this_bth->migration_state, this_bth->solicited_event,ntohl(this_bth->dest_qp)>> 8,this_bth->ack_req, ntohl(this_bth->packet_seq_number)>> 8) ;         
    }
    /* print DETH , if it exists */
    if ( this_bth && (-1 != opcode_to_deth_offset[this_bth->op_code]) )
    {
        /*int  i =0;
        uint8_t *tmp = (uint8_t*) this_bth + sizeof(ib_bth_t) + sizeof(ib_ext_rdeth_t);
        for(i=0; i < 8; i++)
        {
            printf("0x%02x ",*tmp);
            tmp++;
        }
        printf("\n");
        */
        //this_deth = (ib_ext_deth_t*) (uint8_t*)((uint8_t*) this_bth +  sizeof(ib_bth_t) + sizeof(ib_ext_rdeth_t));*/
        if( this_grh)
            this_deth =  (ib_ext_deth_t *)((uint8_t *)&(rec.ib_rec.ib_with_grh.ib_ext) + opcode_to_deth_offset[this_bth->op_code]);
        else
            this_deth = (ib_ext_deth_t *)((uint8_t *) &(rec.ib_rec.ib_with_no_grh.ib_ext) + opcode_to_deth_offset[this_bth->op_code]);
         printf("DETH Header\n");
         printf("Src QP=0x%06x \n",ntohl(this_deth->source_qp) >> 8);
        
    }
    
}
int test_infiniband_crc( dag_record_t *drec)
{
    uint16_t packet_length = 0;
    uint16_t extracted_vcrc ;
    uint16_t calc_vcrc ;
    uint8_t *ib_packet_start = NULL ;
    uint8_t *start_vcrc = NULL;
	bool is_pass = true;
    int num_ext_hdrs  = 0;
    erf_payload_t *proto_hdr = NULL;
    
    /* check for wlen is min 24 . 24 is the smallest size in bytes for infiniband packet */
    if ( ntohs(drec->wlen) < 24 ) 
    {
        error_printf("fcs %"PRIu64":  wlen(%d) is less than minimum packet length for Infiniband packet \n",pc, ntohs(drec->wlen) );
        fcs_fail++;
        return 1;
    }
    /* Get the number of extension headers and get the infiniband protocol header by 
     * advancing the pointer by (num_ext_hdrs * 8). num_ext_hdrs will be 0 for no ext hdrs */
    num_ext_hdrs = dagerf_ext_header_count((uint8_t*)drec, len);
    proto_hdr = (erf_payload_t*)((uintptr_t)drec + (num_ext_hdrs * 8) + dag_record_size);

    packet_length = ntohs ( proto_hdr->infiniband.ib_rec.ib_with_no_grh.lrh.packet_length) & 0x7ff;
    if ( ntohs(drec->wlen) < (packet_length * 4 ))
    {
        error_printf("fcs %"PRIu64":  wlen (%d) is less than packet length specified(%d) \n",pc,ntohs(drec->wlen), (packet_length * 4 ) );
        fcs_fail++;
        return 1;

    }
    ib_packet_start = (uint8_t*) &(proto_hdr->infiniband);
    /* read the VCRC from the packet */ 
    start_vcrc = ib_packet_start + ( packet_length * 4);
    extracted_vcrc = *((uint16_t*)  start_vcrc ) ;
   
    /* calculate the VCRC */
    calc_vcrc = dagcrc_infiniband_vcrc16_r(0, ib_packet_start,  packet_length * 4  ); 
    
    if ( extracted_vcrc != calc_vcrc )
    {
        printf("VCRC failed for the above record. VCRC read: 0x%02x  calculated: 0x%02x\n",extracted_vcrc, calc_vcrc);
        //fcs_fail++;
        is_pass = false;
        //return 1;
    }    
    if (test_infiniband_icrc(drec) )
    {
        //fcs_fail++;
        //return 1;
        is_pass = false;
    }
    if ( is_pass)
     fcs_pass++;
else
    fcs_fail++;
    return (!is_pass);
}

int test_infiniband_icrc( dag_record_t *drec)
{
    uint16_t packet_length = 0;
    uint32_t extracted_icrc ;
    uint32_t calc_icrc ;
    uint8_t *ib_packet_start = NULL ;
    uint8_t *start_icrc = NULL;
    ib_lrh_t *this_lrh = NULL;
    ib_grh_t *this_grh = NULL;
    ib_lrh_t *masked_lrh  = NULL;
    ib_grh_t *masked_grh = NULL;
    ib_bth_t *masked_bth = NULL;
	int num_ext_hdrs  = 0;
    erf_payload_t *proto_hdr = NULL;
    static uint8_t masked_ib_hdr_mem[128];
    uint32_t masked_len = 0;
    uint8_t *masked_ib_hdrs = masked_ib_hdr_mem;

    /* Get the number of extension headers and get the infiniband protocol header by 
     * advancing the pointer by (num_ext_hdrs * 8). num_ext_hdrs will be 0 for no ext hdrs */
    num_ext_hdrs = dagerf_ext_header_count((uint8_t*)drec, len);
    proto_hdr = (erf_payload_t*)((uintptr_t)drec + (num_ext_hdrs * 8) + dag_record_size);

    packet_length = ntohs ( proto_hdr->infiniband.ib_rec.ib_with_no_grh.lrh.packet_length) & 0x7ff;
    ib_packet_start = (uint8_t*) &(proto_hdr->infiniband);
    /* read the ICRC from the packet */ 
    start_icrc = ib_packet_start + ( packet_length * 4) - IB_ICRC_LEN;
    extracted_icrc = *((uint32_t*)  start_icrc ) ;

    this_lrh = & ( proto_hdr->infiniband.ib_rec.ib_with_no_grh.lrh);
    if ( this_lrh->lnh == 0x3)
    {
        /* next header is GRH */
        this_grh =   &(drec->rec.infiniband.ib_rec.ib_with_grh.grh);
    }

    if ( NULL == this_grh )
    {
        /* Local packet - No GRH - Mask the required fields in LRH and BTH*/
        masked_len = sizeof ( ib_lrh_t ) + sizeof(ib_bth_t) ;
        
        //memcpy(masked_lrh, this_lrh, sizeof ( ib_lrh_t));
        memcpy( masked_ib_hdrs, ib_packet_start, masked_len);
        masked_lrh = (ib_lrh_t *) masked_ib_hdrs;
        masked_lrh->virtual_lane = 0xf;

        masked_bth = (ib_bth_t*) (masked_ib_hdrs + sizeof(ib_lrh_t));
        masked_bth->reserved1 = 0xff;
    }
    else 
    {
        /* has GRH - Mask whole LRH and the required fileds of BTH and GRH */
        masked_len = sizeof ( ib_lrh_t ) + sizeof(ib_grh_t) + sizeof(ib_bth_t);
        memcpy( masked_ib_hdrs, ib_packet_start, masked_len);

        masked_lrh = (ib_lrh_t*) masked_ib_hdrs;
        /* mask the whole LRH */
        masked_lrh->link_version = 0xf;
        masked_lrh->virtual_lane = 0xf;
        masked_lrh->lnh = 0x3;
        masked_lrh->reserved1 = 0x3;
        masked_lrh->service_level = 0xf;
        masked_lrh->dest_local_id = 0xffff;
        masked_lrh->packet_length = 0xffff;
        masked_lrh->src_local_id = 0xffff;
        /* mask the grh fields */
        masked_grh = (ib_grh_t*) (masked_ib_hdrs + sizeof(ib_lrh_t));
        /* mask the Flow label, Traffic Class*/
        masked_grh->word0 |= 0xffffff0f;
        masked_grh->hop_limit = 0xff;
        masked_bth = (ib_bth_t*) (masked_ib_hdrs + sizeof(ib_lrh_t) + sizeof( ib_grh_t ));
        masked_bth->reserved1 = 0xff;    
    }
    /* calculate the ICRC */
    calc_icrc = dagcrc_infiniband_icrc32(0, masked_ib_hdrs, masked_len, ib_packet_start + masked_len,  (packet_length * 4 - (masked_len + IB_ICRC_LEN) )); 
    if ( extracted_icrc != calc_icrc )
    {
        printf("ICRC failed for the above record. ICRC read: 0x%04x calculated: 0x%4x\n",extracted_icrc, calc_icrc);
        return 1;
    }    
    return 0;
}


#ifndef ENDACE_UNIT_TEST
int
main(int argc, const char* const * argv)
{
	return dagbits_main(argc, (char**) argv);
}
#endif /* ENDACE_UNIT_TEST */

