/*
 * Copyright (c) 2002-2007 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: h20s_tests.c 15597 2012-03-28 22:21:32Z jomi.gregory $
 */

/* Documentation notes: 
 * --------------------
 *
 * The test cases and the test plan is documented int he IPF and HAT 
 * test plan. Each test case and test doesn't have an in code documentation.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../../../include/dagcrc.h"
#include "../../../include/dagclarg.h"
#include "../../../include/dag_component.h"
#include "../../../include/dag_component_codes.h"
#include "../../../include/dagapi.h"
#include "../../../include/dagerf.h"
#include "../../../include/dag_config.h"
#include "h20s_tests.h"

/* Some constants */
#define PROT_ICMP (1)
#define PROT_TCP (6)
#define PROT_UDP (17)

#define TCP_FIN (0x01)
#define TCP_SYN (0x02)
#define TCP_RST (0x04)
#define TCP_PSH (0x08)
#define TCP_ACK (0x10)
#define TCP_URG (0x20)

/* Some IP macros to constract and extract the fields */
#define IP_ADDR_B0(x) (x & 0x0ff)
#define IP_ADDR_B1(x) ((x>>8) & 0x0ff)
#define IP_ADDR_B2(x) ((x>>16) & 0x0ff)
#define IP_ADDR_B3(x) ((x>>24) & 0x0ff)

#define IP_ADDR_BRK(x) IP_ADDR_B3(x), IP_ADDR_B2(x), IP_ADDR_B1(x), IP_ADDR_B0(x)

#define IP_ADDR_MAKE(a,b,c,d) (((a&0xff)<<24)|((b&0xff)<<16)|((c&0xff)<<8)|(d&0xff))


/* Command Line Arguments constants */
enum
{
	CLA_PRINT,
	CLA_MONO,
	CLA_WLEN,
	CLA_RLEN,
	CLA_SKIP_1ST_HDR,
	CLA_HAS_MAC,
	CLA_SEQNO,
	CLA_HDLC,
	CLA_MPLS,
	CLA_MPLS_CNT,
	CLA_SRCIP,
	CLA_DSTIP,
	CLA_SRCTCP,
	CLA_DSTTCP,
	CLA_MAC,
	CLA_MAC_ALGO,
	CLA_VERBOSE
};

/* Internal Function prototypes */
int print_record(char *rec, int len, uint64_t pkt_cnt);
int test_mono(char *rec, int len, int num);
int test_seqno(char *rec, int len, int num);
int test_rlen(char *rec, int len, int num);
int test_wlen(char *rec, int len, int num);
int test_hdlc(char *rec, int len, int num);
int test_mpls(char *rec, int len, int num);
int test_mpls_cnt(char *rec, int len, int num);
int test_srcip(char *rec, int len, int num);
int test_dstip(char *rec, int len, int num);
int test_srctcp(char *rec, int len, int num);
int test_dsttcp(char *rec, int len, int num);
int test_mac(char *rec, int len, int num);
int test_mac_algo(char *rec, int len, int num);

int parse_rec(char *rec, int len);

/* Global variables for this test */
char rec_msg[MAX_STR_LEN];
int rec_msg_len = 0;

global_params_t settings;

/* Global tests definitions */
#define H20S_PRINT    0x0001
#define H20S_MONO     0x0002
#define H20S_WLEN     0x0004
#define H20S_RLEN     0x0008
#define H20S_SEQNO    0x0010
#define H20S_HDLC     0x0020
#define H20S_SRCIP    0x0040
#define H20S_DSTIP    0x0080
#define H20S_SRCTCP   0x0100
#define H20S_DSTTCP   0x0200
#define H20S_MPLS     0x0400
#define H20S_MPLS_CNT 0x0800
#define H20S_MAC      0x1000
#define H20S_MAC_ALGO 0x2000

#define TEST_CNT 13

char* test_names[] =
{
	"mono",
	"wlen",
	"rlen",
	"seqno",
	"hdlc",
	"srcip",
	"dstip",
	"srctcp",
	"dsttcp",
	"mpls",
	"mpls_cnt",
	"mac",
	"mac_algo",
};

uint32_t tests = 0;

/* Global counters for final reporting */
typedef struct test_counters {
	char     ran;
	uint32_t passed;
	uint32_t failed;
	uint32_t ignored;
	uint32_t warning;
	uint32_t unknown;
} test_counters_t;

uint32_t h20s_total = 0;
uint32_t h20s_failed = 0;
uint32_t h20s_ignored = 0;
uint32_t h20s_warning = 0;
test_counters_t h20s_counter[TEST_CNT];

