/*
 * 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$
 */

#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 "dagcam/infiniband_proto.h"
#include "../../../lib/dagcam/bfs_parse.h"
#include "bfs_tests.h"
#include "dag_protocol_decode.h"

/* #define BFS_TEST_ICMP */

/* Some IP macros to constract and extract the fields */
#define PROT_ICMP (1)
#define PROT_TCP (6)
#define PROT_UDP (17)
#define ICMP_HDR_LEN (12)
#define UDP_HDR_LEN (8)
#define TCP_HDR_MIN_LEN (20)


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


/* IPv6 Extension header values */
#define HOPOPT_EXT_HEADER        0    
#define ROUTE_EXT_HEADER        43
#define FRAG_EXT_HEADER            44
#define AUTH_EXT_HEADER            51
#define ESP_EXT_HEADER            50
#define OPTS_EXT_HEADER            60

//#define BFS_TESTS_DEBUG 1 
#ifdef BFS_TESTS_DEBUG
#define DEBUG_OUT  stdout
#endif
/* Command Line Arguments constants */
enum
{
    CLA_HASH_VALUE,
    CLA_N_TUPLE,
    CLA_FILTER_FILENAME,
    CLA_HASH_STRING,
    CLA_INPUT_TEST_COLOR,
    CLA_HELP,
    CLA_DEFAULT_COLOR,
    CLA_NO_HASH_TESTS,
    CLA_CRCLEN
};
enum 
{
    kExtHdrTypeBFS = 1,
    kExtHdrTypeSig = 2,
};
enum 
{
    kBfsFilterValidColor = 0x1,
    kBfsFilterExpectedColor  = 0x2
};
/* 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  hash;
    uint32_t raw_hash;
    uint32_t pload_hash;
    uint16_t lctr;
    uint16_t etype;
    uint16_t ip_prot;
    uint16_t src_port;
    uint16_t dst_port;
    uint8_t  tcp_flags;
#ifdef BFS_TEST_ICMP
    uint8_t  icmp_type;
    uint8_t  icmp_code;
#endif
    uint8_t  ip_version;
    uint32_t mpls_top;
    uint32_t mpls_btm;
    uint8_t label_count;
    uint64_t src_ipaddr[2];
    uint64_t dst_ipaddr[2];
    uint16_t  vlan_1;
    uint16_t  vlan_2;
    uint8_t   bfs_ext_found;
    uint8_t   *pload_ptr;
    uint16_t   pload_len ;
} bfs_tests_fields_t;

/* Internal Function prototypes */
static int bfs_tests_parse_rec(char *rec, int len, bfs_tests_fields_t *flds);
static void bfs_tests_incr_counters(int res);
static int8_t bfs_tests_load_hat_details_from_card(void);
static int8_t bfs_tests_load_color_details_from_card(void);
static void bfs_tests_convert_range_to_table(hat_range_t *in_range);
static uint32_t bfs_tests_hlb_range_ui2reg(uint32_t ui_value);
static uint32_t bfs_tests_calc_crc(bfs_tests_fields_t *flds, int tuple);
static int8_t bfs_tests_convert_input_string(void);
static int8_t bfs_tests_check_overlapping_ranges(hat_range_t *hat);
static int bfs_tests_filter_parse_rules(const char* filter_rule_filename);
int bfs_tests_validate_match(const bfs_tests_fields_t *flds,const  bfs_filter_rule_t* const matched_rule, int fill_error);
static int bfs_tests_do_verify(bfs_tests_fields_t *flds, uint64_t rec_cnt);
static int bfs_tests_validate_input_parameters(void);
static int bfs_test_verify_hashes(bfs_tests_fields_t *in_flds, uint64_t rec_cnt);
static int bfs_tests_check_any_color_match(bfs_tests_fields_t *flds);

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


global_params_t bfs_tests_settings;

/* Global counters for final reporting */ 
static uint64_t ignored = 0;
static uint64_t failed = 0;
static uint64_t passed = 0;
static uint64_t warning = 0;
static uint64_t unknown = 0;
static uint64_t color_test_passed = 0;
static uint64_t flow_hash_test_passed = 0;
static uint64_t pload_hash_test_passed = 0;
static uint64_t color_test_sram_missed = 0;

#define BFS_TEST_MAX_RULES (128)
#define PLOAD_CRC_ALIGN_LEN (8)

static uint32_t g_hat_table[1024];
static int g_n_tuple = 0;
static int g_fcs_len = 4; /* CRC length to be stripped for pload hash calc*/
static int g_sram_color_default = -1;
static int g_input_hash_value = -1;
static char g_input_hash_string[4096];
static char g_filter_filename[4096];
static bfs_filter_rule_t g_bfs_filter_test_rules[BFS_TEST_MAX_RULES];
/* if the rule index corresponds to a rule in the filter file */
static uint8_t g_color_filter_flags[BFS_TEST_MAX_RULES] = {0};
/* flags to chk whether to do hash and color tests */
static uint8_t g_do_bfs_hash_tests = 1;
static uint8_t g_do_bfs_color_test = 0;
static int  g_input_test_color = -1;


test_printf pf;

#ifdef BFS_TESTS_DEBUG
static void bfs_test_debug_print_fields(bfs_tests_fields_t *flds)
{
    fprintf(DEBUG_OUT, "ifc: %u type: %u rlen: %u wlen: %u bfs-ext: %u colour: %u hash: %u rawhash: %u\n", flds->ifc, flds->rectype, flds->rlen, flds->wlen, flds->bfs_ext_found, flds->colour, flds->hash,flds->raw_hash);
    fprintf(DEBUG_OUT, "etype: 0x%04x mpls-top: 0x%08x mpls-btm: 0x%08x vlan1: 0x%04x vlan2: 0x%04x lbl-count %u\n",flds->etype, flds->mpls_top, flds->mpls_btm, flds->vlan_1, flds->vlan_2, flds->label_count);
    fprintf(DEBUG_OUT, "ip-ver: %u ip-src0: 0x%08"PRIx64" ip-src1: 0x%08"PRIx64" ip-dst0: 0x%08"PRIx64" ip-dst1: 0x%08"PRIx64" ip-prot: 0x%04x\n",flds->ip_version, flds->src_ipaddr[0], flds->src_ipaddr[1], flds->dst_ipaddr[0], flds->dst_ipaddr[1], flds->ip_prot);
    fprintf(DEBUG_OUT, "src-port: %u dst-port: %u tcp-flags:0x%02x\n",flds->src_port, flds->dst_port,  flds->tcp_flags);
#ifdef BFS_TEST_ICMP
    fprintf(DEBUG_OUT, "icmp-type: %u icmp-code: %u\n",  flds->icmp_type, flds->icmp_code);
#endif
}
#endif 

#ifdef _WIN32
#define inline 
#endif


/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_parse_ext_header
 DESCRIPTION :   Parse extension header and fill appropriate fields in bfs_tests_field.
 PARAMETERS :    erf_header - start of the ERF packet.
                rem_len : contains the remaining length and updated the new length after parsing.
                in_flds: bfs_tests_field
  RETURNS :       -1 if failed ( insufficient len) 
                   number of extn headers
---------------------------------------------------------------------*/
static inline uint8_t bfs_tests_parse_ext_header(char *erf_header, int *rem_len, bfs_tests_fields_t *in_flds)
{
    uint64_t hdr;
    uint8_t  hdr_type;
    uint32_t hdr_num;

    in_flds->bfs_ext_found  = 0;
    hdr_num = 0;

    /* check if we have any extension headers */
    if ( (erf_header[8] & 0x80) == 0x00 )
    {
        /* no ext header */
        return 0;
    }
    /* loop over the extension headers */
    do {
        if ( *rem_len <=  ((hdr_num+1) * 8) )
        {
            /* insuffiicent len */
            return -1;
        }        
        /* get the header type */
        hdr_type = erf_header[(16 + (hdr_num * 8))];

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

        hdr_num++;

        /* check the header */
        switch ( hdr_type & 0x7F )
        {
            case EXT_HDR_TYPE_BFS:
                in_flds->colour = (uint32_t)((hdr >> 32) & 0x0ffff);
                in_flds->hash  = (uint32_t)((hdr >> 48) & 0xff);
                in_flds->raw_hash =  (uint32_t)(hdr & 0xffffffff);
                in_flds->bfs_ext_found = kExtHdrTypeBFS;
                break;
            case EXT_HDR_TYPE_SIGNATURE:
                in_flds->colour = (uint32_t)((hdr >> 24) & 0xff);
                in_flds->raw_hash =  (uint32_t)(hdr & 0xffffff);
                in_flds->pload_hash = (uint32_t)((hdr >> 32) & 0x0ffffff);
                in_flds->bfs_ext_found = kExtHdrTypeSig;
                break;
            default:
                break;
        }
    } while ( hdr_type & 0x80 );
    *rem_len -= ( 8* hdr_num);
    return hdr_num;
}

