/*
 * 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: pkt_steer.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 "pkt_steer.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_TESTCASE,
	CLA_TESTNUM
};

/* Structure to hold the information required by the tests */
typedef struct fields
{
	uint8_t  ifc;
	uint8_t  rectype;
	uint16_t rlen;
	uint16_t wlen;
	uint16_t colour;
	uint8_t  stream;
	uint8_t  hash;
	uint16_t lctr;
	uint16_t etype;
	uint32_t src_ip;
	uint32_t dst_ip;
	uint16_t ip_prot;
	uint16_t src_port;
	uint16_t dst_port;
	uint8_t  tcp_flags;
	uint8_t  icmp_type;
	uint8_t  icmp_code;
} fields_t;

/* Internal Function prototypes */
int parse_rec(char *rec, int len, fields_t *flds);
void incr_counters(int res);
void load_hat_table(void);
uint32_t hlb_range_ui2reg(uint32_t ui_value);
uint32_t calc_crc(fields_t *flds, int tuple);

int testcase1(int testnum, fields_t *flds, uint64_t rec_cnt);

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

int testcase;
int testnum;

global_params_t settings;

/* Global counters for final reporting */
uint32_t total = 0;
uint32_t ignored = 0;
uint32_t failed = 0;
uint32_t passed = 0;
uint32_t warning = 0;
uint32_t unknown = 0;

uint32_t hat_table[1024];

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


/* Main initialization of the test.
 * Valid parameters are: 
 * -c <testcase> 
 * -t <testnum>
 */
int pkt_steer_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;

	/* 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_int(clarg, "Select test set", "--test_case", 'c', "n", &testcase, CLA_TESTCASE);
	dagclarg_add_int(clarg, "Select a test from the test set", "--test_num", 't', "n", &testnum, CLA_TESTNUM);

	/* Parse the command line options. */
	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
		case CLA_TESTCASE:
		case CLA_TESTNUM:
			/* Do nothing */
			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;
	}

	dagcrc_make_aal5_table();
	load_hat_table();

	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 pkt_steer_run(char *rec, int len, char* lastpkt, struct_protocol_t prot, uint64_t rec_cnt)
{

	fields_t flds;
	int ret_val;

	rec_msg_len = 0;

	/* Parse the record and verify that we have a correct record. */
	if ((ret_val = parse_rec(rec, len, &flds)) != TEST_PASS)
	{
		incr_counters(ret_val);
		return ret_val;
	}

	/* Select the correct test case and test to run, return the result */
	switch (testcase)
	{
	case 1:
		ret_val = testcase1(testnum, &flds, rec_cnt);
		incr_counters(ret_val);
		break;

	default:
		incr_counters(TEST_WARNING);
		return TEST_WARNING;
	}

	return ret_val;
}

int pkt_steer_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 pkt_steer_final_msg(char *buf, int size)
{
	char msg[MAX_STR_LEN];

	snprintf(msg, MAX_STR_LEN, 
		 "pkt_steer: %d.%d pass %u\npkt_steer: %d.%d fail %u\npkt_steer: %d.%d warning %u\npkt_steer: %d.%d ignore %u\n",
		 testcase, testnum, passed, 
		 testcase, testnum, failed, 
		 testcase, testnum, warning, 
		 testcase, testnum, 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 ((failed > 0) || (total == 0))
		return TEST_FAIL;
	else if ((warning > 0) || (ignored > 0))
		return TEST_WARNING;
	else
		return TEST_PASS;
}

int pkt_steer_cleanup()
{
	return TEST_PASS;
}

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

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

	return TEST_PASS;
}

