/*
 * Copyright (c) 2006-2006 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagerf.c 12355 2010-01-11 04:56:43Z wilson.zhu $
 */

/* Routines that are generally useful throughout the programs in the tools directory. 
 * Subject to change at a moment's notice, not supported for customer applications, etc.
 */

/* File header. */
#include "dagerf.h"

/* Endace headers. */
#include "dagapi.h"
#include "dagutil.h"

/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagerf.c 12355 2010-01-11 04:56:43Z wilson.zhu $";


const char *
dagerf_type_to_string(uint8_t type, tt_t legacy_type)
{
	switch (type & 0x7F)
	{
		case ERF_TYPE_LEGACY:
			switch (legacy_type)
			{
				case TT_ATM:
					return "Legacy ATM";
					
				case TT_POS:
					return "Legacy POS";
					
				case TT_ETH:
					return "Legacy ATM";
				
				case TT_ERROR:
					/* Client has specified that Legacy type is an error. */
					return "Legacy (error)";
				
				default:
					return "Legacy (unknown)";
			}
			break;
		
		case ERF_TYPE_HDLC_POS:
			return "ERF PoS";
		
		case ERF_TYPE_ETH:
			return "ERF Ethernet";
		
		case ERF_TYPE_ATM:
			return "ERF ATM";
		
		case ERF_TYPE_AAL5:
			return "ERF AAL5";
		
		case ERF_TYPE_AAL2:
			return "ERF AAL2";
		
		case ERF_TYPE_MC_HDLC:
			return "ERF Multi Channel HDLC";
		
		case ERF_TYPE_MC_RAW:
			return "ERF Multi Channel RAW";
		
		case ERF_TYPE_MC_ATM:
			return "ERF Multi Channel ATM";
		
		case ERF_TYPE_MC_RAW_CHANNEL:
			return "ERF Multi Channel RAW link data";
		
		case ERF_TYPE_MC_AAL5:
			return "ERF Multi Channel AAL5";
		
		case ERF_TYPE_MC_AAL2:
			return "ERF Multi Channel AAL2";
		
		case ERF_TYPE_COLOR_HDLC_POS:
			return "ERF PoS with Color";
		
		case ERF_TYPE_COLOR_HASH_POS:
			return "ERF PoS with Color and Hash";

		case ERF_TYPE_COLOR_ETH:
			return "ERF Ethernet with Color";

		case ERF_TYPE_COLOR_HASH_ETH:
			return "ERF Ethernet with Color and Hash";

		case ERF_TYPE_DSM_COLOR_HDLC_POS:
			return "ERF PoS with DSM Color";
		
		case ERF_TYPE_DSM_COLOR_ETH:
			return "ERF Ethernet with DSM Color";
	
		case ERF_TYPE_IP_COUNTER:
			return "IP Address Counter";
		
		case ERF_TYPE_TCP_FLOW_COUNTER:
			return "TCP Flow Counter";
		
		case TYPE_PAD:
			return "ERF Pad";
        	
		case ERF_TYPE_INFINIBAND:
            		return "ERF Infiniband";
        
        	case ERF_TYPE_INFINIBAND_LINK:
            		return "ERF Infiniband Link";
        
		case ERF_TYPE_RAW_LINK:
			return "ERF Raw Link";
        case TYPE_ERF_INTERNAL0:
        case TYPE_ERF_INTERNAL1: 
        case TYPE_ERF_INTERNAL2: 
        case TYPE_ERF_INTERNAL3: 
        case TYPE_ERF_INTERNAL4: 
        case TYPE_ERF_INTERNAL5: 
        case TYPE_ERF_INTERNAL6: 
        case TYPE_ERF_INTERNAL7: 
        case TYPE_ERF_INTERNAL8: 
        case TYPE_ERF_INTERNAL9: 
        case TYPE_ERF_INTERNAL10:
        case TYPE_ERF_INTERNAL11:
        case TYPE_ERF_INTERNAL12:
        case TYPE_ERF_INTERNAL13:
        case TYPE_ERF_INTERNAL14:
        case TYPE_ERF_INTERNAL15:  
            return "ERF Internal Type";
        case ERF_TYPE_IPV4:
            return "ERF IPv4";
        case ERF_TYPE_IPV6:
            return "ERF IPv6";
		default:
			return "unknown ERF type";
	}
	
	assert(0); /* Should never get here. */
	return "unknown ERF type";
}