/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_parse_erf_header
 DESCRIPTION :   Parse ERF header and fill appropriate fields in bfs_tests_field.
 PARAMETERS :    erf_header - start of the ERF packet.
                rem_len : contains the remaining length and updated the new length after parsing.
                in_flds: bfs_tests_field
  RETURNS :       -1 if failed ( insufficient len) 
                  1 if success
---------------------------------------------------------------------*/
static inline int8_t bfs_tests_parse_erf_header(char *erf_header, int *rem_len, bfs_tests_fields_t *in_flds)
{
    dag_record_t* drec = (dag_record_t *) erf_header;
    if ( *rem_len < 20 )
    {
        return -1;
    }
    in_flds->lctr = ntohs(drec->lctr);
    in_flds->ifc = drec->flags.iface;
    in_flds->rectype = drec->type;
    in_flds->rlen = ntohs(drec->rlen);
    in_flds->wlen = ntohs(drec->wlen);
    *rem_len -= dag_record_size;
    return 1;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_parse_l2_headers
 DESCRIPTION :   Parse layer 2 and MPLS and VLAN headers (if any) and fill appropriate fields in bfs_tests_field.
 PARAMETERS :    in_rec -ERF packet.(Hacked one - ext header offseted so can't access ERF header flds there) 
                rem_len : contains the remaining length and updated the new length after parsing.
                in_flds: bfs_tests_field
  RETURNS :       NULL if failed ( insufficient len or non-ip) 
                  address of IP header
---------------------------------------------------------------------*/
static inline uint8_t* bfs_tests_parse_l2_headers(uint8_t *in_rec, int *rem_len, bfs_tests_fields_t *in_flds)
{
    dag_record_t* drec = (dag_record_t *) in_rec;
    switch ( in_flds->rectype & 0x7f)
    {
        case ERF_TYPE_HDLC_POS:
        case ERF_TYPE_COLOR_HDLC_POS:
        case ERF_TYPE_COLOR_HASH_POS:
        case ERF_TYPE_DSM_COLOR_HDLC_POS:
        {
            unsigned int hdlc = ntohl(drec->rec.pos.hdlc);
            uint16_t    temp_eth_type = hdlc & 0xffff;
            /* We can get the l2 proto here. So verify it first, before going furthur */
            if(*rem_len < 4 )
            {
                *rem_len -= 4; /* to let the caller know ran out of bytes*/
                return NULL;
            }
            in_flds->etype = temp_eth_type;
            *rem_len -= 4;
            /* Check for MPLS */
            if ( hdlc == 0xFF030281 || hdlc == 0xFF030283 || hdlc == 0x0F008847 || hdlc == 0x0F008848 )
            {
                uint32_t* overlay32 = (uint32_t*) drec->rec.pos.pload;
                uint32_t  mpls_value = 0;
                uint32_t  mpls_index = 0;
                uint32_t  temp_mpls_top = 0;
                
                /* Jump over MPLS Shim headers */
                if ( *rem_len > 4 )
                {
                    do 
                    {
                        mpls_value = ntohl (overlay32[mpls_index]);
                        if ( mpls_index == 0 ) temp_mpls_top = mpls_value;
                        mpls_index++;
                        *rem_len -= 4;
    
                    } while ( ((mpls_value & 0x00000100) == 0) && (*rem_len > 4) );
                }
                /* assign  mpls top and bottom */
                in_flds->mpls_top = temp_mpls_top;
                if  (mpls_index > 1) 
                {
                   in_flds->mpls_btm = mpls_value;
                }
                /* asgin label cnt field  - for mpls max is 7*/
                if ( mpls_index > 7) mpls_index = 7;
                in_flds->label_count =  mpls_index;
                return (uint8_t*) &overlay32[mpls_index];
            }
            else if ( hdlc == 0xFF030021 || hdlc == 0xFF030057 || hdlc == 0x0F000800 || hdlc == 0x0F0086DD )
            {
                    return (uint8_t*) drec->rec.pos.pload;

            }
        }
        case ERF_TYPE_ETH:
        case ERF_TYPE_COLOR_ETH:
        case ERF_TYPE_COLOR_HASH_ETH:
        case ERF_TYPE_DSM_COLOR_ETH:
        {
            uint16_t ethertype = ntohs(drec->rec.eth.etype);
            
            if(*rem_len < 16 )
            {
                *rem_len -= 16; /* to let the caller know ran out of bytes*/
                return NULL;
            }
            in_flds->etype = ethertype;
            *rem_len -= 16; /* size of drec->rec.eth */
            if ((ethertype == 0x8100) || (ethertype == 0x88a8) )
            {
                /* VLAN ethernet encapsulations here */
                uint16_t* overlay16;
                int length_type ;
                uint8_t*    temp_pload_ptr = (uint8_t*) drec->rec.eth.pload;
                do{ 
                    overlay16 = (uint16_t*) temp_pload_ptr;
                    length_type = ntohs(overlay16[1]);
                    if( in_flds->label_count == 0 ) in_flds->vlan_1 = ntohs(overlay16[0]) & 0xfff;
                    if ( in_flds->label_count == 1 ) in_flds->vlan_2 = ntohs(overlay16[0]) & 0xfff;
                     in_flds->label_count++;
                    *rem_len -= 4;
                    /* the l2 proto is the last e-type in vlan tags */
                    in_flds->etype = (uint16_t) length_type;
                    /* check if its ipv4 or ipv6 */
                    if((0x0800 == length_type) || (0x86dd == length_type))
                    {
                        /* Found IP header. */
                        return  (uint8_t*) &overlay16[2];
                    }
                    temp_pload_ptr = (uint8_t*) &overlay16[2];
                }while ( ((length_type == 0x8100) || (length_type == 0x88a8)) && ( *rem_len > 4) );

            }
            else if ((ethertype == 0x8848) || (ethertype == 0x8847) )
            {
                /* MPLS ethernet encapsulations here */
                uint32_t* overlay32 = (uint32_t*) drec->rec.eth.pload;
                uint32_t  mpls_value = 0;
                uint32_t  mpls_index = 0;
                uint32_t  temp_mpls_top = 0;
                
                /* Jump over MPLS Shim headers */
                if ( *rem_len > 4 )
                {
                    do 
                    {
                        mpls_value = ntohl (overlay32[mpls_index]);
                        if ( mpls_index == 0 ) temp_mpls_top = mpls_value;
                        mpls_index++;
                        *rem_len -= 4;
    
                    } while ( ((mpls_value & 0x00000100) == 0) && (*rem_len > 4) );
                }
                /* assign  mpls top and bottom */
                in_flds->mpls_btm = mpls_value;
                if  (mpls_index > 1) 
                {
                   in_flds->mpls_top = temp_mpls_top;
                }
                /* assgin label cnt field  - for mpls max is 7*/
                if ( mpls_index > 7) mpls_index = 7;
                in_flds->label_count =  mpls_index;
                return (uint8_t*) &overlay32[mpls_index];
            }
            else if ((ethertype == 0x0800) || (ethertype == 0x86dd))
            {
                return  (uint8_t*) drec->rec.eth.pload;
            }
        }
        default:
            /* do nohthing */
            break;
    }
    return NULL;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_parse_l3_l4_headers
 DESCRIPTION :   Parse layer 3 and 4 and fill appropriate fields in bfs_tests_field.
 PARAMETERS :    ip_header - start IP header.
                rem_len : contains the remaining length and updated the new length after parsing.
                in_flds: bfs_tests_field
  RETURNS :       -1 if failed ( insufficient len or not ip v4 or v6) 
                  1 if success
---------------------------------------------------------------------*/
static inline int8_t  bfs_tests_parse_l3_l4_headers(uint8_t *ip_header, int *rem_len, bfs_tests_fields_t *flds)
{
    uint8_t *pload = ip_header;
    uint8_t  ihl;
    int tcp_hdr_bytes = 0;

    flds->ip_version = ((pload[0] & 0xf0) >> 4);
    
    if (4 == flds->ip_version) //(etype == 0x0800) || (etype == 0x0021)) /* IP v4 */
    {
        if (*rem_len < 20)
                return -1;

        ihl = ((uint8_t) pload[0]) & 0x0f;
        flds->ip_prot = pload[9];
        flds->src_ipaddr[0] = IP_ADDR_MAKE(pload[12], pload[13], pload[14], pload[15]);
        flds->dst_ipaddr[0] = IP_ADDR_MAKE(pload[16], pload[17], pload[18], pload[19]);
        
        *rem_len -= 4*ihl;
        pload = pload + 4*ihl;
    }
    else if (6 == flds->ip_version)
    {
        int offset = 0;
        uint8_t next_ext_hdr;
        int finished = 0;
        if (*rem_len < 40)
                return -1;
        flds->src_ipaddr[1] = bswap_64(*( (uint64_t*) &ip_header[8]) );
        flds->src_ipaddr[0] = bswap_64(*( (uint64_t*) &ip_header[16]) );
        flds->dst_ipaddr[1] = bswap_64(*( (uint64_t*) &ip_header[24]) );
        flds->dst_ipaddr[0] = bswap_64(*( (uint64_t*) &ip_header[32]) );

        next_ext_hdr = pload[6];
        offset += 40;
        while (!finished)
        {
            if ( *rem_len < (offset + 8))
            {
                /* no more bytes.So as firmware does, take the next hdr frm IPV6 header*/
                next_ext_hdr = pload[6];/*pload still points to the start of IPV6 */
                break;
            }
            switch (next_ext_hdr)
            {
                case HOPOPT_EXT_HEADER:
                    next_ext_hdr = pload[offset];
                    offset += (pload[offset + 1] * 8) + 8;
                    break;
                    
                case ROUTE_EXT_HEADER:
                    next_ext_hdr = pload[offset];
                    offset += (pload[offset + 1] * 8) + 8;
                    break;
                    
                case OPTS_EXT_HEADER:
                    next_ext_hdr = pload[offset];
                    offset += (pload[offset + 1] * 8) + 8;
                    break;
                    
                case FRAG_EXT_HEADER:
                    next_ext_hdr = pload[offset];
                    offset += 8;
                    break;
                    
                case AUTH_EXT_HEADER:
                    next_ext_hdr = pload[offset];
                    offset += (pload[offset + 1] * 4) + 8;
                    break;
                
                case ESP_EXT_HEADER:
                    finished = 1;
                    break;

                default:
                    finished = 1;
            }
        }
        flds->ip_prot = next_ext_hdr;

        pload = pload + offset;
        *rem_len -= offset ;
    }
    else 
    {
        /* TODO ignore ?not v4 and v6 ?? */
        return -1;
    }
    /* now layer 4 */
    switch(flds->ip_prot) {

#ifdef BFS_TEST_ICMP
    case PROT_ICMP:
	    *rem_len -= (ICMP_HDR_LEN);
	    if (*rem_len < 0)
	    {
		    return -1;
	    }
	    flds->icmp_type = pload[0];
	    flds->icmp_code = pload[1];
	    break;
#endif
    case PROT_TCP:
        /*min of 14 bytes to extract the protocal fileds */
        if (*rem_len < 14)
        {
            rem_len -= 14;
            return -1;
        }
        /*incase malformed (tcplen=0), take 20 bytes atleast as protocol and rest as payload */
        tcp_hdr_bytes =  dagutil_max(TCP_HDR_MIN_LEN, (4 * ((pload[12] & 0xf0) >> 4)));
        *rem_len -= (tcp_hdr_bytes);
        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];
        break;
    case PROT_UDP:
        *rem_len -= (UDP_HDR_LEN);
        if (*rem_len < 0)
            return -1;
        flds->src_port = (uint16_t) (pload[0] << 8 | pload[1]);
        flds->dst_port = (uint16_t) (pload[2] << 8 | pload[3]);
        break;
        }

    return 1;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_validate_input_parameters
 DESCRIPTION :   validate the parameters before starting the test
  RETURNS :       TEST_FAIL or TEST_PASS
---------------------------------------------------------------------*/
int bfs_tests_validate_input_parameters(void)
{
    if (g_do_bfs_hash_tests && ( (g_n_tuple < 2)  || (g_n_tuple > 6 )) )
    {
        dagutil_error("Invalid Tuple %d\n",g_n_tuple);
        return TEST_FAIL;
    }
    if ( g_do_bfs_color_test )
    {
        if ( g_sram_color_default <  0 )
        {
            dagutil_error("Invalid sram miss value %d\n",g_sram_color_default);
            return TEST_FAIL;
        }
        /*check if the miss value is already a rule in the input file*/
        if ( (g_sram_color_default < BFS_TEST_MAX_RULES)  && (g_color_filter_flags[g_sram_color_default]& kBfsFilterValidColor))
        {
            dagutil_error("sram miss value(%d) is already a color in the rule file \n",g_sram_color_default);
            return TEST_FAIL;
        }
    }
    if ( !g_do_bfs_color_test && !g_do_bfs_hash_tests )
    {
        dagutil_warning("Color test and Hash tests are disabled\n");
    }
    return TEST_PASS;
}

/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_init
 DESCRIPTION :   Plugin in init funciton as required by plugin_data
 PARAMETERS :    
  RETURNS :
---------------------------------------------------------------------*/
int bfs_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;

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

    /* Set up the command line options. */
    clarg = dagclarg_init(param_cnt, (const char* const *) params);
    
    /* General options. */
    dagclarg_add(clarg, "This page.", "--help", 0, CLA_HELP);
    dagclarg_add_int(clarg, "Expected hash bin value(Optional- if not given,it is read from the BFS ext header)", "--hash", 'h', "n", &g_input_hash_value, CLA_HASH_VALUE);
    dagclarg_add_int(clarg, "n_tuple_select used for hashing(Optional- if not given,it is read from the card)", "--tuple", 'n', "n", &g_n_tuple, CLA_N_TUPLE);
    dagclarg_add_string(clarg, "Hash distribution string(Optional- if not given,it is read from the card)", "--hash-string", 'l', "string", g_input_hash_string, 4096, CLA_HASH_STRING);
    dagclarg_add_string(clarg, "Input file with filter rules", "--infile", 'i', "filename", g_filter_filename, 4096, CLA_FILTER_FILENAME);
    dagclarg_add_int(clarg, "Expected Color (Optional- if not given,it is read from the BFS ext header)", "--color", 'c', "n", &g_input_test_color, CLA_INPUT_TEST_COLOR);
    dagclarg_add_int(clarg, "sram miss value used for filtering(Optional- if not given,it is read from the card)", "--miss", 'm', "n", &g_sram_color_default, CLA_DEFAULT_COLOR);
    dagclarg_add(clarg, "Disable all hash tests.", "--no-hash", 0, CLA_NO_HASH_TESTS);
    dagclarg_add_int(clarg, "CRC bytes to be skipped from the payload hash calculation(default is 4)", "--crc", 'C', "n", &g_fcs_len, CLA_CRCLEN);
    /* Parse the command line options. */
    clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
    while (1 == clarg_result)
    {
        switch (code)
        {
        case CLA_HELP:
            dagclarg_display_usage(clarg, stderr);
            return TEST_HELP;
        case CLA_HASH_VALUE:
        case CLA_N_TUPLE:
        case CLA_DEFAULT_COLOR:
            /* Do nothing */
            break;
        case CLA_INPUT_TEST_COLOR:
            if((g_input_test_color<BFS_TEST_MAX_RULES) && (g_input_test_color >= 0))
            {
                g_color_filter_flags[g_input_test_color] |= kBfsFilterExpectedColor;
            }
            else
            {
                dagutil_warning("Ignoring input color: %d\n",g_input_test_color);
            }
            break;
        case CLA_HASH_STRING:
        case CLA_FILTER_FILENAME:
        case CLA_CRCLEN:
            break;
        case CLA_NO_HASH_TESTS:
            g_do_bfs_hash_tests = 0;
            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 the crc table*/
    dagcrc_make_aal5_table();
    /* initialize the hash table */
    memset(g_hat_table, -1, sizeof(g_hat_table));

    if (strlen(g_input_hash_string) > 0 )
    {
        if ( 0 >= bfs_tests_convert_input_string())
        {
            dagutil_error("while processing hash input range: %s\n",g_input_hash_string);
            return TEST_FAIL;
        }
    }
 
    if( strlen(g_filter_filename) > 0 )
    {
        if ( TEST_FAIL == bfs_tests_filter_parse_rules( g_filter_filename) )
        {
            dagutil_error("while parsing  filter input file : %s\n",g_filter_filename);
            return TEST_FAIL;
        }
        g_do_bfs_color_test = 1;
    }
    if (g_do_bfs_hash_tests &&  ((g_input_hash_string[0] == '\0') || (g_n_tuple <= 0 )) )
    {
        if ( 0 >= bfs_tests_load_hat_details_from_card())
        {
            dagutil_error("while loading hash table from: %s\n",bfs_tests_settings.dagname);
            return TEST_FAIL;
        }
    }
    if( g_do_bfs_color_test && ( g_sram_color_default < 0))
    {
        if ( 0 >= bfs_tests_load_color_details_from_card())
        {
            dagutil_error("while getting color miss value from: %s\n",bfs_tests_settings.dagname);
            return TEST_FAIL;
        }

    }
    /* validate the parameters */
    return bfs_tests_validate_input_parameters();
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_run
 DESCRIPTION :   This is the main test function as required by plugin_data
                This function basically calls the correct function for testing and increments the correct counters depending on the test result.
 PARAMETERS : 
  RETURNS :
---------------------------------------------------------------------*/
int bfs_tests_run(char *rec, int len, char* lastpkt, struct_protocol_t prot, uint64_t rec_cnt)
{

    bfs_tests_fields_t flds;
    int ret_val;
    rec_msg_len = 0;

    /* initialize all fileds to 0 */
    memset(&flds, 0, sizeof(bfs_tests_fields_t));
    /* Parse the record and verify that we have a correct record. */
    if ((ret_val = bfs_tests_parse_rec(rec, len, &flds)) == TEST_FAIL)
    {
        bfs_tests_incr_counters(ret_val);
        return ret_val;
    }
#ifdef BFS_TESTS_DEBUG
    bfs_test_debug_print_fields(&flds);
#endif
#if 0
    if (g_input_test_color != -1 )
    {
        if(!flds.bfs_ext_found)
        {
            /* we are expecting the input colour .*/
            flds.colour = (uint16_t)g_input_test_color;
        }
    }
    else if( 0 == flds.bfs_ext_found)
    {
        /* no ext hdr and no input color. dont do colortest */
        g_do_bfs_color_test = 0;
    }
#endif
    ret_val = bfs_tests_do_verify(&flds, rec_cnt);
    bfs_tests_incr_counters(ret_val);
    return ret_val;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_err_msg
 DESCRIPTION :   Plugin in error  funciton as required by plugin_data
 PARAMETERS :    
  RETURNS :
---------------------------------------------------------------------*/
int bfs_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;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_final_msg
 DESCRIPTION :   Plugin in final  funciton as required by plugin_data
 PARAMETERS :    
  RETURNS :
---------------------------------------------------------------------*/
int bfs_tests_final_msg(char *buf, int size)
{
    char msg[MAX_STR_LEN];

    snprintf(msg, MAX_STR_LEN, 
         "bfs_tests: pass %"PRIu64"\nbfs_tests: fail %"PRIu64"\nbfs_tests: warning %"PRIu64"\nbfs_tests: ignore %"PRIu64"\nbfs_tests: flowhash-pass %"PRIu64"\nbfs_tests: ploadhash-pass %"PRIu64"\nbfs_tests: color-pass %"PRIu64"\nbfs_tests: color-missed %"PRIu64"\n",
          passed, failed, warning, ignored,flow_hash_test_passed,pload_hash_test_passed, color_test_passed, color_test_sram_missed);

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

    if (failed > 0)
        return TEST_FAIL;
    else
        return TEST_PASS;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_cleanup
 DESCRIPTION :   Plugin in cleanup  funciton as required by plugin_data
---------------------------------------------------------------------*/
int bfs_tests_cleanup()
{
    return TEST_PASS;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_printf
 DESCRIPTION :   Plugin in printf  funciton as required by plugin_data
---------------------------------------------------------------------*/
int bfs_tests_printf (char *format, ...)
{

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

    return TEST_PASS;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_do_verify
 DESCRIPTION :   Verify the parsed fields against hash test and color test.
 PARAMETERS :    flds:  parsed bfs_tests_field
                 rec_cnt: record counter
  RETURNS :       -TEST_PASS if success 
---------------------------------------------------------------------*/
int bfs_tests_do_verify(bfs_tests_fields_t *flds, uint64_t rec_cnt)
{
    int ret_val = TEST_PASS;
    int len_incr;
    int ret_here = TEST_PASS;


    /* if nothing to do return a warning */
    if ( !g_do_bfs_hash_tests && !g_do_bfs_color_test)
    {
         len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Hash test disabled Color test disabled");
         rec_msg_len += len_incr;
         return TEST_WARNING;
    }
    if(g_do_bfs_hash_tests)
    {
        ret_here = TEST_PASS;
        if((ret_here = bfs_test_verify_hashes(flds, rec_cnt))!= TEST_PASS)
        {
            ret_val = ret_here;
        }
    }
    if (g_do_bfs_color_test )
    {
        if(0 == flds->bfs_ext_found)
        {
            /*Have to iterate through the list and find whether the packet matches any rule and expected */
            ret_here = TEST_PASS;
            if((ret_here = bfs_tests_check_any_color_match(flds)) != TEST_PASS)
            {
                ret_val = ret_here;
            }
            else
            {
                color_test_passed++;
            }
        }
        else 
        {
            if ( flds->colour == g_sram_color_default)
            {
                color_test_sram_missed++; 
                color_test_passed++;
            }
            else if ( flds->colour >= BFS_TEST_MAX_RULES )
            {
                len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Invalid Color %d. Value greater than the maximum value supported by the test", flds->colour);
                rec_msg_len += len_incr;
                ret_val = TEST_FAIL;
            }
            else if (0 == (g_color_filter_flags[flds->colour]&kBfsFilterValidColor))
            {
                len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Invalid Color %d.(not in the filter rule file)", flds->colour);
                rec_msg_len += len_incr;
                ret_val = TEST_FAIL;
            }
            else if ((g_input_test_color != -1) && (0 == (g_color_filter_flags[flds->colour]&kBfsFilterExpectedColor)))
            {
                /* given value and extn header value don't match.Fail the test */
                len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Color test failed(Unexpected color %d from Extn header).", flds->colour);
                rec_msg_len += len_incr;
                ret_val = TEST_FAIL;
            }
            else if (TEST_FAIL == bfs_tests_validate_match(flds, &(g_bfs_filter_test_rules[flds->colour]), 1/*fill error*/) )
            {
                /*error  message wud be filled inside */
                ret_val = TEST_FAIL;
            }
            else
            {
                color_test_passed++;
            }
        }
    }
    return ret_val;
}
void bfs_tests_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;
    }
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_parse_rec
 DESCRIPTION :   Parse the incoming ERF packet. Calls different functions to parse layer by layer fields.
 PARAMETERS :    rec:  incoming ERF packet
                 in_len: record length
                flds : bfs_tests_fields to be filled
  RETURNS :       -TEST_PASS if success 
---------------------------------------------------------------------*/
int  bfs_tests_parse_rec(char *rec, int in_len, bfs_tests_fields_t *flds)
{
    dag_record_t* drec = (dag_record_t*) rec;
    int len = in_len;
    uint32_t ext_headers_count =0;
    uint8_t *ip_hdr_ptr = NULL;
    int len_incr = 0;
    uint16_t available_erf_pload_len  = 0; 

    
    /* We don't handle legacy records, so we ignore them and report that... */
    if (dagerf_is_legacy_type((uint8_t*) rec))
    {
        len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,
                    "Warning: Legacy type\n");
        rec_msg_len += len_incr;
        return TEST_WARNING;
    }

    if (0 > bfs_tests_parse_erf_header(rec , &len, flds))
    {
        len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,
                    "Warning: Record length not sufficient for ERF header\n");
        rec_msg_len += len_incr;
        return TEST_WARNING;
    }
    if ( (ext_headers_count = bfs_tests_parse_ext_header(rec,  &len, flds)) < 0 )
    {

        len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,
                    "Warning: Record length not sufficient for ext headers\n");
        rec_msg_len += len_incr;
        return TEST_WARNING;
    }
    if((flds->rectype & 0x7f) == ERF_TYPE_ETH )
    {
       available_erf_pload_len = flds->wlen+2; 
    }
    else
    {
        available_erf_pload_len = flds->wlen;
    }

    /*if the rem len is less than wlen */
    if(len<available_erf_pload_len)
    {
        available_erf_pload_len = len; 
    }
    else
    {
        len = available_erf_pload_len;
    }
    drec =  (dag_record_t*) ((uintptr_t)drec + (ext_headers_count * 8)) ;
    
    if ((ip_hdr_ptr = bfs_tests_parse_l2_headers((uint8_t*)drec , &len, flds)) == NULL)
    {
        if(len < 0)
        {
            len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,
                        "Warning: Record length not sufficient for layer2 headers\n");
            rec_msg_len += len_incr;
            /* not enough bytes to locate pload_ptr */
            return TEST_WARNING;
        }
        else
        {
            /*unknown l2 type and failed to locate ip hdr. */
            flds->pload_ptr = (uint8_t*)drec+dag_record_size+(available_erf_pload_len - len);
            flds->pload_len = len ;
            return TEST_PASS;
        }
    }
    
    if ( 0 > bfs_tests_parse_l3_l4_headers(ip_hdr_ptr, &len, flds))
    {
        if( len < 0 )
        {
            len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,
                    "Warning: Record length not sufficient for layer3 and 4 headers\n");
            rec_msg_len += len_incr;
            /* not enough bytes to locate pload_ptr */
            return TEST_WARNING;
        }
        else
        {
            /* some un-identified protocol. assign the pload proto frm there */
        }
    }
    flds->pload_ptr = (uint8_t*) drec +dag_record_size+(available_erf_pload_len-len);
    flds->pload_len = len;
    return TEST_PASS;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_convert_range_to_table
 DESCRIPTION :   Used to parse hash string
 ---------------------------------------------------------------------*/
void bfs_tests_convert_range_to_table(hat_range_t *in_range)
{
    uint32_t idx, range, start, end;
    for(range=0; range<in_range->bin_num; range++)
    {
        start = bfs_tests_hlb_range_ui2reg(in_range->bin_range[range].min);
        end = bfs_tests_hlb_range_ui2reg(in_range->bin_range[range].max);
        for (idx = start; idx < end; idx++)
        {
            g_hat_table[idx] = (range );
        }
    }
    return;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_load_hat_details_from_card
 DESCRIPTION :   load the hat table , tuple value etc  from the card
 RETURNS :      -1 if failed
 ---------------------------------------------------------------------*/
int8_t bfs_tests_load_hat_details_from_card(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;


    if (NULL == (dag_ref = dag_config_init(bfs_tests_settings.dagname)))
    {
        dagutil_error("Cannot find a the card %s. This test cannot run when checking a file.\n", bfs_tests_settings.dagname);
        return -1;
    }
    if (!(root = dag_config_get_root_component(dag_ref)))
    {
        dagutil_error("Cannot get root component.\n");
        return -1;
    }
    if (!(hat = dag_component_get_subcomponent(root, kComponentHAT, 0)))
    {
        dagutil_error("Cannot find a HAT component.\n");
        return -1;
    }
    if ( (g_input_hash_string[0] == '\0'))
    {
        if (!(attr = dag_component_get_attribute_uuid(hat, kStructAttributeHATRange)))
        {
            dagutil_error("Cannot get HAT Range attribute\n");
            return -1;
        }
        if (kDagErrNone != dag_config_get_struct_attribute(dag_ref, attr, (void *) &hlb_range))
        {
            dagutil_error("Cannot read HLB Table from cartd\n");
            return -1;
        }
        /* We don'tneed to check for invalid ranges because we don't
        expect the API to return them */
        bfs_tests_convert_range_to_table(hlb_range);
    }
    /* get the n_tuple_value , if its not set already */
    if ( g_n_tuple <= 0 )
    {
        if (!(attr = dag_component_get_attribute_uuid(hat, kUint32AttributeNtupleSelect)))
        {
            dagutil_error("Cannot get HAT tuple attribute\n");
            return -1;
        }
        g_n_tuple = (int) dag_config_get_uint32_attribute(dag_ref, attr);
        dagutil_verbose("tuple select read from the card:%d\n", g_n_tuple);
    }

    /* Here we should dispose of the card reference... */
    dag_config_dispose(dag_ref);
    return 1;
}

/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_load_color_details_from_card
 DESCRIPTION :   load  sram-miss-value etc  from the card
 RETURNS :      -1 if failed
 ---------------------------------------------------------------------*/
int8_t bfs_tests_load_color_details_from_card(void)
{
    dag_component_t root;
    dag_card_ref_t dag_ref = NULL;
    dag_component_t ipf = NULL;
    attr_uuid_t attr = 0;


    if (NULL == (dag_ref = dag_config_init(bfs_tests_settings.dagname)))
    {
        dagutil_error("Cannot find a the card %s. This test cannot run when checking a file.\n", bfs_tests_settings.dagname);
        return -1;
    }
    if (!(root = dag_config_get_root_component(dag_ref)))
    {
        dagutil_error("Cannot get root component.\n");
        return -1;
    }
    if (!(ipf = dag_component_get_subcomponent(root, kComponentIPF, 0)))
    {
        dagutil_error("Cannot find a BFS component.\n");
        return -1;
    }
    if (!(attr = dag_component_get_attribute_uuid(ipf, kUint32AttributeSRamMissValue)))
    {
        dagutil_error("Cannot get sram miss  attribute\n");
        return -1;
    }
    g_sram_color_default = (int) dag_config_get_uint32_attribute(dag_ref, attr);
    dagutil_verbose("SRAM miss value from the card:%d\n", g_sram_color_default);

    /* Here we should dispose of the card reference... */
    dag_config_dispose(dag_ref);
    return 1;
}

/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_convert_input_string
 DESCRIPTION :   Used to parse hash string
 ---------------------------------------------------------------------*/
int8_t bfs_tests_convert_input_string(void)
{
      /*String to structure conversion*/
    char *subtoken,*token ;
    char *saveptr = NULL; 
    char *saveptr2 = NULL;
    hat_range_t parsed_range;
    int loop;
    char *str;
    char *array = NULL;
    int count = 0;
    
    memset( &parsed_range, 0 , sizeof(hat_range_t));

    /* FIXME Use a temp array. so as to preseve the 'const'-ness of parameter*/
    array = (char*) malloc ( strlen (g_input_hash_string) + 1);
    strcpy( array, g_input_hash_string );
    for(loop = 1,str = array;;loop++,str = NULL)
    {
        token = strtok_r(str, ":", &saveptr);
        if (token == NULL)
        break;
        subtoken = strtok_r(token,"-",&saveptr2);
        if(subtoken == NULL)
        {
            if (array)
                free (array);
            return -1;
        }
        parsed_range.bin_range[loop -1].min = atoi(subtoken);
        parsed_range.bin_range[loop -1].max = atoi(saveptr2);
        count++;
    }
    if (array)
        free (array);
    parsed_range.bin_num = count;
    if ( 0 >= bfs_tests_check_overlapping_ranges( &parsed_range))
    {
        return -1;
    }
    bfs_tests_convert_range_to_table(&parsed_range);
    return 1;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_check_overlapping_ranges
 DESCRIPTION :   Used to parse hash string
 ---------------------------------------------------------------------*/
int8_t bfs_tests_check_overlapping_ranges(hat_range_t *hat)
{
    uint32_t i,j;
    for(i = 0;i < hat->bin_num;i++)
    {
        for(j = 0;j < hat->bin_num;j++)
        {
            if((hat->bin_range[j].min <= 1000 && hat->bin_range[j].max <= 1000))
            {
                if((i != j))
                {
                    if(hat->bin_range[i].min <= hat->bin_range[j].min)
                    {
                        if((hat->bin_range[i].max > hat->bin_range[j].min))
                        {
                            if(((hat->bin_range[i].min == 0)&& (hat->bin_range[j].max ==0))||
                            ((hat->bin_range[j].min == 0)&& (hat->bin_range[j].max ==0)))
                            {	
                                continue;
                            }
                            else
                            {
                                dagutil_error("overlapping indices %d: %d \n", i,j);
                                return -1;
                            }
                        }
                    }
                }
            }
            else
            {
                 dagutil_error("One of the values is greater that 1000.Please check \n");
                return -1;
            }
        }
    }	
    return 1;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_hlb_range_ui2reg
 DESCRIPTION :   Used to parse hash string
 ---------------------------------------------------------------------*/
uint32_t bfs_tests_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;    
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_calc_crc
 DESCRIPTION :   Calculate CRC for hash testing
PARAMETERS :     flds: parsed fields
                tuple : n tuple for crc
  RETURNS :       32 bit CRC
---------------------------------------------------------------------*/
uint32_t bfs_tests_calc_crc(bfs_tests_fields_t *flds, int tuple)
{
    int len_incr;
    uint32_t ports;
    uint32_t val_for_crc = 0, crc_res = 0;
    int check = 1;
    int i;
    uint32_t temp_dst_ip_32 = 0, temp_src_ip_32 = 0;
    /*as per bug#7845, consider ports separately for XOR ing, instead of packing them as single 32 bit word*/
    /*ports = (uint32_t) ((flds->src_port << 16 ) | flds->dst_port);*/
    ports = (uint32_t) (flds->src_port ^ flds->dst_port);

    if (flds->ip_version == 4)
    {
        temp_src_ip_32 = (uint32_t) flds->src_ipaddr[0];
        temp_dst_ip_32 = (uint32_t) flds->dst_ipaddr[0];
    }
    else if (flds->ip_version == 6)
    {
        for (i = 0; i < 2; i ++)
        {
            temp_src_ip_32 ^= ((uint32_t)(flds->src_ipaddr[i] >> 32)) ^ ((uint32_t)flds->src_ipaddr[i]);
            temp_dst_ip_32 ^= ((uint32_t)(flds->dst_ipaddr[i] >> 32)) ^ ((uint32_t)flds->dst_ipaddr[i]);
        }
    }

    switch(tuple)
    {
    case 2: /* 2-tuple */
        val_for_crc = temp_src_ip_32 ^ temp_dst_ip_32;
        break;
    case 3: /* 3-tuple */
        val_for_crc = temp_src_ip_32 ^ temp_dst_ip_32 ^ flds->ip_prot;
        break;
    case 4: /* 4-tuple */
        val_for_crc = temp_src_ip_32 ^ temp_dst_ip_32 ^ flds->ip_prot ^ flds->ifc;
        break;
    case 5: /* 5-tuple */
        if ((flds->ip_prot != PROT_TCP) && (flds->ip_prot != PROT_UDP))
            check = 0;  /*TODO ??*/
        val_for_crc = temp_src_ip_32 ^ temp_dst_ip_32 ^ flds->ip_prot ^ ports;
        break;
    case 6: /* 6-tuple */
        if ((flds->ip_prot != PROT_TCP) && (flds->ip_prot != PROT_UDP))
            check = 0; /*TODO ??*/
        val_for_crc = temp_src_ip_32 ^ temp_dst_ip_32 ^ flds->ip_prot ^ flds->ifc ^ ports;
        break;
        
    default:
        len_incr = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,
                    "Warning: %s Invalid tuple: %d\n", 
                    __FUNCTION__, g_n_tuple);
        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;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_filter_parse_rules
 DESCRIPTION :   Parse the rule file as done by filter loader tool.For simplicity BFS_TEST_MAX_RULES rules or max color BFS_TEST_MAX_RULES are allowed
 RETURNS :       -TEST_PASS if success 
---------------------------------------------------------------------*/
int bfs_tests_filter_parse_rules(const char* filter_rule_filename)
{
    int retval = 0;
    FILE * fin;
    uint32_t filter_rules_count  = 0;

    memset(&g_bfs_filter_test_rules, 0, BFS_TEST_MAX_RULES * sizeof(bfs_filter_rule_t));

    fin = fopen(filter_rule_filename,"r");
    if( fin == NULL )
    {
        dagutil_error("File is missing or no access\n");
        return -1;
    }
    bfsrestart(fin);
    memset( &bfs_filter_rule, 0, sizeof (bfs_filter_rule));

    while(1)
    {
        retval = bfslex();
        if(retval == T_RULE_DONE)
        {
            filter_rules_count ++;
            
        if (filter_rules_count > BFS_TEST_MAX_RULES )
        {
            filter_rules_count = BFS_TEST_MAX_RULES ;
            dagutil_warning("Only %d Rules are taken\n", filter_rules_count);
            break;
        }
        }
        else if (retval == T_RULE_CONTINUE)
        {
            dagutil_error("This state is unused please contact suppot@endace.com \
 and send the rule file used and this line print out. retval: %d rules_count:%d\n",retval,filter_rules_count);
        }
        else if ( retval < 0 )
        {
            printf(" errors flex returns: %d at rule: %d\n",retval,filter_rules_count);
            break;
        }
        else if (retval == 0)
        {
            break;
            
        } else {
            dagutil_error("Unknown state please contact suppot@endace.com \
 and send the rule file used and this line print out. retval: %d rules_count:%d\n",retval,filter_rules_count);
        }
        /* check the color ( user tag) is within max allowed by the test */
        if(bfs_filter_rule.user_tag >= BFS_TEST_MAX_RULES)
        {
            printf("Error. Invalid Color(%u). Allowed color values are 0 - %u\n",bfs_filter_rule.user_tag, (BFS_TEST_MAX_RULES -1) );
            return TEST_FAIL;
        }
        memcpy( &g_bfs_filter_test_rules[bfs_filter_rule.user_tag],&bfs_filter_rule,sizeof(bfs_filter_rule) );
        g_color_filter_flags[bfs_filter_rule.user_tag] |= kBfsFilterValidColor;
        memset( &bfs_filter_rule, 0, sizeof (bfs_filter_rule));
    } 
    /* clean up */
    fclose(fin);
    if ( retval < 0 )
        return TEST_FAIL;
    /* complete */
    dagutil_verbose("Ruleset file parsed successifully, %d rules have been created.\n", filter_rules_count);
    return TEST_PASS;
}
/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_validate_match
 DESCRIPTION :   Compare a matched rule frm the rule file against the parsed packet fields
 PARAMETERS:    flds - Parsed fields
                matched_rule -  the matched rule.
                fill_error  set if the error string needs to be printed
                            0 if just compare and pass or fail
 RETURNS :       -TEST_PASS if success 
---------------------------------------------------------------------*/
int bfs_tests_validate_match(const bfs_tests_fields_t *flds,const  bfs_filter_rule_t* const matched_rule, int fill_error)
{
    int len_print = 0;

    if ( (matched_rule->iface.mask & flds->ifc) != matched_rule->iface.data)
    {
        /* intrface   is given in the rule, but doesn't match with the rule */
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u iface(0x%x & 0x%x) did not match the packet(0x%x)",matched_rule->user_tag, matched_rule->iface.data,matched_rule->iface.mask, flds->ifc );
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }
    
    /* compare l2 proto */
    if ( (matched_rule->l2_proto.mask & flds->etype) != matched_rule->l2_proto.data)
    {
        /* l2 proto is given in the rule, but doesn't match with the rule */
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u L2 proto(0x%04x & 0x%04x) did not match the packet(0x%04x).",matched_rule->user_tag, matched_rule->l2_proto.data, matched_rule->l2_proto.mask, flds->etype);
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }
    /* compare MPLS */
    if (  (matched_rule->mpls_top.mask & flds->mpls_top) != matched_rule->mpls_top.data) 
    {
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u MPLS TOP(0x%08x & 0x%08x) did not match the packet(0x%08x).",matched_rule->user_tag, matched_rule->mpls_top.data, matched_rule->mpls_top.mask, flds->mpls_top);
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    } 
    if ( (matched_rule->mpls_bottom.mask & flds->mpls_btm ) != matched_rule->mpls_bottom.data )
    {
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u MPLS BTM(0x%08x & 0x%08x) did not match the packet(0x%08x).", matched_rule->user_tag, matched_rule->mpls_bottom.data, matched_rule->mpls_bottom.mask, flds->mpls_btm);
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }
    if ( (matched_rule->label_cnt.mask & flds->label_count) != matched_rule->label_cnt.data) 
    {
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u MPLS count(0x%x & 0x%x) did not match the packet(0x%x).",matched_rule->user_tag, matched_rule->label_cnt.data,matched_rule->label_cnt.mask, flds->label_count );
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }

    /* compare VLAN */
    if (  (matched_rule->vlan_1.mask & flds->vlan_1) != matched_rule->vlan_1.data) 
    {
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u VLAN 1(0x%04x & 0x%04x) did not match the packet(0x%04x).",matched_rule->user_tag,matched_rule->vlan_1.data, matched_rule->vlan_1.mask, flds->vlan_1);
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    } 
    if ( (matched_rule->vlan_2.mask &  flds->vlan_2) != matched_rule->vlan_2.data)
    {
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u VLAN 2(0x%04x & 0x%x) did not match the packet(0x%04x).",matched_rule->user_tag, matched_rule->vlan_2.data,matched_rule->vlan_2.mask, flds->vlan_2);
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }

    if (flds->ip_version == 4)
    {
        if ( matched_rule->ip_address_type == k128Bit )
        {
            if(fill_error)
            {
                len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u Packet has ipv4, rule is ipv6 \n",matched_rule->user_tag);
                rec_msg_len += len_print;
            }
            return TEST_FAIL;
        }
        /* compare ip addresses */
        if( ((uint32_t)(matched_rule->src_ip.mask[0]) & ((uint32_t) flds->src_ipaddr[0])) != (uint32_t) matched_rule->src_ip.data[0])
        {
            if(fill_error)
            {
                len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u SRC IP(0x%08x & 0x%08x) did not match the packet(0x%08x).",matched_rule->user_tag, (uint32_t) matched_rule->src_ip.data[0], (uint32_t) matched_rule->src_ip.mask[0], (uint32_t) flds->src_ipaddr[0]);
                rec_msg_len += len_print;
            }
            return TEST_FAIL;
        }
        if( ((uint32_t)(matched_rule->dst_ip.mask[0]) & ((uint32_t) flds->dst_ipaddr[0])) != (uint32_t)matched_rule->dst_ip.data[0])
        {
            if(fill_error)
            {
                len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u DST IP(0x%08x & 0x%08x) did not match the packet(0x%08x).",matched_rule->user_tag, (uint32_t)matched_rule->dst_ip.data[0], (uint32_t)matched_rule->dst_ip.mask[0], (uint32_t) flds->dst_ipaddr[0]);
                rec_msg_len += len_print;
            }
            return TEST_FAIL;
        }
    }
    if (flds->ip_version == 6)
    {
        if ( matched_rule->ip_address_type == k32Bit )
        {
            if(fill_error)
            {
                len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u Packet has ipv6, rule is ipv4 \n",matched_rule->user_tag);
                rec_msg_len += len_print;
            }
            return TEST_FAIL;
        }
        /* compare ip addresses */
        if( ( matched_rule->src_ip.mask[0] & flds->src_ipaddr[1])!= matched_rule->src_ip.data[0])
        {
            if(fill_error)
            {
                len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u SRC IP(0x%016"PRIx64" %016"PRIx64" & 0x%016"PRIx64" %016"PRIx64") did not match the packet(0x%016"PRIx64" %016"PRIx64").",matched_rule->user_tag, matched_rule->src_ip.data[0], matched_rule->src_ip.data[1], matched_rule->src_ip.mask[0], matched_rule->src_ip.mask[1], flds->src_ipaddr[1], flds->src_ipaddr[0] );
                rec_msg_len += len_print;
            }
            return TEST_FAIL;
        }
        if( (matched_rule->src_ip.mask[1] & flds->src_ipaddr[0]) != matched_rule->src_ip.data[1])
        {
            if(fill_error)
            {
                len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u SRC IP(0x%016"PRIx64" %016"PRIx64" & 0x%016"PRIx64" %016"PRIx64") did not match the packet(0x%016"PRIx64" %016"PRIx64").",matched_rule->user_tag, matched_rule->src_ip.data[0], matched_rule->src_ip.data[1], matched_rule->src_ip.mask[0], matched_rule->src_ip.mask[1], flds->src_ipaddr[1], flds->src_ipaddr[0] );
                rec_msg_len += len_print;
            }
            return TEST_FAIL;
        }
        if( (matched_rule->dst_ip.mask[0] & flds->dst_ipaddr[1]) != matched_rule->dst_ip.data[0]) 
        {
            if(fill_error)
            {
                len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u DST IP(0x%016"PRIx64" %016"PRIx64" & 0x%016"PRIx64" %016"PRIx64") did not match the packet(0x%016"PRIx64" %016"PRIx64").",matched_rule->user_tag, matched_rule->src_ip.data[0], matched_rule->src_ip.data[1], matched_rule->src_ip.mask[0], matched_rule->src_ip.mask[1], flds->src_ipaddr[1], flds->src_ipaddr[0] );
                rec_msg_len += len_print;
            }
            return TEST_FAIL;
        }
        if( (matched_rule->dst_ip.mask[1]& flds->dst_ipaddr[0])  != matched_rule->dst_ip.data[1]) 
        {
            if(fill_error)
            {
                len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u DST IP(0x%016"PRIx64" %016"PRIx64" & 0x%016"PRIx64" %016"PRIx64") did not match the packet(0x%016"PRIx64" %016"PRIx64").",matched_rule->user_tag, matched_rule->src_ip.data[0], matched_rule->src_ip.data[1], matched_rule->src_ip.mask[0], matched_rule->src_ip.mask[1], flds->src_ipaddr[1], flds->src_ipaddr[0] );
                rec_msg_len += len_print;
            }
            return TEST_FAIL;
        }
    }
    if ( (matched_rule->ip_prot.mask & flds->ip_prot) != matched_rule->ip_prot.data)
    {
        /* ip prot is given in the rule, but doesn't match with the rule */
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u IP prot(0x%02x & 0x%02x) did not match the packet(0x%02x).",matched_rule->user_tag, matched_rule->ip_prot.data, matched_rule->ip_prot.mask, flds->ip_prot);
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }

    /* compare Layer 4 fields */
    if ( (matched_rule->src_port.mask & flds->src_port) != matched_rule->src_port.data)
    {
        /* src port  is given in the rule, but doesn't match with the rule */
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u SRC port(0x%04x & 0x%04x) did not match the packet(0x%04x).",matched_rule->user_tag, matched_rule->src_port.data, matched_rule->src_port.mask, flds->src_port );
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }
    if ( (matched_rule->dst_port.mask & flds->dst_port) != matched_rule->dst_port.data)
    {
        /* dst port is given in the rule, but doesn't match with the rule */
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u DST port(0x%04x & 0x%04x) did not match the packet(0x%04x).",matched_rule->user_tag, matched_rule->dst_port.data, matched_rule->dst_port.mask, flds->dst_port);
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }
    /* TODO additional checks cud be made to chek the proto is TCP for the below chek. but */
    /*    ignoring for the moment */
     if ( (matched_rule->tcp_flags.mask &  flds->tcp_flags) != matched_rule->tcp_flags.data)
    {
        /* tcp flags is given in the rule, but doesn't match with the rule */
        if(fill_error)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,"Colour:%u TCP flags(0x%02x & 0x%02x) did not match the packet(0x%02x).",matched_rule->user_tag, matched_rule->tcp_flags.data, matched_rule->tcp_flags.mask, flds->tcp_flags);
            rec_msg_len += len_print;
        }
        return TEST_FAIL;
    }

    return TEST_PASS;
}

/*--------------------------------------------------------------------
  FUNCTION :      bfs_test_verify_hashes
 DESCRIPTION :   Verify the hash values
 PARAMETERS :    flds:  parsed bfs_tests_field
                 rec_cnt record count
  RETURNS :       -TEST_PASS if success 
---------------------------------------------------------------------*/
int bfs_test_verify_hashes(bfs_tests_fields_t *flds, uint64_t rec_cnt)
{
    int ret_val = TEST_PASS;
    int len_print = 0;
    uint32_t flow_hash = 0, pload_hash = 0;
    uint8_t temp_zero_padding[PLOAD_CRC_ALIGN_LEN]={0};
    int16_t crc_buf_len = 0;
    uint16_t crc_pad_len =0;
    uint8_t calc_hash_bin = 0;


    /* first calcuate flow hash*/
    flow_hash = bfs_tests_calc_crc(flds, g_n_tuple);
    calc_hash_bin = g_hat_table[(flow_hash&0x3ff)];
    if(kExtHdrTypeBFS == flds->bfs_ext_found)
    {
        /*if extn hdr  values are not equal to calc, there is an error  */
        if((calc_hash_bin != flds->hash)|| (flow_hash != flds->raw_hash))
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Flow hash test failed(Calculated Hash value %d Bin %d CRC 0x%08x, BFS Hash Bin %d,  Raw Hash 0x%08x ).", 
                        (flow_hash&0x3ff),calc_hash_bin, flow_hash, flds->hash, flds->raw_hash);
            rec_msg_len += len_print;
            ret_val =  TEST_FAIL;
        }
        /*if expected hash bin value is given and not equal to calc, there is an error  */
        else if((g_input_hash_value != -1)&& (g_input_hash_value != flds->hash))
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Hash test failed(Expected Hash value %d From BFS header %d).", g_input_hash_value, flds->hash );
            rec_msg_len += len_print;
            ret_val =  TEST_FAIL;
        }
        else
        {
            flow_hash_test_passed++;
        }
        if(bfs_tests_settings.verbose > 1)
        {
            fprintf(stdout, "bfs_tests %"PRIu64": Calc[flow:0x%08x bin:%d] BFS[raw:0x%08x bin:%d]\n",rec_cnt, flow_hash, calc_hash_bin, flds->raw_hash, flds->hash);
        }
    }
    else if(kExtHdrTypeSig == flds->bfs_ext_found)
    {
        if((flow_hash&0xffffff) != flds->raw_hash)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Flow hash mismatch Calc:0x%08x Hdr:0x%06x ",  flow_hash, flds->raw_hash);
            rec_msg_len += len_print;
            ret_val = TEST_FAIL;
        }
        /*if expected hash bin value is given and not equal to calc, there is an error  */
        else if((g_input_hash_value != -1)&& (g_input_hash_value != calc_hash_bin))
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Hash bin failed(Expected Hash value %d Calc  %d).", g_input_hash_value, calc_hash_bin );
            rec_msg_len += len_print;
            ret_val =  TEST_FAIL;
        }
        else
        {
            flow_hash_test_passed++;
        }
        /* Calculate payload hash */

        crc_buf_len = (flds->pload_len - (uint16_t)g_fcs_len);
        if(crc_buf_len <=  0)
        {
            crc_buf_len = 0;
            crc_pad_len= PLOAD_CRC_ALIGN_LEN;
        }
        else
        {
            crc_pad_len =  (PLOAD_CRC_ALIGN_LEN - (crc_buf_len % PLOAD_CRC_ALIGN_LEN))%PLOAD_CRC_ALIGN_LEN;
        }
        pload_hash = dagcrc_do_crc(0, flds->pload_ptr,crc_buf_len, DAGCRC_CRC32C, DAGCRC_MODE_START); 
        pload_hash = dagcrc_do_crc(pload_hash,temp_zero_padding ,crc_pad_len, DAGCRC_CRC32C, DAGCRC_MODE_END); 
        if((pload_hash&0xffffff) != flds->pload_hash)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Pload hash mismatch Calc:0x%08x Hdr:0x%06x ",  pload_hash, flds->pload_hash);
            rec_msg_len += len_print;
            ret_val = TEST_FAIL;
        }
        else
        {
            pload_hash_test_passed++;
        }
        if(bfs_tests_settings.verbose > 1)
        {
            fprintf(stdout, "bfs_tests %"PRIu64": Calc[flow:0x%08x bin:%d pload:0x%08x] Sig[flow:0x%06x bin:%d pload:0x%06x]\n",rec_cnt, flow_hash, calc_hash_bin, pload_hash, flds->raw_hash, g_input_hash_value, flds->pload_hash);
        }
    }
    else if(g_input_hash_value != -1)
    {
        if(g_input_hash_value != calc_hash_bin)
        {
            len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Hash bin failed(Expected Hash value %d Calc  %d).", g_input_hash_value, calc_hash_bin );
            rec_msg_len += len_print;
            ret_val =  TEST_FAIL;
        }
        else
        {
            flow_hash_test_passed++;
        }
        if(bfs_tests_settings.verbose > 1)
        {
            fprintf(stdout, "bfs_tests %"PRIu64": Calc[flow:0x%08x bin:%d] Exp[bin:%d]\n",rec_cnt, flow_hash, calc_hash_bin, g_input_hash_value);
        }
    }
    else
    {
        len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Ignoring hash test Extn hdr found=%d Exp bin: %d ",flds->bfs_ext_found,  g_input_hash_value);
        rec_msg_len += len_print;
        ret_val =  TEST_WARNING;
    }
    return ret_val;
}