/* This is the results of parsing a record and extracting
 * the internal ERF record.
 * We always assume ERF-in-ERF with an optional MAC insertion
 * conrolled by command line option (-m) */ 
typedef struct rec_fields {
	int64_t  outer_ts;
	uint16_t outer_rlen;
	uint16_t outer_wlen;
	uint16_t outer_type;
	uint16_t outer_etype;
	uint8_t  src_mac[6];
	uint8_t  dst_mac[6];
	int64_t  ts;
	uint16_t rlen;
	uint16_t wlen;
	uint8_t  type;
	flags_t  flags;
	uint32_t seq_no;
	uint32_t ext_top;
	uint8_t  has_hash;
	uint8_t  hash;
	uint16_t prot;
	uint16_t etype;
	uint32_t mpls_top;
	uint8_t  mpls_cnt;
	uint8_t  ip_len;
	uint8_t  ip_ver;
	uint8_t  ip_prot;
	uint32_t src_ip;
	uint32_t dst_ip;
	uint16_t src_port;
	uint16_t dst_port;
	uint8_t  pload[1];
} rec_fields_t;

rec_fields_t cur_rec;
rec_fields_t last_rec;

void incr_counters(int res, test_counters_t *cntr);

/* Global tests configuration */
#define MAX_LIST_LEN 10

int has_mac = 0;
int skip_1st_hdr = 0;
int wlen_val = 0;
int rlen_val = 0;
int wlen_list[MAX_LIST_LEN];
int rlen_list[MAX_LIST_LEN];
int srcip_list[MAX_LIST_LEN];
int dstip_list[MAX_LIST_LEN];
int srctcp_list[MAX_LIST_LEN];
int dsttcp_list[MAX_LIST_LEN];
int mpls_list[MAX_LIST_LEN];
int mpls_lbl_list[MAX_LIST_LEN];
int mac_list[MAX_LIST_LEN];
uint32_t hdlc_val = 0; // XXXX: actually want uint32_t. Hrm

int rlen_cnt = 0;
int wlen_cnt = 0;
int srcip_cnt = 0;
int dstip_cnt = 0;
int srctcp_cnt = 0;
int dsttcp_cnt = 0;
int mpls_cnt = 0;
int mpls_lbl_cnt = 0;
int mac_cnt = 0;

int verbose = 0;

/* And some configurations... */
test_printf pf;


/* Main initialization of the test.
 */
