/*
 * Copyright (c) 2003-2005 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: unified_filter-loader.c 13764 2011-01-17 00:47:14Z nuwan.gunasekara $
 * Author: Jomi Gregory
 * Unified TCAM filter loader
 */
/* Endace headers. */
#include "dag_platform.h"
#include "dagutil.h"
#include "dagclarg.h"
#include "dagapi.h"

/* Endace headers. dag filtering architecture */
#include "dagcam/infiniband_proto.h"

#include "dagcam/unified_filter_interface.h"

/* CVS Header. */
static const char* const kCvsHeader __attribute__((unused)) = "$Id: unified_filter-loader.c 13764 2011-01-17 00:47:14Z nuwan.gunasekara $";
static const char* const kRevisionString = "$Revision: 13764 $";

/** Possible command line option identifiers */
enum
{
	CLA_HELP,
	CLA_VERBOSE,
	CLA_VERSION,
	CLA_RULE_FILENAME,
	CLA_PARSE_ONLY,
	CLA_INIT,
	CLA_DB,
 	CLA_DEVICE,
    CLA_RULE_PARSETYPE,
    CLA_IFACE,
    CLA_INIT_RULESET,
    CLA_INIT_IFACE,
    CLA_OUTPUT_MAPPING,
    CLA_NO_DROP,
    CLA_LINKTYPE,
    CLA_TCAM_WIDTH,
    CLA_VERIFY
};

/** The max length of the filename string on the command line */
#define MAX_FILENAME_LEN   1024

/** Type that describes a port filter entry */
typedef struct _port_entry_t
{
	uint16_t  value;
	uint16_t  mask;
} port_entry_t;

/** Error code indicating that an attribute is missing */

#define SMALL_BUF_SIZE 64 
#define BUFFER_SIZE  256
static char dagname[BUFFER_SIZE] = "dag0";;
static int dagstream = 0;


static unified_filter_type_t parse_filter_type(char *type_string)
{
    if (0 == strcmp(type_string, "ipfv2") )
    {
        return kIPFV2Filter;
    }
    else if (0 == strcmp(type_string,"infiniband"))
    {
        return kInfinibandFilter;
    }
	else if(0 == strcmp(type_string,"bfs"))
	{
		return kBFSFilter;
	}
    else
    {
        dagutil_error("Error: Unknown filter type- %s\n", type_string);
        return kInvalidFilterType;
    }
}

static unified_filter_mapping_t
get_filter_mapping(const char* argument)
{
	if (0 == strcmp(argument, "rxerror"))
	{
		return kMappingRxError;
	}
	else if ((0 == strcmp(argument, "lcntr")) || (0 == strcmp(argument, "colour")) || (0 == strcmp(argument, "color")))
	{
		return kMappingLossCntrl;
	}
	else if (0 == strcmp(argument, "padoffset"))
	{
		return kMappingPadOffset;
	}
    	else 
    	{
        	dagutil_error("Error: Unknown mapping- %s\n",argument);
    	}
	return kMappingUnknown;
}


static unified_filter_linktype_t
get_filter_linktype(const char* argument)
{
	if (0 == strcmp(argument, "ethernet"))
	{
		return kLinkEthernet;
	}
	else if (0 == strcmp(argument, "pos4"))
	{
		return kLinkHDLC;
	}
	else if (0 == strcmp(argument, "pos4chdlc"))
	{
		return kLinkHDLC;
	}
	else if (0 == strcmp(argument, "pos4ppp"))
	{
		return kLinkPPP;
	}
    else 
    {
        dagutil_error("Error: unknown linktype- %s\n",argument);
    }
	return kLinkUnknown;
}