/*--------------------------------------------------------------------
  FUNCTION :      bfs_tests_check_any_color_match
 DESCRIPTION :   Go through all the rules and check if the matched ones are expected.
 PARAMETERS :    flds:  parsed bfs_tests_field
  RETURNS :       -TEST_PASS if success 
---------------------------------------------------------------------*/
int bfs_tests_check_any_color_match(bfs_tests_fields_t *flds)
{
    int len_print = 0;
    int i = 0;
    for(i=0;i<BFS_TEST_MAX_RULES;i++)
    {
        if(g_color_filter_flags[i]&kBfsFilterValidColor)
        {
            /*rule for color is given. verify if it matches */
            if(TEST_PASS == bfs_tests_validate_match(flds, &(g_bfs_filter_test_rules[i]),0/*no error print*/))
            {
                /*Check if its expected */
                if((g_input_test_color != -1) && ((g_color_filter_flags[i]&kBfsFilterExpectedColor) == 0))
                {
                    /*expected color given and this one not expected */
                    len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Color test failed(Unexpected color %d matched from rules) ",i);
                    rec_msg_len += len_print;
                    return TEST_FAIL;
                }
                return TEST_PASS;
            }
        }
    }
    /*none of the rules match packet. If we expect sram miss packet, then pass else ,fail */
    if((g_sram_color_default < BFS_TEST_MAX_RULES) && (g_color_filter_flags[g_sram_color_default]&kBfsFilterExpectedColor))
    {
            color_test_sram_missed++;
            return TEST_PASS;
    }
    else
    {
        len_print = snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "Packet did not match any rule ");
        rec_msg_len += len_print;
        return TEST_FAIL;
    }
}