int h20s_tests_init(char *params[], int param_cnt, global_params_t *global_params, test_printf f)
{

	ClArgPtr clarg = NULL;
	FILE* errorfile = NULL;
	int argindex = 0;
	int clarg_result = 0;
	int code = 0;
	char buf[256];
	int tmp_val = 0;

	/* Have a local copy of the settings */
	memcpy(&settings, global_params, sizeof(settings));

	/* Set up the command line options. */
	clarg = dagclarg_init(param_cnt, (const char* const *) params);
	
	/* General options. */
	dagclarg_add(clarg, "Print all records", "--print", 'p', CLA_PRINT);
	dagclarg_add(clarg, "Verbose error printing with record output", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add(clarg, "Indicate there's a MAC address after first ERF header", "--hasmac", 'm', CLA_HAS_MAC);
	dagclarg_add(clarg, "Suppress the printing of first ERF header", "--skip", '1', CLA_SKIP_1ST_HDR);
	dagclarg_add(clarg, "Check incrementing time stamp", "--ts", 't', CLA_MONO);
	dagclarg_add(clarg, "Check incrementing Sequence Numbers", "--seq", 's', CLA_SEQNO);
	dagclarg_add_int(clarg, "Check constant WLEN (Repeat for several values)", "--wlen", 'w', "n", &wlen_val, CLA_WLEN);
	dagclarg_add_int(clarg, "Check constant RLEN (Repeat for several values)", "--rlen", 'r', "n", &rlen_val, CLA_RLEN);
	dagclarg_add_string(clarg, "Check constant HDLC Header", "--hdlc", 'h', "n", buf, 256, CLA_HDLC);
	dagclarg_add_string(clarg, "Check constant number of MPLS labels", "--mplscnt", 'C', "n", buf, 256, CLA_MPLS_CNT);
	dagclarg_add_string(clarg, "Check constant Least Significant Byte of MPLS Label", "--mpls", 'M', "n", buf, 256, CLA_MPLS);
	dagclarg_add_string(clarg, "Check constant Least Significant Byte of Source IP Address", "--srcip", 'S', "n", buf, 256, CLA_SRCIP);
	dagclarg_add_string(clarg, "Check constant Least Significant Byte of Destination IP Address", "--dstip", 'D', "n", buf, 256, CLA_DSTIP);
	dagclarg_add_string(clarg, "Check constant Least Significant Byte of Source Port", "--srcport", 'R', "n", buf, 256, CLA_SRCTCP);
	dagclarg_add_string(clarg, "Check constant Least Significant Byte of Destination Port", "--dstport", 'T', "n", buf, 256, CLA_DSTTCP);
	dagclarg_add_string(clarg, "Check constant Least Significant Byte of Destination MAC address", "--mac", 'M', "n", buf, 256, CLA_MAC);
	dagclarg_add(clarg, "Check correct MAC addresses algorithmicly", "--algomac", 'A', CLA_MAC_ALGO);

	/* Parse the command line options. */
	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
		case CLA_PRINT:
			tests |= H20S_PRINT;
			break;

		case CLA_MONO:
			tests |= H20S_MONO;
			break;

		case CLA_RLEN:
			tests |= H20S_RLEN;
			if (rlen_cnt < MAX_LIST_LEN)
				rlen_list[rlen_cnt++] = rlen_val;
			break;

		case CLA_WLEN:
			tests |= H20S_WLEN;
			if (wlen_cnt < MAX_LIST_LEN)
				wlen_list[wlen_cnt++] = wlen_val;
			break;

		case CLA_SEQNO:
			tests |= H20S_SEQNO;
			break;

		case CLA_HDLC:
			hdlc_val = strtoul(buf, NULL, 0);
			tests |= H20S_HDLC;
			break;

		case CLA_SRCIP:
			tmp_val = strtoul(buf, NULL, 0);
			if (srcip_cnt < MAX_LIST_LEN)
				srcip_list[srcip_cnt++] = tmp_val;
			tests |= H20S_SRCIP;
			break;

		case CLA_DSTIP:
			tmp_val = strtoul(buf, NULL, 0);
			if (dstip_cnt < MAX_LIST_LEN)
				dstip_list[dstip_cnt++] = tmp_val;
			tests |= H20S_DSTIP;
			break;

		case CLA_SRCTCP:
			tmp_val = strtoul(buf, NULL, 0);
			if (srctcp_cnt < MAX_LIST_LEN)
				srctcp_list[srctcp_cnt++] = tmp_val;
			tests |= H20S_SRCTCP;
			break;

		case CLA_DSTTCP:
			tmp_val = strtoul(buf, NULL, 0);
			if (dsttcp_cnt < MAX_LIST_LEN)
				dsttcp_list[dsttcp_cnt++] = tmp_val;
			tests |= H20S_DSTTCP;
			break;

		case CLA_MPLS:
			tmp_val = strtoul(buf, NULL, 0);
			/* printf("Adding MPLS Label mpls_list[%d] = 0x%08x\n", mpls_cnt, tmp_val); */
			if (mpls_cnt < MAX_LIST_LEN)
				mpls_list[mpls_cnt++] = tmp_val;
			tests |= H20S_MPLS;
			break;

		case CLA_MPLS_CNT:
			tmp_val = strtoul(buf, NULL, 0);
			if (mpls_lbl_cnt < MAX_LIST_LEN)
				mpls_lbl_list[mpls_lbl_cnt++] = tmp_val;
			tests |= H20S_MPLS_CNT;
			break;

		case CLA_MAC:
			tmp_val = strtoul(buf, NULL, 0);
			if (mac_cnt < MAX_LIST_LEN)
				mac_list[mac_cnt++] = tmp_val;
			tests |= H20S_MAC;
			break;

		case CLA_MAC_ALGO:
			tests |= H20S_MAC_ALGO;
			break;

		case CLA_HAS_MAC:
			has_mac = 1;
			break;
			
		case CLA_SKIP_1ST_HDR:
			skip_1st_hdr = 1;
			break;

		case CLA_VERBOSE:
			verbose = 1;
			break;

		default:
			if (params[argindex][0] == '-')
			{
				/* Unknown option. */
				dagutil_error("unknown option %s\n", params[argindex]);
				/* print_usage(clarg);*/
				return TEST_FAIL;
			}
			break;
		}
		clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

	/* Check for errors in the arguments */
	if (-1 == clarg_result)
	{
		if (argindex < param_cnt)
		{
			dagutil_error("while processing option %s\n", params[argindex]);
		}
		dagclarg_display_usage(clarg, stderr);
		return TEST_FAIL;
	}

	/* Initialize local variables */
	memset(&h20s_counter, 0, sizeof(h20s_counter));
	memset(&cur_rec, 0, sizeof(cur_rec));
	memset(&last_rec, 0, sizeof(last_rec));

	return TEST_PASS;
}