static uint8_t validate_input_params(unified_filter_param_t *in_params )
{

    if ( in_params->initialise &&  in_params->parse_only)
    {
        dagutil_error("Initialize and Parse-only together not allowed\n");
        return 0;
    }
    if ( !(in_params->initialise) || in_params->parse_only)
    {
        if ( -1 != in_params->init_interfaces )
        {
            dagutil_warning ( "--i-port should only be used with -i option \n");
        }
        if ( -1 != in_params->init_rulesets )
        {
            dagutil_warning ( "--i-ruleset should only be used with -i option \n");
        }
    }
    return 1;
}
/**
 * Utility function that simply prints the version information to stdout.
 *
 */
static void
print_version(void)
{
	printf("%s (DAG %s) %s\n", dagutil_get_progname(), kDagReleaseVersion, kRevisionString);
}
/**
 * Utility function that simply prints the usage information to stdout.
 *
 * @param[in]  clarg    The dagclarg handle that contains all the possible command
 *                      line options.
 *
 */
static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("%s - Utility to load rules for classification.\n", dagutil_get_progname());
	printf("Usage: %s [options]\n", dagutil_get_progname());
	dagclarg_display_usage(clarg, stdout);
}

static void init_filter_params(unified_filter_param_t **in_params)
{
	*in_params = (unified_filter_param_t*)malloc ( sizeof( unified_filter_param_t));
	memset( *in_params, 0, sizeof (unified_filter_param_t));
	(*in_params)->init_interfaces = -1;
	(*in_params)->init_rulesets = -1;
	(*in_params)->cur_iface = -1;
        (*in_params)->db_not_specified = 1;
	return;
}
static void cleanup_filter(unified_filter_handle_p in_handle, unified_rule_list_t *in_list,unified_filter_param_t *in_params)
{
    if ( NULL == in_handle)
    {
        return;/* dont do anything */
    }
    if ( in_list && ( in_list->count > 0 ))
    {
        cleanup_rules(in_handle, in_list);
    }
    dispose_filter(in_handle);
    if ( NULL != in_params)
    {
        free (in_params);
    }
}