int testcase1(int testnum, fields_t *flds, uint64_t rec_cnt)
{
	int ret_val = TEST_PASS;
	int len_incr;
	uint32_t crc;

	switch(testnum)
	{
	case 1:
		if ((flds->rectype != 19) && (flds->rectype != 20))
		{
			len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Recieived unexpected ERF Type (%d).", flds->rectype);
			rec_msg_len += len_incr;
			ret_val = TEST_FAIL;
		}
		else if ((flds->rectype == 19) || (flds->rectype == 20))
		{
			// This test only handles records of type 19 & 20
			// First we check the HAT is correct
			crc = calc_crc(flds, 2) & 0x03FF;
			if (hat_table[crc] != flds->hash)
			{
				
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Hash value is not correct (CRC %d, HAT %d, Table %d).", 
						    crc, hat_table[crc],  flds->hash);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			// Than we check the Colour is what we expect
			if (flds->colour != (flds->src_port & 0x03FF))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Unexpected colour (Colour %d, Expected %d).", 
						    flds->colour, (flds->src_port & 0x03FF));
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			// Last we check that the capture stream matches the hash and colour
			if (((flds->lctr & 0x03FF) % 8) != (settings.cap_stream / 2))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Colour & HASH not expected on this stream (Entry %d, Exp. %d).", 
						    flds->lctr & 0x03FF, (flds->lctr & 0x03FF) % 8);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
		}
		break;

	case 2:
		if ((flds->rectype != 19) && (flds->rectype != 20))
		{
			len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Recieived unexpected ERF Type (%d).", flds->rectype);
			rec_msg_len += len_incr;
			ret_val = TEST_FAIL;
		}
		else if ((flds->rectype == 19) || (flds->rectype == 20))
		{
			// This test only handles records of type 19 & 20
			// First we check the HAT is correct
			crc = calc_crc(flds, 3) & 0x03FF;
			if (hat_table[crc] != flds->hash)
			{
				
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Hash value is not correct (CRC %d, HAT %d, Table %d).", 
						    crc, hat_table[crc],  flds->hash);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			// Than we check the Colour is what we expect
			if (flds->colour != ((flds->src_ip & 0x01F) << 5 | (flds->dst_ip & 0x01F)))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Unexpected colour (Colour %d, Expected %d).", 
						    flds->colour, ((flds->src_ip & 0x01F) << 5 | (flds->dst_ip & 0x01F)));
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			// Last we check that the capture stream matches the hash and colour
			if (((flds->lctr & 0x03FF) % 8) != (settings.cap_stream / 2))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Colour & HASH not expected on this stream (Entry %d, Exp. %d).", 
						    flds->lctr & 0x03FF, (flds->lctr & 0x03FF) % 8);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
		}
		break;

	case 3:
		if ((flds->rectype != 19) && (flds->rectype != 20))
		{
			len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Recieived unexpected ERF Type (%d).", flds->rectype);
			rec_msg_len += len_incr;
			ret_val = TEST_FAIL;
		}
		else if ((flds->rectype == 19) || (flds->rectype == 20))
		{
			/* This test truncate the record to the minimum at the GPP
			   this means we won't have enought data to seer, hash, or classifiy the record
			   so we don't actually need to check anything other then the record length
			*/
			if (flds->rlen > 48)
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "RLEN is higher than 48 Bytes (%d).", flds->rlen);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;		
			}

		}
		break;

	case 4:
		if ((flds->rectype != 19) && (flds->rectype != 20))
		{
			len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Recieived unexpected ERF Type (%d).", flds->rectype);
			rec_msg_len += len_incr;
			ret_val = TEST_FAIL;
		}
		else if ((flds->rectype == 19) || (flds->rectype == 20))
		{
			// This test only handles records of type 19 & 20
			// First we check the HAT is correct
			crc = calc_crc(flds, 4) & 0x03FF;
			if (hat_table[crc] != flds->hash)
			{
				
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Hash value is not correct (CRC %d, HAT %d, Table %d).", 
						    crc, hat_table[crc],  flds->hash);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			// Than we check the Colour is what we expect
			if (flds->colour != ((flds->src_ip & 0x01F) << 5 | (flds->dst_ip & 0x01F)))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Unexpected colour (Colour %d, Expected %d).", 
						    flds->colour, ((flds->src_ip & 0x01F) << 5 | (flds->dst_ip & 0x01F)));
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			// Last we check that the capture stream matches the hash and colour
			if (((flds->lctr & 0x03FF) % 8) != (settings.cap_stream / 2))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Colour & HASH not expected on this stream (Entry %d, Exp. %d).", 
						    flds->lctr & 0x03FF, (flds->lctr & 0x03FF) % 8);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
		}
		break;

	case 5:
		if ((flds->rectype != 19) && (flds->rectype != 20))
		{
			len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Recieived unexpected ERF Type (%d).", flds->rectype);
			rec_msg_len += len_incr;
			ret_val = TEST_FAIL;
		}
		else if ((flds->rectype == 19) || (flds->rectype == 20))
		{
			// This test only handles records of type 19 & 20
			// First we check the HAT is correct
			crc = calc_crc(flds, 4) & 0x03FF;
			if (hat_table[crc] != flds->hash)
			{
				
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Hash value is not correct (CRC %d, HAT %d, Table %d).", 
						    crc, hat_table[crc],  flds->hash);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			// Than we check the Colour is what we expect
			if (flds->colour != ((flds->src_ip & 0x01F) << 5 | (flds->dst_ip & 0x01F)))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Unexpected colour (Colour %d, Expected %d).", 
						    flds->colour, ((flds->src_ip & 0x01F) << 5 | (flds->dst_ip & 0x01F)));
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			// Last we check that the capture stream matches the hash and colour
			// Large HASH
			if ((settings.cap_stream <= 6) && (((flds->lctr & 0x03FF) % 4) != (settings.cap_stream / 2)))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Colour & HASH not expected on this stream (Entry %d, Exp. 0,2,4,6).", 
						    flds->lctr & 0x03FF);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			else if (((settings.cap_stream == 8) || (settings.cap_stream == 10)) &&
				 (flds->colour >= 512))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Colour & HASH not expected on this stream (Entry %d, 8,10).", 
						    flds->lctr & 0x03FF);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}
			else if (((settings.cap_stream == 12) || (settings.cap_stream == 14)) &&
				 (flds->colour < 512))
			{
				len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Colour & HASH not expected on this stream (Entry %d, Exp. 12,14).", 
						    flds->lctr & 0x03FF);
				rec_msg_len += len_incr;
				ret_val = TEST_FAIL;
			}

		}
		break;

	default:
		len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Warning: Test case 1 recieved an unknown test");
		rec_msg_len += len_incr;
		return TEST_WARNING;
		break;

	}

	len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "\n");
	rec_msg_len += len_incr;
	return ret_val;
}