/* This is the main test function.
 *
 * This function basically calls the correct function for testing
 * and increments the correct counters depending on the test result.
 *
 */
int h20s_tests_run(char *rec, int len, char* lastpkt, struct_protocol_t prot, uint64_t rec_cnt)
{
	int ret_val = TEST_PASS;
	int tmp_val;

	// int ret_val;
	rec_msg_len = 0;

	parse_rec(rec, len);

	if (tests & H20S_MONO)
	{	
		tmp_val = test_mono(rec, len, 0);
		incr_counters(tmp_val, &h20s_counter[0]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_WLEN)
	{
		tmp_val = test_wlen(rec, len, 1);
		incr_counters(tmp_val, &h20s_counter[1]);
		ret_val	|= tmp_val;
	}

	if (tests & H20S_RLEN)
	{
		tmp_val = test_rlen(rec, len, 2);;
		incr_counters(tmp_val, &h20s_counter[2]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_SEQNO)
	{
		tmp_val = test_seqno(rec, len, 3);
		incr_counters(tmp_val, &h20s_counter[3]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_HDLC)
	{
		tmp_val = test_hdlc(rec, len, 4);
		incr_counters(tmp_val, &h20s_counter[4]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_SRCIP)
	{
		tmp_val = test_srcip(rec, len, 5);
		incr_counters(tmp_val, &h20s_counter[5]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_DSTIP)
	{
		tmp_val = test_dstip(rec, len, 6);
		incr_counters(tmp_val, &h20s_counter[6]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_SRCTCP)
	{
		tmp_val = test_srctcp(rec, len, 7);
		incr_counters(tmp_val, &h20s_counter[7]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_DSTTCP)
	{
		tmp_val = test_dsttcp(rec, len, 8);
		incr_counters(tmp_val, &h20s_counter[8]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_MPLS)
	{
		tmp_val = test_mpls(rec, len, 9);
		incr_counters(tmp_val, &h20s_counter[9]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_MPLS_CNT)
	{
		tmp_val = test_mpls_cnt(rec, len, 9);
		incr_counters(tmp_val, &h20s_counter[9]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_MAC)
	{
		tmp_val = test_mac(rec, len, 10);
		incr_counters(tmp_val, &h20s_counter[10]);
		ret_val |= tmp_val;
	}

	if (tests & H20S_MAC_ALGO)
	{
		tmp_val = test_mac_algo(rec, len, 10);
		incr_counters(tmp_val, &h20s_counter[10]);
		ret_val |= tmp_val;
	}

	if ((verbose && (TEST_FAIL == ret_val)) || (tests & H20S_PRINT))
	{
		print_record(rec, len, rec_cnt);
	}

	return ret_val;
}

int h20s_tests_err_msg(char *buf, int size)
{
    if (size > 0)
    {
	    strncpy(buf, rec_msg, size);
	    buf[size-1] = '\0'; /* Just in case the buffer was too short...*/
    }
	return TEST_PASS;
}

int h20s_tests_final_msg(char *buf, int size)
{
	char msg[MAX_STR_LEN];
	int used_len = 0;
	int cnt;

	for (cnt=0; used_len<MAX_STR_LEN && cnt<TEST_CNT; cnt++)
	{
		if (h20s_counter[cnt].ran)
		{
			used_len += snprintf(&msg[used_len], MAX_STR_LEN-used_len, 
					     "h20s-%s: %u passed %u failed %u ignored\n",
					     test_names[cnt], h20s_counter[cnt].passed, h20s_counter[cnt].failed, 
					     h20s_counter[cnt].ignored);
		}
	}

    if (size > 0)
    {
	    strncpy(buf, msg, size);
	    buf[size-1] = '\0';
    }

	/* Test will fail if we had failuirs captures or we didn't capture any packets */
	/* Test will have wanting if we idn't fail, but we encountered wanting during the test, or we ignored packets */
	/* Otherwise, we pass the test */
	if ((h20s_failed > 0) || (h20s_total == 0))
		return TEST_FAIL;
	else if ((h20s_warning > 0) || (h20s_ignored > 0))
		return TEST_WARNING;
	else
		return TEST_PASS;
}

int h20s_tests_cleanup()
{
	return TEST_PASS;
}

int h20s_tests_printf (char *format, ...)
{

	printf ("%s: %s - Test printf\n", __FILE__, __FUNCTION__);

	return TEST_PASS;
}

int test_mono(char *rec, int len, int num)
{
	static int first = 1;
	int ret_val = TEST_PASS;

	if (first)
	{
		first = 0;
	}
	else if (cur_rec.ts < last_rec.ts)
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
					"h20s_%s: Last TS 0x%.16"PRIx64" Current TS 0x%.16"PRIx64"\n", test_names[num], last_rec.ts, cur_rec.ts);
		ret_val = TEST_FAIL;
	}

	return ret_val;
}

int test_rlen(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	int cnt;
	int in_list = 0;
	
	for (cnt = 0; cnt < rlen_cnt; cnt++)
		if (cur_rec.rlen == rlen_list[cnt])
			in_list = 1;

	if (!in_list)
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
					"h20s_%s: Packet RLEN %d\n", test_names[num], cur_rec.rlen);
		ret_val = TEST_FAIL;
	}

	return ret_val;
}

int test_wlen(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	int cnt;
	int in_list = 0;
	
	for (cnt = 0; cnt < wlen_cnt; cnt++)
		if (cur_rec.wlen == wlen_list[cnt])
			in_list = 1;

	if (!in_list)
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
					"h20s_%s: Packet WLEN %d\n", test_names[num], cur_rec.wlen);
		ret_val = TEST_FAIL;
	}

	return ret_val;
}

int test_seqno(char *rec, int len, int num)
{	
	static int first = 1;
	static uint32_t min_diff = -1;
	static uint32_t max_diff = 0;
	int ret_val = TEST_PASS;
	uint32_t diff;
	
	if (!first)
	{
		diff = cur_rec.seq_no - last_rec.seq_no;
		if (diff < min_diff)
		{
			min_diff = diff;
			printf("\n\tNew minimum difference: 0x%08x\n", diff);
		}
		if (diff > max_diff)
		{
			max_diff = diff;
			printf("\n\tNew maximum difference: 0x%08x\n", diff);
		}
	}

	if (first)
	{
		first = 0;
	}
	else if (cur_rec.seq_no <= last_rec.seq_no)
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
					"h20s_%s: Last SEQ# %u Current %u diff %d\n", test_names[num], last_rec.seq_no, cur_rec.seq_no, cur_rec.seq_no-last_rec.seq_no);
		ret_val = TEST_FAIL;
	}

	return ret_val;
}

int test_hdlc(char *rec, int len, int num)
{	
	int ret_val = TEST_PASS;
	
	if( cur_rec.outer_etype != hdlc_val ) {
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
					"h20s_%s: got hdlc 0x%.8x expected 0x%.8x\n", test_names[num], cur_rec.outer_etype, hdlc_val);
		ret_val = TEST_FAIL;
	}

	return ret_val;
}

void incr_counters(int res, test_counters_t *cntr)
{
	h20s_total++;
	cntr->ran = 1;
        switch (res)
        {
        case TEST_PASS:
                cntr->passed++;
                break;

        case TEST_IGNORE:
		h20s_ignored++;
                cntr->ignored++;
                break;

        case TEST_FAIL:
		h20s_failed++;
                cntr->failed++;
                break;
                
        case TEST_WARNING:
		h20s_warning++;
                cntr->warning++;
                break;

        default:
                cntr->unknown++;
                break;
        }
}

int test_mpls(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	uint32_t idx;
	uint32_t in_list = 0;

	if (cur_rec.mpls_cnt > 0)
	{
		for (idx=0; idx<mpls_cnt; idx++)
			if ((0xff & (cur_rec.mpls_top >> 12)) == mpls_list[idx])
					in_list = 1;
	
			if (!in_list)
			{
				rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
							"h20s_%s: MPLS not in list 0x%08x\n", test_names[num], cur_rec.mpls_top);
				ret_val = TEST_FAIL;
			}
	}
	else /* We assume it's not MPLS enabled packet and just set a warning */
	{
		ret_val = TEST_IGNORE;
		if (verbose)
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
						"h20s_%s: Packet doesn't have MPLS (Protocol 0x%0x4x, Etype 0x%04x)\n", test_names[num], cur_rec.prot, cur_rec.etype);
	}

	return ret_val;
}