const char *
dagerf_record_to_string(uint8_t * erf, tt_t legacy_type)
{
	return dagerf_type_to_string(erf[8], legacy_type);
}


unsigned int
dagerf_get_length(uint8_t * erf)
{
	uint16_t * overlay16 = (uint16_t *) erf;
	
	if (ERF_TYPE_LEGACY == erf[8])
	{
		return 64;
	}
	
	return ntohs(overlay16[5]);
}


unsigned int
dagerf_is_known_type(uint8_t * erf)
{
	if ((erf[8] & 0x7F) <= TYPE_MAX)
	{
		return 1;
	}
	
	return 0;
}


unsigned int
dagerf_is_legacy_type(uint8_t * erf)
{
	if (ERF_TYPE_LEGACY == erf[8])
	{
		return 1;
	}
	
	return 0;
}


unsigned int
dagerf_is_modern_type(uint8_t * erf)
{
	if ((TYPE_MIN <= (erf[8] & 0x7F)) && ((erf[8] & 0x7F) <= TYPE_MAX))
	{
		return 1;
	}
	
	return 0;
}


unsigned int
dagerf_is_ethernet_type(uint8_t * erf)
{
	switch (erf[8] & 0x7F)
	{
		case ERF_TYPE_ETH:
		case ERF_TYPE_COLOR_ETH:
		case ERF_TYPE_DSM_COLOR_ETH:
			return 1;
	}
	
	return 0;
}

unsigned int
dagerf_is_ip_type(uint8_t * erf)
{
	switch (erf[8] & 0x7F)
	{
		case ERF_TYPE_IPV4:
		case ERF_TYPE_IPV6:
			return 1;
	}
	
	return 0;
}

unsigned int
dagerf_is_pos_type(uint8_t * erf)
{
	switch (erf[8] & 0x7F)
	{
		case ERF_TYPE_HDLC_POS:
		case ERF_TYPE_COLOR_HDLC_POS:
		case ERF_TYPE_DSM_COLOR_HDLC_POS:
			return 1;
	}

	return 0;
}


unsigned int
dagerf_is_color_type(uint8_t * erf)
{
	switch (erf[8] & 0x7F)
	{
		case ERF_TYPE_COLOR_HDLC_POS:
		case ERF_TYPE_DSM_COLOR_HDLC_POS:
		case ERF_TYPE_COLOR_ETH:
		case ERF_TYPE_DSM_COLOR_ETH:
		case ERF_TYPE_COLOR_HASH_POS:
		case ERF_TYPE_COLOR_HASH_ETH:
			return 1;
	}

	return 0;
}


unsigned int
dagerf_is_multichannel_type(uint8_t * erf)
{
	switch (erf[8] & 0x7F)
	{
		case ERF_TYPE_MC_HDLC:
		case ERF_TYPE_MC_RAW:
		case ERF_TYPE_MC_ATM:
		case ERF_TYPE_MC_RAW_CHANNEL:
		case ERF_TYPE_MC_AAL5:
		case ERF_TYPE_MC_AAL2:
			return 1;
	}

	return 0;
}


unsigned int
dagerf_ext_header_count(uint8_t * erf, size_t len)
{
	uint32_t hdr_num = 0;
	uint8_t  hdr_type;

	/* basic sanity checks */
	if ( erf == NULL )
		return 0;
	if ( len < 16 )
		return 0;


	/* check if we have any extension headers */
	if ( (erf[8] & 0x80) == 0x00 )
		return 0;

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

		/* get the header type */
		hdr_type = erf[(16 + (hdr_num * 8))];
		hdr_num++;

	} while ( hdr_type & 0x80 );

	return hdr_num;
}