void incr_counters(int res)
{
	switch (res)
	{
	case TEST_PASS:
		passed++;
		break;

	case TEST_IGNORE:
		ignored++;
		break;

	case TEST_FAIL:
		failed++;
		break;
		
	case TEST_WARNING:
		warning++;
		break;

	default:
		unknown++;
		break;
	}
}

int  parse_rec(char *rec, int len, fields_t *flds)
{
	dag_record_t* drec = (dag_record_t*) rec;
	uint32_t tmp;
	uint16_t color;
	uint16_t stream = 0;
	uint16_t hash = 0;
	uint16_t etype;
	uint8_t  ihl;
	uint8_t *pload;
	uint8_t *tp;
	
	/* We don't handle legacy records, so we ignore them and report that... */
	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		ignored++;
		return TEST_IGNORE;
	} 
	
	/* If it's not legacy record than it's modern and we continue... */
	/* We also need at least 20 Bytes to extract the ether-type */
	if (len < 20)
		return TEST_IGNORE;
	else
		len -= 20;

	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)
		{
			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)
		{
			color = ntohs(drec->lctr);
			hash = color & 0x0f;
			color = color >> 4;
			tmp = ntohl(drec->rec.pos.hdlc);
			etype = (uint16_t) (tmp & 0xffff);
		} 
		else 
		{
			tmp = ntohl(drec->rec.pos.hdlc);
			color = (uint16_t) ((tmp>>16) & 0xffff);
			stream = color & 0x03;
			color = color >> 2;
			etype = (uint16_t) (tmp & 0xffff);
		}
		flds->lctr = ntohs(drec->lctr);
		flds->ifc = drec->flags.iface;
		flds->rectype = drec->type;
		flds->rlen = ntohs(drec->rlen);
		flds->wlen = ntohs(drec->wlen);
		flds->colour = color;
		flds->stream = stream;
		flds->hash = hash;
		flds->etype = etype;
		
		/* Make sure the payload points to the IP header */
		pload = drec->rec.pos.pload;

		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);
			hash = 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 */

		flds->lctr = ntohs(drec->lctr);
		flds->ifc = drec->flags.iface;
		flds->rectype = drec->type;
		flds->rlen = ntohs(drec->rlen);
		flds->wlen = ntohs(drec->wlen);
		flds->colour = color;
		flds->stream = stream;
		flds->hash = hash;
		flds->etype = etype;
		break;
		
	default:
		ignored++;
		return TEST_IGNORE;

	}

	/* We need at least 20 bytes for IP header */
	if (len < 20)
		return TEST_IGNORE;

	if ((etype == 0x0800) || (etype == 0x0021)) /* IP v4 */
	{
		ihl = ((uint8_t) pload[0]) & 0x0f;
		flds->ip_prot = pload[9];
		flds->src_ip = IP_ADDR_MAKE(pload[12], pload[13], pload[14], pload[15]);
		flds->dst_ip = IP_ADDR_MAKE(pload[16], pload[17], pload[18], pload[19]);
		
		len -= 4*ihl;

		pload = pload + 4*ihl;
		if (flds->ip_prot == 1) /* ICMP */
		{
			if (len < 2)
				return TEST_IGNORE;
			flds->icmp_type = pload[0];
			flds->icmp_code = pload[1];
		}
		else if (flds->ip_prot == 6)  /* TCP */
		{
			if (len < 13)
				return TEST_IGNORE;
			flds->src_port = (uint16_t) (pload[0] << 8 | pload[1]);
			flds->dst_port = (uint16_t) (pload[2] << 8 | pload[3]);
			flds->tcp_flags = pload[13];
		}
		else if (flds->ip_prot == 17)  /* UDP */
		{
			if (len < 4)
				return TEST_IGNORE;
			flds->src_port = (uint16_t) (pload[0] << 8 | pload[1]);
			flds->dst_port = (uint16_t) (pload[2] << 8 | pload[3]);
		}
	}
	
	return TEST_PASS;
}