int test_mpls_cnt(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	uint32_t idx;
	uint32_t in_list = 0;

	for (idx=0; idx<mpls_lbl_cnt; idx++)
		if (cur_rec.mpls_cnt == mpls_lbl_list[idx])
			in_list = 1;

	if (!in_list)
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
					"h20s_%s: MPLS label count not in list 0x%08x\n", test_names[num], cur_rec.mpls_cnt);
		ret_val = TEST_FAIL;
	}

	return ret_val;
}

int test_srcip(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	uint32_t idx;
	uint32_t in_list = 0;

	if (cur_rec.ip_ver == 4)
	{
		for (idx=0; idx<srcip_cnt;idx++)
		{
			if (srcip_list[idx] == (cur_rec.src_ip & 0xff))
			{
				in_list = 1;
			}
		}

		if (!in_list)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: Source IP not in list 0x%08x\n", test_names[num], cur_rec.src_ip);
			ret_val = TEST_FAIL;
		}
	}
	else
	{
		if (verbose)
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: Packet doesn't have IP (Protocol 0x%0x4x, Etype 0x%04x)\n", test_names[num], cur_rec.prot, cur_rec.etype);
		return TEST_IGNORE;
	}

	return ret_val;
}

int test_dstip(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	uint32_t idx;
	uint32_t in_list = 0;

	/* Now we point to to the IP Address */
	if (cur_rec.ip_ver == 4)
	{
		for (idx=0; idx<dstip_cnt;idx++)
		{
			if (dstip_list[idx] == (cur_rec.dst_ip & 0xff))
			{
				in_list = 1;
			}
		}

		if (!in_list)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: Destination IP not in list 0x%08x\n", test_names[num], cur_rec.dst_ip);
			ret_val = TEST_FAIL;
		}
	}
	else
	{
		ret_val = TEST_IGNORE;
		if (verbose)
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: Packet doesn't have IP (Protocol 0x%0x4x, Etype 0x%04x)\n", test_names[num], cur_rec.prot, cur_rec.etype);
	}

	return ret_val;
}