int
main(int argc, const char *argv[])
{
	ClArgPtr        clarg;
	int             result;
	FILE*           errorfile = NULL;
	int             argindex;
	int             code;
	char            filename_buf[MAX_FILENAME_LEN];
    char            parsetype_string[32];
	char 			dagname_buf[DAGNAME_BUFSIZE];
	bool            parse_rules = false;
	bool            load_rules = true;
	bool            initialise = false;
    bool            verify      = false;
    int verb_val = 1;
	int		database = 0;
    int     iface = -1;
    int     init_interface = -1;
    int     init_ruleset = -1;
	unified_filter_handle_p my_filter_handle = NULL;
    unified_filter_param_t *my_filter_param = NULL;
    unified_filter_error_t ret_val = kUnifiedFilterGeneralError;
    unified_rule_list_t my_rule_list;
	char           mapping_buf[SMALL_BUF_SIZE];
    char           linktype_buf[SMALL_BUF_SIZE];
    int            width = 0;


	dagutil_set_progname("dagfilter-loader");

	/* Set up the command line options. */
	clarg = dagclarg_init(argc, (const char* const *) argv);
	dagclarg_add(clarg, "This page.", "--help", 'h', CLA_HELP);
	dagclarg_add(clarg, "Display version information.", "--version", 'V', CLA_VERSION);
    dagclarg_add(clarg, "Reset the coprocessor (as opposed to hot-swapping the rulesets).", "--initialise", 0, CLA_INIT);
	dagclarg_add_long_option(clarg, CLA_INIT, "--initialize");

	dagclarg_add_int(clarg, "Number of interfaces in the TCAM configuration.  (Only valid with the --initialise option.)(Not valid for Infiniband DAG cards.)", "--init-ifaces", 0, "int", &init_interface, CLA_INIT_IFACE);
	dagclarg_add_long_option(clarg, CLA_INIT_IFACE, "--init-ports");

	dagclarg_add_int(clarg, "Number of rulesets (databases) in the coprocessor configuration.  (Only valid with the --initialise option.Not valid for BFS.)", "--init-rulesets", 0, "int", &init_ruleset, CLA_INIT_RULESET);
    dagclarg_add_int(clarg, "TCAM width (Only valid with the --initialise option.)", "--width", 'w', "width", &width, CLA_TCAM_WIDTH);
    
	dagclarg_add_string(clarg, "Name of the file containing the rules to load.", "--rules", 'f', "filename", filename_buf, MAX_FILENAME_LEN, CLA_RULE_FILENAME);
	dagclarg_add(clarg, "If specified the file is only parsed not loaded into hardware.(You need to specify filter type based on which file is to be parsed using -p)", "--parse-only", 'y', CLA_PARSE_ONLY);
	dagclarg_add_string(clarg, "filter type based on which the file has to be parsed", "--parse-as", 'p', "parsetype", parsetype_string, 32, CLA_RULE_PARSETYPE);
	dagclarg_add_int(clarg, "Use ruleset (0-max) instead of ruleset 0", "--db", 0, "number", &database, CLA_DB);
        dagclarg_add_int(clarg, "Use the rules for iface interface.(Not valid for Infiniband DAG cards)", "--iface", 0, "iface", &iface, CLA_IFACE);
	dagclarg_add_string(clarg, "DAG card to use. default: /dev/dag0.", "--device", 'd', "device", dagname, DAGNAME_BUFSIZE, CLA_DEVICE);
	/* suppressed options work, but are not displayed in help */
	dagclarg_add_string(clarg, "Where to map the packet classification.", "--mapping", 'm', "mapping", mapping_buf, SMALL_BUF_SIZE, CLA_OUTPUT_MAPPING);
	dagclarg_suppress_display(clarg, CLA_OUTPUT_MAPPING);
	dagclarg_add(clarg, "Don't drop packets routed to a full receive stream", "--no-drop", 0, CLA_NO_DROP);
	dagclarg_suppress_display(clarg, CLA_NO_DROP);
	/*-------------------------------------------------------*/
	dagclarg_add_string(clarg, "Link type: valid values are 'ethernet', 'pos4chdlc', and 'pos4ppp'.", "--link", 'l', "linktype", linktype_buf, SMALL_BUF_SIZE, CLA_LINKTYPE);
	dagclarg_add(clarg, "Verify (Read back) rules after loading. (Default is no verification.)", "--verify", 'r', CLA_VERIFY);
    	dagclarg_add_int(clarg, "Set the verbosity level.", "--verbose", 'v', "level", &verb_val, CLA_VERBOSE);
	
	dagutil_set_verbosity(0);
    init_filter_params(&my_filter_param);
    memset(&my_rule_list, 0 , sizeof (unified_rule_list_t));

	/* Parse the command line options. */
	result = dagclarg_parse(clarg, errorfile, &argindex, &code);

	while (1 == result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg);
				return EXIT_SUCCESS;
				break;
	
			case CLA_VERBOSE:
                dagutil_set_verbosity(verb_val);
				errorfile = stderr;
				break;

		   	case CLA_VERSION:
				print_version();
				return EXIT_SUCCESS;
				break;

			case CLA_RULE_FILENAME:
				parse_rules = true;
				break;
				
			case CLA_PARSE_ONLY:
				my_filter_param->parse_only = 1;
		         my_filter_param->initialise = 0;
                load_rules = false;
				break;
            case CLA_RULE_PARSETYPE:
                my_filter_param->type = parse_filter_type(parsetype_string);
                break;
			case CLA_INIT:
				my_filter_param->initialise = 1;
                initialise = true;
				break;
			case CLA_DB:
				my_filter_param->cur_ruleset = database;
				my_filter_param->db_not_specified = 0;
				break;
            case CLA_IFACE:
				my_filter_param->cur_iface = iface;
				break;
			
			case CLA_DEVICE:
			     
				  break;				
            case CLA_INIT_IFACE:
                    my_filter_param->init_interfaces = init_interface;
                    break;
            case CLA_INIT_RULESET:
                    my_filter_param->init_rulesets = init_ruleset;
                    break;
            case CLA_OUTPUT_MAPPING:
                    my_filter_param->mapping = get_filter_mapping(mapping_buf);
                    break;
            case CLA_NO_DROP:
                    my_filter_param->is_no_drop = 1;
                    break;
            case CLA_LINKTYPE:
                    my_filter_param->linktype = get_filter_linktype(linktype_buf);
                    break;
            case CLA_TCAM_WIDTH:
                    my_filter_param->tcam_width = width ;
                    break;
            case CLA_VERIFY:
                    verify = true;
                    break;
			default:
				if ((0 == strcmp(argv[argindex], "--usage")) || (0 == strcmp(argv[argindex], "-?")))
				{
					print_usage(clarg);
					return EXIT_SUCCESS;
				}
				else if (argv[argindex][0] == '-')
				{
					/* Unknown option. */
					dagutil_error("unknown option %s\n", argv[argindex]); 
					dagclarg_display_usage(clarg, stdout);
					return EXIT_FAILURE;
				}
				break;
		};
		result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	};

    if (-1 == dag_parse_name(dagname,dagname_buf, DAGNAME_BUFSIZE, &dagstream))
	{
	   dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
	}
	/* ClargPtr should no longer be necessary. */
	dagclarg_dispose(clarg);

    /* just assigning the ptr. not Strcpying */
    my_filter_param->dagname = dagname_buf;

    if ( 0 == validate_input_params (my_filter_param) )
    {
        return EXIT_FAILURE;
    }
   
    /* Check if anything is needed to be done */
	if ( !parse_rules && !initialise )
	{
		/* nothing to do */
		dagutil_warning ("a rule filename must be specified for this utility to do something\n");
		dagutil_warning ("or initialize the TCAM operation must be setup \n");
		return EXIT_SUCCESS;
	};

    my_filter_handle = filter_factory_get_filter(my_filter_param);
    if(NULL == my_filter_handle )
    {
        dagutil_error("Filter init failed \n");
        return EXIT_FAILURE;
    }
	if ( parse_rules )
    {
        ret_val = parse_rule_file(my_filter_handle, filename_buf, &my_rule_list);
        if ( kUnifiedFilterSuccess != ret_val)
        {
            dagutil_error("Parsing the file failed \n");
            cleanup_filter(my_filter_handle,&my_rule_list,my_filter_param);

            return EXIT_FAILURE;
        }
    }
	/* Check if we need the TCAM */
	if ( parse_rules && load_rules   )
	{
		ret_val =  write_rules(my_filter_handle, &my_rule_list);
        if ( kUnifiedFilterSuccess != ret_val)
        {
            dagutil_error("Writing  the rules failed \n");
            cleanup_filter(my_filter_handle,&my_rule_list,my_filter_param);

            return EXIT_FAILURE;
        }
		if ( verify )
        {
            read_verify_rules(my_filter_handle, &my_rule_list);
            cleanup_filter(my_filter_handle,&my_rule_list,my_filter_param);

            return EXIT_FAILURE;
        }
    }

    if ( initialise )
    {
        ret_val = configure_filter(my_filter_handle, my_filter_param);
        if ( kUnifiedFilterSuccess != ret_val)
        {
            dagutil_error("Configuring the filter failed \n");
            cleanup_filter(my_filter_handle,&my_rule_list,my_filter_param);

            return EXIT_FAILURE;
        }
	}
    
    if ( my_filter_param->parse_only )
    {
        ret_val = print_rule_list(my_filter_handle, &my_rule_list, stdout);
        if ( kUnifiedFilterSuccess != ret_val)
        {
            dagutil_error("Printing   the rules failed \n");
            cleanup_filter(my_filter_handle,&my_rule_list,my_filter_param);

            return EXIT_FAILURE;
        }
    }
    cleanup_filter(my_filter_handle, &my_rule_list,my_filter_param);

	return EXIT_SUCCESS;
}