void load_hat_table(void)
{
	dag_component_t root;
	dag_component_t hat = NULL;
	dag_card_ref_t dag_ref = NULL;
	attr_uuid_t attr = 0;
	hat_range_t *hlb_range;

	uint32_t idx, range, start, end;

	memset(hat_table, -1, sizeof(hat_table));

	if (NULL == (dag_ref = dag_config_init(settings.dagname)))
	{
		dagutil_panic("Cannot find a the card %s. This test cannot run when checking a file.\n", settings.dagname);
	}
	if (!(root = dag_config_get_root_component(dag_ref)))
	{
		dagutil_panic("Cannot get root component.\n");
	}
	if (!(hat = dag_component_get_subcomponent(root, kComponentHAT, 0)))
	{
		dagutil_panic("Cannot find a HAT component.\n");
	}
	if (!(attr = dag_component_get_attribute_uuid(hat, kStructAttributeHATRange)))
	{
		dagutil_panic("Cannot get HAT Range attribute\n");
	}
	if (kDagErrNone != dag_config_get_struct_attribute(dag_ref, attr, (void *) &hlb_range))
	{
		dagutil_panic("Cannot read HLB Table from cartd\n");
	}
	/* We don'tneed to check for invalid ranges because we don't
	   expect the API to return them */
	for(range=0; range<hlb_range->bin_num; range++)
	{
		start = hlb_range_ui2reg(hlb_range->bin_range[range].min);
		end = hlb_range_ui2reg(hlb_range->bin_range[range].max);
		for (idx = start; idx < end; idx++)
		{
			hat_table[idx] = (range + 1);
			if (hat_table[idx] == 16)
				hat_table[idx] = 0;
			// printf("HAT[%d] = %d\n", idx, hat_table[idx]);
		}
	}
	/* Here we should dispose of the card reference... */
	dag_config_dispose(dag_ref);
}

uint32_t hlb_range_ui2reg(uint32_t ui_value)
{
	uint32_t ret_val;
	if(ui_value==999)
		return 1023;
	ret_val = ui_value*1024/1000;
		return ret_val;	
}

uint32_t calc_crc(fields_t *flds, int tuple)
{
	int len_incr;
	uint32_t ports;
	uint32_t val_for_crc, crc_res;
	int check = 1;

	ports = (uint32_t) ((flds->src_port << 16 ) | flds->dst_port);

	switch(tuple)
	{
	case 2: /* 2-tuple */
		val_for_crc = flds->src_ip ^ flds->dst_ip;
		break;
	case 3: /* 3-tuple */
		val_for_crc = flds->src_ip ^ flds->dst_ip ^ flds->ip_prot;
		break;
	case 4: /* 4-tuple */
		val_for_crc = flds->src_ip ^ flds->dst_ip ^ flds->ip_prot ^ flds->ifc;
		break;
	case 5: /* 5-tuple */
		if ((flds->ip_prot != PROT_TCP) && (flds->ip_prot != PROT_UDP))
			check = 0;
		val_for_crc = flds->src_ip ^ flds->dst_ip ^ flds->ip_prot ^ ports;
		break;
	case 6: /* 6-tuple */
		if ((flds->ip_prot != PROT_TCP) && (flds->ip_prot != PROT_UDP))
			check = 0;
		val_for_crc = flds->src_ip ^ flds->dst_ip ^ flds->ip_prot ^ flds->ifc ^ ports;
		break;
		
	default:
		len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,
				    "Warning: Test case %d recieved an unknown test\n", 
				    testcase);
		rec_msg_len += len_incr;
		break;
		
	}

	val_for_crc = bswap_32(val_for_crc);
	crc_res = dagcrc_aal5_crc( CRC_INIT, (char*)&val_for_crc, 4);
	
	return crc_res;
}