int test_srctcp(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	uint32_t idx;
	uint32_t in_list = 0;

	if ((cur_rec.ip_ver == 4) && ((cur_rec.ip_prot == PROT_TCP) || (cur_rec.ip_prot == PROT_UDP)))
	{
		for (idx=0; idx<srctcp_cnt; idx++)
		{
			if (srctcp_list[idx] == (cur_rec.src_port & 0xff))
			{
				in_list = 1;
			}
		}

		if (!in_list)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: Source Port not in list 0x%08x\n", test_names[num], cur_rec.src_port);
			ret_val = TEST_FAIL;
		}
	}
	else
	{
		if (verbose)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: Not IPv4 and/or TCP packet\n", test_names[num]);

		}
		ret_val = TEST_IGNORE;
	}

	return ret_val;

}

int test_dsttcp(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	uint32_t idx;
	uint32_t in_list = 0;

	if ((cur_rec.ip_ver == 4) && ((cur_rec.ip_prot == PROT_TCP) || (cur_rec.ip_prot == PROT_UDP)))
	{
		for (idx=0; idx<dsttcp_cnt; idx++)
		{
			if (dsttcp_list[idx] == (cur_rec.dst_port & 0xff))
			{
				in_list = 1;
			}
		}

		if (!in_list)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: Destination Port not in list 0x%08x\n", test_names[num], cur_rec.dst_port);
			ret_val = TEST_FAIL;
		}
	}
	else
	{
		if (verbose)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: Not IPv4 and/or TCP packet\n", test_names[num]);

		}
		ret_val = TEST_IGNORE;
	}

	return ret_val;
}

int test_mac(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	int in_list = 0;
	int idx;

	/* We check that the least significant byte of the destination
	 * MAC address is in the supplied list */
	if (has_mac)
	{
		for (idx=0; idx<mac_cnt; idx++)
		{
			if (cur_rec.dst_mac[5] == mac_list[idx])
			{
				in_list = 1;
			}
		}
		if (!in_list)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: MAC Address doesn't match (Got %02x:%02x:%02x:%02x:%02x:%02x)\n", 
									test_names[num], cur_rec.dst_mac[0], cur_rec.dst_mac[1], cur_rec.dst_mac[2],
									cur_rec.dst_mac[3], cur_rec.dst_mac[4], cur_rec.dst_mac[5]);
			ret_val = TEST_FAIL;
		}
	}
	else
	{
		if (verbose)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: MAC Addresses were not indicated as present in the record.\n", test_names[num]);

		}
		ret_val = TEST_IGNORE;
	}

	return ret_val;
}

int test_mac_algo(char *rec, int len, int num)
{
	int ret_val = TEST_PASS;
	int idx;
	uint8_t xored = 0;

	/* We extract the source and destination IP Addresses
	 * and "xor" all 8 bytes to one another. This gives us a HASH value
	 * that is 10 to 12 bits wide. We expect that it'll match the
	 * 12 least significant bits of the destination MAC address */ 

	if (has_mac && (cur_rec.ip_ver == 4))
	{
		for (idx=0; idx<4; idx++)
		{
			xored ^= (cur_rec.src_ip >> (8*idx)) ^ (cur_rec.dst_ip >> (8*idx));
		}
		if (xored != cur_rec.dst_mac[5])
		{
			ret_val = TEST_FAIL;
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: MAC Address doesn't match (Got %02x:%02x:%02x:%02x:%02x:%02x, Expected %02x)\n", 
									test_names[num], cur_rec.dst_mac[0], cur_rec.dst_mac[1], cur_rec.dst_mac[2],
									cur_rec.dst_mac[3], cur_rec.dst_mac[4], cur_rec.dst_mac[5], xored);

		}
	}
	else
	{
		if (verbose)
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, 
									"h20s_%s: MAC Addresses were not indicated as present in the record or not IPv4.\n", test_names[num]);

		}
		ret_val = TEST_IGNORE;
	}
	
	return ret_val;
}


/* Print the ERF record based on the global configuration */
int print_record(char *rec, int len, uint64_t rec_cnt)
{

	dag_record_t *drec =(dag_record_t*) rec;
	uint32_t seq_no, flags, cnt;
	uint64_t tmp;
	
	printf("h20s_print: %"PRIu64"\n", rec_cnt);
	if (!skip_1st_hdr)
	{
		printf("ts1=0x%.16"PRIx64"\n", drec->ts);
		printf("type1: %s\n", dagerf_type_to_string(drec->type, TT_ATM));
		printf("dserr=%d rxerr=%d trunc=%d vlen=%d ifc=%d lctr=%d rlen=%d wlen=%d\n",
		       (int)drec->flags.dserror,
		       (int)drec->flags.rxerror,
		       (int)drec->flags.trunc,
		       (int)drec->flags.vlen,
		       (int)drec->flags.iface,
		       ntohs(drec->lctr),
		       ntohs(drec->rlen),
		       ntohs(drec->wlen));
	}
	/* Skip header and Padding for ethernet */
	len -= 16+2;
	rec += 16+2;

	if (has_mac)
	{
		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]);
		printf("etype=0x%04x\n", ntohs(drec->rec.eth.etype));
		len -= 14;
		rec += 14;

	}

	/* New record start after the MAC addresses and ETYPE */
	/* Don't forget the PAD bytes */
	drec = (dag_record_t*)rec;

	/* Now we can print the real encapsulated record */
	printf("ts2=0x%.16"PRIx64"\n", drec->ts);
	printf("type2: %s\n", dagerf_type_to_string(drec->type, TT_ATM));
	printf("dserr=%d rxerr=%d trunc=%d vlen=%d ifc=%d lctr=%d rlen=%d wlen=%d\n",
	       (int)drec->flags.dserror,
	       (int)drec->flags.rxerror,
	       (int)drec->flags.trunc,
	       (int)drec->flags.vlen,
	       (int)drec->flags.iface,
	       ntohs(drec->lctr),
	       ntohs(drec->rlen),
	       ntohs(drec->wlen));
	/* Skip the 2nd header */
	len -= 16;
	rec += 16;

	/* Print extension header */
	tmp = *((uint64_t*)rec);
	// printf("%.16"PRIx64"\n", tmp);
	seq_no = ntohl(tmp >> 32);
	flags = ntohl(0xffffffff & tmp);
	if (flags & 8)
	{
		printf("ext=%x seq=%u hit=%d mhit=%d hash=%u clr=%u drop=%d strm=%d\n", 
		       (int) (0xff & (flags >> 24)),
		       seq_no,
		       (int) (0x1 & (flags >> 20)),
		       (int) (0x1 & (flags >> 19)),
		       (int) (0xff & (flags >> 11)),
		       (int) (0xff & (flags >> 3)),
		       (int) (0x1 & (flags >> 2)),
		       (int) (0x3 & flags));
	}
	else
	{
		printf("ext=%x seq=%u hit=%d mhit=%d clr=%u drop=%d strm=%d\n", 
		       (int) (0xff & (flags >> 24)),
		       seq_no,
		       (int) (0x1 & (flags >> 20)),
		       (int) (0x1 & (flags >> 19)),
		       (int) (0xffff & (flags >> 3)),
		       (int) (0x1 & (flags >> 2)),
		       (int) (0x3 & flags));
	}
	/* Skip the extension header */
	len -= 8;
	rec += 8;
	
	/* Print the payload */
	for (cnt=1; cnt<=len; cnt++)
	{
		printf("%02x ", *(uint8_t*)rec++);
		if ((cnt % 16) == 0)
			printf("\n");
	}

	printf("\n\n");

	return TEST_PASS;
}

/* Here we parse the record and generate a global structure that is used by all the tests
 * to check what ever needs checking */
int parse_rec(char *rec, int len)
{
	
	dag_record_t *drec = (dag_record_t*)rec;
	uint8_t have_mpls = 0;
	uint64_t tmp64;
	uint32_t tmp32;
	
	/* Preserve the last record just in case some one might want to use it... */	
	memcpy(&last_rec, &cur_rec, sizeof(cur_rec));
	memset(&cur_rec, 0, sizeof(cur_rec));

	if (len > 16)
	{
		cur_rec.outer_ts = drec->ts;
		cur_rec.outer_rlen = ntohs(drec->rlen);
		cur_rec.outer_wlen = ntohs(drec->wlen);
		cur_rec.outer_type = drec->type;
	}

	/* We skip the above header and the Ethernet ERF type padding */
	len -= 16 + 2;
	rec += 16 + 2;

	if (has_mac && (len > 14))
	{
		cur_rec.outer_etype = ntohs(drec->rec.eth.etype);
		memcpy(cur_rec.dst_mac, drec->rec.eth.dst, 6);
		memcpy(cur_rec.src_mac, drec->rec.eth.src, 6);
		len -= 14;
		rec += 14;
	}

	/* Now we have another ERF record this ttime POS */
	if (len > 16)
	{
		drec = (dag_record_t*)rec;
		cur_rec.ts = drec->ts;
		cur_rec.rlen = ntohs(drec->rlen);
		cur_rec.wlen = ntohs(drec->wlen);
		cur_rec.type = drec->type;
		cur_rec.flags = drec->flags;
		len -= 16;
		rec += 16;
	}

	/* Process extension header */
	if (len > 8)
	{
		tmp64 = *((uint64_t*)rec);
		cur_rec.seq_no = ntohl(tmp64 >> 32);
		cur_rec.ext_top = ntohl(tmp64 & 0xffffffff);
		cur_rec.has_hash = (cur_rec.ext_top & 8) ? 1 : 0;
		cur_rec.hash = (cur_rec.ext_top & 8) ? (0xff & (cur_rec.ext_top >> 11)) : 0;
		len -= 8;
		rec += 8;
	}

	/* Process protocol and etype */
	if (len > 4)
	{
		tmp32 = ntohl(*(uint32_t*)rec);
		cur_rec.etype = tmp32 & 0xffff;
		cur_rec.prot = tmp32 >> 16;
		len -= 4;
		rec += 4;
	}

	/* Process MPLS if Etype and Prot match */
	if ((len > 4) && ((tmp32 == 0x0f008847) || (tmp32 == 0x0f008848) || 
					  (tmp32 = 0xff030281) || (tmp32 == 0xff030283)))
	{
		have_mpls = 1;
		cur_rec.mpls_top = ntohl( *((uint32_t*)rec) );
		cur_rec.mpls_cnt = 1;
		tmp32 = ntohl( *((uint32_t*)rec) );
		while ((len > 4) && (tmp32 & (1<<23)))
		{
			len -= 4;
			rec += 4;
			tmp32 = ntohl( *((uint32_t*)rec) );
			++cur_rec.mpls_cnt;
		}
	}

	/* Now we can process the IP Header */
	if (len > 20)
	{
		tmp32 = ntohl( *(uint32_t*)rec);
		cur_rec.ip_ver = tmp32 >> 28;

		/* We only deal with IPv4*/
		if (cur_rec.ip_ver == 4)
		{
			cur_rec.ip_len = 0xf & (tmp32 >> 24);
			cur_rec.ip_prot = 0xff & (ntohl( *(uint32_t*)(rec+8) ) >> 16);
			cur_rec.src_ip = ntohl(*(uint32_t*)(rec+12));
			cur_rec.dst_ip = ntohl(*(uint32_t*)(rec+16));
		}
		len -= 4 * cur_rec.ip_len;
		rec += 4 * cur_rec.ip_len;
	}

	/* Now we can check for TCP or UDP ports */
	if ((len > 4) && ((cur_rec.ip_prot == PROT_TCP) || (cur_rec.ip_prot == PROT_UDP)))
	{
		tmp32 = ntohl( *(uint32_t*)rec );
		cur_rec.src_port = tmp32 >> 16;
		cur_rec.dst_port = tmp32 & 0xffff;
	}

	return TEST_PASS;
}

