/*
 * Copyright (c) 2004-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: dagpf.c 9865 2008-09-01 08:16:23Z vladimir $
 */

#include "dagapi.h"
#include "dagnew.h"
#include "dagreg.h"

#include "dag_filter.h"
#include "filters.h"
#include "dagipf.h"
#include "dagpf.h"

static int load_filters_iface(FilterPrivatePtr dag_filter_private,
                              int dagfd,
                              filter_list_t* list,
                              uint8_t iface,
                              bool initialise,
                              bool verify);
static unsigned int create_ip_address_entry(ip_address_t* address, const char* token);
static unsigned int create_port_entry(cam_port_t* port, const char* token);
static unsigned int create_tcp_flags_entry(tcp_flags_t* flags, const char* token);
static unsigned int create_ip_protocol_entry(protocol_t* proto, const char* token); 
static unsigned int create_l2_protocol_entry(l2_protocol_t* proto, const char* token);
static int is_dag_in_atm_mode(int dagfd);
static unsigned int detect_loaded_copro_image(coprocessor_t coprocessor_type,
                                              volatile uint8_t* dagiom,
                                              dag_reg_t registers[DAG_REG_MAX_ENTRIES]);

/**
 * Initialize filter loader library
 *
 * Allocate memory for the data structures and return pointers to them. 
 *
 * \param filterlist  pointer to an array of filterlists (for both interfaces plus a default list)
 * \param param       pointer to parameter structure
 *
 * \return non-zero in case of errors, zero otherwise
 */
int dagpf_init(filter_list_t** filterlist, filter_param_t** param) {

    /* one filterlist for each interface plus one for the default list */
    *filterlist = calloc(FILTER_LOADER_MAX_INTERFACES + 1 , sizeof(filter_list_t));
    if (!filterlist) {
        return 1;
    }

    *param = calloc(1, sizeof(filter_param_t));
    if (!param) {
        return 1;
    }

    /* set the default value */
    (*param)->coprocessor_type = kCoprocessorUnknown;

    (*param)->initialise = false;
    (*param)->verify = false;
    (*param)->drop = false;
    (*param)->_port = -1;
    (*param)->init_interfaces = 0;
    (*param)->init_rulesets = 0;
    (*param)->snaplength = UINT16_MAX;
    (*param)->linktype = DF_LINK_UNKNOWN; /* Must be set. */
    (*param)->mapping = DF_MAPPING_UNKNOWN; /* The only safe choice that works for both Ethernet and PoS. */

    return 0;
}


/**
 * Cleanup filter loader library
 *
 * Free allocated memory, close files.
 *
 * \param filterlist  an array of filterlists (for both interfaces plus a default list)
 * \param param       pointer to parameter structure
 *
 * \return zero
 */
int dagpf_cleanup(filter_list_t* filterlist, filter_param_t* param) {
    int i;
    filter_t* current;
    filter_t* next;
    int j = 0;

    /* go through the filterlists and free the allocated memory */
    for (i = 0; i <= FILTER_LOADER_MAX_INTERFACES; i++) {
        current = filterlist[i].head;
        while (current) {
            next = current->next;
            free(current);
            current = next;
        }
    }
    free(filterlist);
    free(param);
    return 0;
}

/**
 * Coprocessor check
 *
 * Check coprocessor type and loaded image. Store the copro type in
 * the corresponding field of 'param' to be used by filter loading.
 *
 * \param dagfd  file descriptor for the DAG device
 * \param param  pointer to the parameter structure to fill in
 * 
 * \return non-zero in case of errors, zero otherwise
 */
int dagpf_check_copro(int dagfd, filter_param_t* param) {
	dag_reg_t registers[DAG_REG_MAX_ENTRIES];
    volatile uint8_t* uDagiom;
    param->coprocessor_type = kCoprocessorUnknown;

	uDagiom = dag_iom(dagfd);
	assert(NULL != uDagiom);

	param->coprocessor_type = dagutil_detect_coprocessor(uDagiom);

	switch (param->coprocessor_type)
	{
		case kCoprocessorNotFitted:
			printf("No Endace Coprocessor is fitted.\n");
			return EXIT_FAILURE;
			
		case kCoprocessorPrototype:
			printf("Detected Endace Prototype Coprocessor: DO NOT USE.\n");
			return EXIT_FAILURE;
			
		case kCoprocessorSC128:
			printf("Detected Endace SC128 Coprocessor.\n");
			break;
			
		case kCoprocessorSC256:
			printf("Detected Endace SC256 Coprocessor.\n");
			break;
	
		case kCoprocessorSC256C:
			printf("Detected Endace SC256 Coprocessor Rev C.\n");
			break;
		
		case kCoprocessorBuiltin:
			printf("Detected Endace Accelerated card.\n");
			break;

		case kCoprocessorUnknown:
			printf("Detected unknown Endace Coprocessor.\n");
			return EXIT_FAILURE;
		
		default:
			assert(0); /* Illegal value. */
			break;
	}

	if (0 == detect_loaded_copro_image(param->coprocessor_type, uDagiom, registers))
	{
		dagutil_error("coprocessor image not loaded");
		return EXIT_FAILURE;
	}

    return EXIT_SUCCESS;
}

/**
 * Read filters from file
 *
 * Read the input file line-by-line and parse each line(rule) to
 * determine the various parameters of the rule and create the
 * corresponding CAM entries.
 *
 * \param infile      file pointer to input file
 * \param filterlist  an array of filterlists (for both interfaces plus a default list)
 * \param param       pointer to parameter structure
 *
 * \return non-zero in case of error
 */
int
dagpf_read_filters(FILE* infile, filter_list_t* filterlist, filter_param_t* param)
{
	unsigned int valid_lines = 0;
	unsigned int all_filter_found = 0;
	unsigned int line = 0;
	char linebuf[LINE_MAX];

    filter_list_t* uDefaultFilterList = &filterlist[FILTER_LOADER_MAX_INTERFACES];
    filter_list_t* uFilterList = filterlist;

	while (fgets(linebuf, LINE_MAX, infile))
	{
		const char* token = NULL;
		const char* value = NULL;
		filter_t* filter = NULL;
		int classification = 0;
		int int_value = 0;
		int iface = -1;
		int protocol_set = 0;
		int dest_buffer = DST_BUFFER_NONE;
		int valid_entry = 1;
		cam_entry_t entry;
		
		line++;
		memset(&entry, 0, sizeof(cam_entry_t));

		token = strtok(linebuf, " \t\n");
		if (!token || (strlen(token) == 0) || (token[0] == '#') || (token[0] == '%'))
		{
			/* Comment or empty line. */
			continue;
		}
		int_value = strtol(token, NULL, 0);

		switch (param->coprocessor_type)
        {
        case kCoprocessorSC128:
            /* First token should be a 14-bit colour. */
            if ((0 <= int_value) && (int_value < 16384))
            {
                classification = int_value;
            }
            else
            {
                /* Invalid colour. */
                fprintf(stderr, "error (line %u): invalid classification value '%d' (must be in [0,16383])\n", line, int_value);
                valid_entry = 0;
            }
            break;

		case kCoprocessorSC256: /* flow through */
        case kCoprocessorSC256C: /* flow through */
        case kCoprocessorBuiltin:
 			if ((0 <= int_value) && (int_value < 32768))
			{
				classification = int_value;
			}
			else
			{
				/* Invalid colour. */
				fprintf(stderr, "error (line %u): invalid classification value '%d' (must be in [0,32767])\n", line, int_value);
				valid_entry = 0;
			}
            break;

        default: /* we should never get here */
            assert(0);
        }
            
		/* Repeatedly read keywords and values. */
		token = strtok(NULL, " \t\n");
		while ((1 == valid_entry) && (NULL != token) && (strlen(token) > 0))
		{
            /* action */
			if (strcmp(token, "accept") == 0)
			{
				entry.action = ACTION_PASS;
			}
			else if (strcmp(token, "reject") == 0)
			{
				entry.action = ACTION_DROP;
			}
            /* protocol */
			else if (strcmp(token, "all") == 0)
			{
				entry.protocol.protocol = 0;
				entry.protocol.mask = 0;
				protocol_set = 1;
				all_filter_found = 1;
			}
			else if (strcmp(token, "ip") == 0)
			{
				entry.protocol.protocol = IPPROTO_IP;
				entry.protocol.mask = 0xff;
				protocol_set = 1;
			}
			else if (strcmp(token, "icmp") == 0)
			{
				entry.protocol.protocol = IPPROTO_ICMP;
				entry.protocol.mask = 0xff;
				protocol_set = 1;
			}
			else if (strcmp(token, "igrp") == 0)
			{
				entry.protocol.protocol = IPPROTO_IGRP;
				entry.protocol.mask = 0xff;
				protocol_set = 1;
			}
			else if (strcmp(token, "tcp") == 0)
			{
				entry.protocol.protocol = IPPROTO_TCP;
				entry.protocol.mask = 0xff;
				protocol_set = 1;
			}
			else if (strcmp(token, "udp") == 0)
			{
				entry.protocol.protocol = IPPROTO_UDP;
				entry.protocol.mask = 0xff;
				protocol_set = 1;
			}
            else if (strcmp(token, "l2-proto") == 0)
			{
				value = strtok(NULL, " \t\n");
				if ((NULL == value) || (strlen(value) == 0))
				{
					fprintf(stderr, "error (line %u): unspecified l2 protocol\n", line);
					valid_entry = 0;
				}
		
				if (create_l2_protocol_entry(&entry.l2_protocol, value) == 0)
				{
					fprintf(stderr, "error (line %u): unrecognised l2 protocol '%s'\n", line, value);
					valid_entry = 0;
				}
				protocol_set = 1;
	
			}
			else if (strcmp(token, "ip-proto") == 0)
			{
				value = strtok(NULL, " \t\n");
				if ((NULL == value) || (strlen(value) == 0))
				{
					fprintf(stderr, "error (line %u): unspecified ip protocol\n", line);
					valid_entry = 0;
				}
		
				if (create_ip_protocol_entry(&entry.protocol, value) == 0)
				{
					fprintf(stderr, "error (line %u): unrecognised ip protocol '%s'\n", line, value);
					valid_entry = 0;
				}
				protocol_set = 1;
	
			}
			else if (strcmp(token, "non-ip") == 0)
			{
				/* Technically not the correct protocol number to use, but sufficient for our purposes
				 * dag_filter.c has been modified to treat this protocol number specially.
				 */
				entry.protocol.protocol = IPPROTO_RAW;
				entry.protocol.mask = 0xff;
				protocol_set = 1;
			}
            /* stream */
			else if (strcmp(token, "red") == 0)
			{
				dest_buffer |= DST_BUFFER_RED;
			}
			else if (strcmp(token, "blue") == 0)
			{
				dest_buffer |= DST_BUFFER_BLUE;
			}
            /* source address */
			else if (strcmp(token, "src-ip") == 0)
			{
				value = strtok(NULL, " \t\n");
				if ((NULL == value) || (strlen(value) == 0))
				{
					fprintf(stderr, "error (line %u): unspecified source IP address\n", line);
					valid_entry = 0;
				}
		
				if (create_ip_address_entry(&entry.source, value) == 0)
				{
					fprintf(stderr, "error (line %u): unrecognised source IP address '%s'\n", line, value);
					valid_entry = 0;
				}
			}
            /* source port */
			else if (strcmp(token, "src-port") == 0)
			{
				value = strtok(NULL, " \t\n");
				if ((NULL == value) || (strlen(value) == 0))
				{
					fprintf(stderr, "error (line %u): unspecified source port\n", line);
					valid_entry = 0;
				}
		
				if (create_port_entry(&entry.src_port, value) == 0)
				{
					fprintf(stderr, "error (line %u): unrecognised source port '%s'\n", line, value);
					valid_entry = 0;
				}
			}
            /* destination address */
			else if (strcmp(token, "dst-ip") == 0)
			{
				value = strtok(NULL, " \t\n");
				if ((NULL == value) || (strlen(value) == 0))
				{
					fprintf(stderr, "error (line %u): unspecified destination IP address\n", line);
					valid_entry = 0;
				}
		
				if (create_ip_address_entry(&entry.dest, value) == 0)
				{
					fprintf(stderr, "error (line %u): unrecognised destination IP address '%s'\n", line, value);
					valid_entry = 0;
				}
			}
            /* destination port */
			else if (strcmp(token, "dst-port") == 0)
			{
				value = strtok(NULL, " \t\n");
				if ((NULL == value) || (strlen(value) == 0))
				{
					fprintf(stderr, "error (line %u): unspecified destination port\n", line);
					valid_entry = 0;
				}
		
				if (create_port_entry(&entry.dest_port, value) == 0)
				{
					fprintf(stderr, "error (line %u): unrecognised destination port '%s'\n", line, value);
					valid_entry = 0;
				}
			}
            /* TCP flags */
			else if (strcmp(token, "tcp-flags") == 0)
			{
				value = strtok(NULL, " \t\n");
				if (value)
				{
					if (create_tcp_flags_entry(&entry.tcp_flags, value) == 0)
					{
						fprintf(stderr, "error (line %u): unrecognised TCP flags '%s'\n", line, value);
						valid_entry = 0;
					}
				}
				else
				{
					fprintf(stderr, "error (line %u): unspecified TCP flags\n", line);
					valid_entry = 0;
				}
			}
            /* snaplength */
			else if (strcmp(token, "snap") == 0)
			{
				value = strtok(NULL, " \t\n");
				if (value)
				{
					int_value = strtol(value, NULL, 0);
					if ((0 < int_value) && (int_value < 65536))
					{
						entry.snaplength = int_value;
					}
					else
					{
						/* Invalid snap length value. */
						fprintf(stderr, "error (line %u): invalid snap length '%d' (must be in [1,65535])\n", line, int_value);
						valid_entry = 0;
					}
				}
				else
				{
					/* No snap length given. */
					fprintf(stderr, "error (line %u): unspecified snap length\n", line);
					valid_entry = 0;
				}
			}
            /* interface */
			else if (strcmp(token, "iface") == 0)
			{
				value = strtok(NULL, " \t\n");
				if (value)
				{
					int_value = strtol(value, NULL, 0);
					if ((0 <= int_value) && (int_value < FILTER_LOADER_MAX_INTERFACES))
					{
						iface = int_value;
					}
					else
					{
						/* Invalid interface value. */
						fprintf(stderr, "error (line %u): invalid interface '%d' (must be in [0,%u])\n", line, int_value, FILTER_LOADER_MAX_INTERFACES);
						valid_entry = 0;
					}
				}
				else
				{
					/* No snap length given. */
					fprintf(stderr, "error (line %u): unspecified interface\n", line);
					valid_entry = 0;
				}
			}
			else
			{
				/* Unrecognised token. */
				fprintf(stderr, "error (line %u): Unrecognised token '%s'.\n", line, token);
				valid_entry = 0;
			}
			
			token = strtok(NULL, " \t\n");
		}

		/* Check that compulsory fields have been set. */
		if (0 == protocol_set)
		{
			/* No protocol. */
			valid_entry = 0;
		}

		if (valid_entry)
		{
			if (0 == entry.snaplength)
			{
				if (param->snaplength)
				{
					entry.snaplength = param->snaplength;
				}
			}

			/* Ensure consistency between the pass/drop action and the destination buffer. */
			if (ACTION_PASS == entry.action)
			{
				/* If no destination buffer was specified, send packet to first buffer. */
				if (DST_BUFFER_NONE == dest_buffer)
				{
					dest_buffer = DST_BUFFER_RED;
				}
			}
			else if (ACTION_DROP == entry.action)
			{
				/* If packet should be dropped, make sure destination buffer bits are clear. */
				dest_buffer = DST_BUFFER_NONE;
			}
			else
			{
				assert(0);
			}

			/* Merge the destination buffer and classification into the 16-bit colour field. */
			entry.colour = ((classification << 2) & 0xfffc) | (dest_buffer & 0x03);

			/* If we get here then the filter has been successfully parsed, 
			 * so add it to the appropriate filter list. 
			 */
			filter = malloc(sizeof(filter_t));

			if (filter)
			{
				filter->next = NULL;
				memcpy(&filter->entry, &entry, sizeof(cam_entry_t));

				if (dagutil_get_verbosity())
				{
					display_filter_entry(stdout, &entry);
				}

				if (-1 == iface)
				{
					/* Add to default filter list. */
					if (NULL != uDefaultFilterList->head)
					{
						uDefaultFilterList->tail->next = filter;
						uDefaultFilterList->tail = filter;
					}
					else
					{
						uDefaultFilterList->head = filter;
						uDefaultFilterList->tail = filter;
					}

					uDefaultFilterList->items++;
					dagutil_verbose("added to default filter list\n");
				}
				else
				{
					/* Add to filter list for a specific interface. */
					if (NULL != uFilterList[iface].head)
					{
						uFilterList[iface].tail->next = filter;
						uFilterList[iface].tail = filter;
					}
					else
					{
						uFilterList[iface].head = filter;
						uFilterList[iface].tail = filter;
					}

					uFilterList[iface].items++;
					dagutil_verbose("added to filter list for interface %d\n", iface);
				}
			}
			else
			{
				fprintf(stderr, "error (line %u): could not allocate memory: %s\n", line, strerror(errno));
				return 1;
			}
			
			valid_lines++;
		}
	}
	
	if (0 == valid_lines)
	{
		dagutil_warning("no valid filters found.\n");
	}
	
	if (0 == all_filter_found)
	{
		dagutil_warning("no default filter (protocol 'all') found.\n");
	}

    return 0;
}

/**
 * Load filters to DAG card
 *
 * Convert the list of filters into an array and load them to CAM. Use
 * the parameters given in 'param', i.e. which ruleset to use, drop
 * policy, etc. See the type definition of the filter_param_t.
 *
 * \param dagfd        file descriptor of the device
 * \param filterlist   array of filterlists
 * \param param        parameters affecting filter loading
 *
 * \return non-zero if error occured and reset is required after loading filters
 */
int
dagpf_load_filters(int dagfd, filter_list_t* filterlist, filter_param_t* param)
{
	int do_reset = 0;
	uint8_t index;
    filter_list_t* uDefaultFilterList = &filterlist[FILTER_LOADER_MAX_INTERFACES];
    filter_list_t* uFilterList = filterlist;
    FilterPrivatePtr dag_filter_private;
    int error = 0;

	/* Do some sanity checking and dispatch to the appropriate routine. */

	/* Cannot mix and match default list and other lists. */
	if (uDefaultFilterList->items != 0)
	{
		/* Default filter list contains filters: must not be filters in other lists. */
		for (index = 0; index < FILTER_LOADER_MAX_INTERFACES; index++)
		{
			if (uFilterList[index].items != 0)
			{
				dagutil_error("some filters had an interface specification and some did not.\n");
				dagutil_error("either all filters should have an interface specification, or no filters should have an interface specification.\n");
				return 1;
			}
		}
	}
	else
	{
		int sum = uFilterList[0].items;

		/* Default filter list does not contain filters: there must be filters in other lists. */
		for (index = 1; index < FILTER_LOADER_MAX_INTERFACES; index++)
		{
			sum += uFilterList[index].items;
		}
	}

	/* Check that the DAG card is not in ATM mode - the IP Filter firmware only supports PoS and Ethernet. */
	if (1 == is_dag_in_atm_mode(dagfd))
	{
		dagutil_error("ATM mode detected - Endace Filter requires PoS or Ethernet links.\n");
		return 1;
	}

    dag_filter_private = dag_filter_init();
    assert(dag_filter_private);

	/* Connect to the Coprocessor, initialising if required. */
	if (param->initialise)
	{
		assert(param->init_interfaces != 0);
		assert(param->init_rulesets != 0);

		/* Configure the Coprocessor. */
        if (0 == dag_filter_configure_copro(dag_filter_private,
                                            dagfd,
                                            param->linktype,
                                            param->mapping,
                                            param->init_interfaces,
                                            param->init_rulesets,
                                            param->coprocessor_type))
		{
			dagutil_error("could not configure Coprocessor.\n");
			return 1;
		}
		
		do_reset = 1;
	}
	else
	{
		/* Connect to the Coprocessor. */
		if (0 == dag_filter_connect_to_copro(dag_filter_private,
                                             dagfd,
                                             param->linktype,
                                             param->coprocessor_type))
		{
			dagutil_error("could not connect to Coprocessor.\n");
			return 1;
		}
	}

	/* Load the filters. */
	if (uDefaultFilterList->items != 0)
	{
		if (-1 == param->_port)
		{
			/* Load filters for both interfaces. */
			uint8_t iface_count = dag_filter_get_port_count(dag_filter_private);
			
			for (index = 0; index < iface_count; index++)
			{
				do_reset += load_filters_iface(dag_filter_private, 
                                               dagfd,
                                               uDefaultFilterList,
                                               index,
                                               param->initialise,
                                               param->verify);
			}
		}
		else
		{
			/* Only load the filters for the specified interface. */
			do_reset += load_filters_iface(dag_filter_private,
                                           dagfd,
                                           uDefaultFilterList,
                                           param->_port,
                                           param->initialise,
                                           param->verify);
		}
	}
	else
	{
		for (index = 0; index < FILTER_LOADER_MAX_INTERFACES; index++)
		{
			if (0 != uFilterList[index].items)
			{
				do_reset += load_filters_iface(dag_filter_private,
                                               dagfd,
                                               &uFilterList[index],
                                               (uint8_t) index,
                                               param->initialise,
                                               param->verify);
			}
		}
	}
	
	if (param->initialise)
	{
		int iface;
		
		/* Set the PBM drop behaviour. */
		if (param->drop)
		{
			dag_filter_set_drop_enable(dag_filter_private, dagfd, 1);
		}
		else
		{
			dag_filter_set_drop_enable(dag_filter_private, dagfd, 0);
		}
		
		/* Initialize the current rulesets for each interface to ruleset 0. */
		for (iface = 0; iface < param->init_interfaces; iface++)
		{
			uint8_t current_ruleset = 99; /* A value that isn't a valid ruleset. */
			
			if (0 == dag_filter_set_port_ruleset(dag_filter_private, iface, 0))
			{
				dagutil_error("could not set ruleset for interface %d.\n", iface);
				return 1;
			}
			
			if (0 == dag_filter_get_port_ruleset(dag_filter_private, iface, &current_ruleset))
			{
				dagutil_error("could not determine current ruleset for interface %d.\n", iface);
				return 1;
			}
			
			assert(0 == current_ruleset);
		}
	}
	
	if (0 != do_reset)
	{
		/* Perform upper datapath reset. */
		dagutil_reset_datapath(dag_iom(dagfd));
	}

    dag_filter_dispose(dag_filter_private);
    return error;
}


/* Implementation of internal routines. */


/**
 * Load filter to interface
 *
 * Convert the list of filters into an array and load them to CAM. Use
 * the current ruleset if initialise is true, otherwise use the other ruleset.
 *
 * \param dag_filter_private    private data
 * \param dagfd  file descriptor of the device
 * \param list   pointer to list of filters
 * \param iface  interface to use
 * \param initilise    whether to interface to use
 *
 * \return non-zero if reset is required after loading filters
 */
static int
load_filters_iface(FilterPrivatePtr dag_filter_private,
                   int dagfd,
                   filter_list_t* list,
                   uint8_t iface,
                   bool initialise,
                   bool verify)
{
	cam_entry_t** cam_array = (cam_entry_t**) malloc(list->items * sizeof(cam_entry_t*));
	unsigned int index = 0;
	uint8_t iface_count;
	int do_reset = 0;
	filter_t* filter;

	if (!cam_array)
	{
		dagutil_error("could not allocate memory for filters: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	memset(cam_array, 0, list->items * sizeof(cam_entry_t*));

	/* Turn the list of filters into an array suitable for loading into the CAM. */
	filter = list->head;
	while (filter)
	{
		cam_array[index] = &filter->entry;

#if DEBUG_SNAPLENGTH
		cam_array[index]->snaplength = index | 0x4200;
#endif /* DEBUG_SNAPLENGTH */

		index++;
		filter = filter->next;
	}
	
	/* Check for valid interface. */
	iface_count = dag_filter_get_port_count(dag_filter_private);
	if (iface >= iface_count)
	{
		dagutil_panic("asked to load filters for interface %d, but the Coprocessor has been configured with only %d interfaces.\n", iface, iface_count);
		exit(EXIT_FAILURE);
	}

	if (initialise)
	{
		/* Load the filters in interface as specified on commandline. */
		if (0 == dag_filter_load_cam_entries(dag_filter_private, iface, 0, cam_array, list->items, verify))
		{
			dagutil_panic("could not load filters into Coprocessor.\n");
			exit(EXIT_FAILURE);
		}
		
		do_reset = 1;
	}
	else
	{
		/* Find out the current ruleset for the port. */
		uint8_t ruleset = 0;
		uint8_t current_ruleset = 0;
		uint8_t ruleset_count = 0;

		if (0 == dag_filter_get_port_ruleset(dag_filter_private, iface, &current_ruleset))
		{
			dagutil_error("could not determine current ruleset.\n");
			exit(EXIT_FAILURE);
		}
		dagutil_verbose("current ruleset is set %d\n", current_ruleset);

		ruleset_count = dag_filter_get_port_ruleset_count(dag_filter_private, iface);
		if (0 == ruleset_count)
		{
			dagutil_error("could not determine ruleset count for interface %u.\n", iface);
			exit(EXIT_FAILURE);
		}

		/* Load these entries in the other ruleset. */
		if (1 == ruleset_count)
		{
			assert(0 == current_ruleset);
			ruleset = 0;
			do_reset = 1;
		}
		else if (2 == ruleset_count)
		{
			if (0 == current_ruleset)
			{
				ruleset = 1;
			}
			else if (1 == current_ruleset)
			{
				ruleset = 0;
			}
			
			do_reset = 0; /* Loading into the inactive ruleset doesn't require a reset. */
		}
		
		if (0 == dag_filter_load_cam_entries(dag_filter_private, iface, ruleset, cam_array, list->items, verify))
		{
			dagutil_error("could not load filters into Coprocessor.\n");
			exit(EXIT_FAILURE);
		}

		/* Activate the newly loaded entries. */
		if (0 == dag_filter_set_port_ruleset(dag_filter_private, iface, ruleset))
		{
			dagutil_error("could not set ruleset.\n");
			exit(EXIT_FAILURE);
		}
	}

    free(cam_array);
	
	return do_reset;
}

/**
 * Check for ATM mode
 *
 * \param dagfd   file descriptor of the DAG device
 *
 * \return non-zero if DAG card is in ATM mode
 */
static int
is_dag_in_atm_mode(int dagfd)
{
//#define USE_CONFIG_API
#ifdef USE_CONFIG_API
    dag_card_ref_t card_ref = NULL;

    // find out the devive name

    card_ref = dag_config_init(dagname);

    dag_config_get_attribute_count(dag_card_ref_t card_ref, dag_attribute_code_t attr_code);
    
    dag_config_dispose(dag_card_ref_t card_ref);
#else
	volatile uint8_t* iom = dag_iom(dagfd);
	daginf_t* daginf = dag_info(dagfd);
	dag_reg_t result[DAG_REG_MAX_ENTRIES];

	memset(result, 0, sizeof(result));

	if (DAG38(daginf->device_code))
	{
		/* DAG 3.8. */
		uint32_t phy_base;
		uint32_t val;
		
		if (-1 == dag_reg_find((char*) iom, DAG_REG_SONIC_3, result))
		{
			dagutil_panic("could not locate physical layer chip\n");
			return 0;
		}
		
		phy_base = DAG_REG_ADDR(result[0]);
		
		/* Determine ATM mode or not. */
		
		/* First interface. */
		val = *(volatile uint32_t*)(iom + phy_base + 0x04); /* 0x04 is FROFF_RXCONFIG. */
		if (0 == (val & (BIT31 | BIT30)))
		{
			return 1;
		}
		
		/* Second interface. */
		if (DAG_REG_SONIC_3 == result[1].module)
		{
			uint32_t phy_base2 = DAG_REG_ADDR(result[1]);
			
			val = *(volatile uint32_t*)(iom + phy_base2 + 0x04); /* 0x04 is FROFF_RXCONFIG. */
			if (0 == (val & (BIT31 | BIT30)))
			{
				return 1;
			}
		}
	}
	else if (0x4300 == (daginf->device_code & 0xff0f))
	{
		/* DAG 4.3S. */
		uint32_t phy_base;
		uint32_t location;
		uint32_t val;

		if (-1 == dag_reg_find((char*) iom, DAG_REG_PM5381, result))
		{
			dagutil_panic("could not locate physical layer chip\n");
			return 0;
		}
		
		phy_base = DAG_REG_ADDR(*result);

		location = ((0x740 << 2) + phy_base); /* 0x740 is the RCFP_Config register. */
		val = *(volatile uint32_t*)(iom + location);
		val &= 0xffff;
		
		if (0 == (val & BIT10))
		{
			return 1;
		}
	}
	else if (0x430e == (daginf->device_code & 0xff0f))
	{
		/* DAG 4.3GE - don't have to worry about Ethernet here. */
		return 0;
	}
	else if (0x520a == (daginf->device_code & 0xff0f))
	{
		/* DAG 5.2 SXA */
        
        /* FIXME!!! TODO: use Config API */
		return 0;
	}
    else if (PCI_DEVICE_ID_DAG5_4GA == (daginf->device_code & 0xff0f))
	{
		/* DAG 5.4GA   */
        
        /* FIXME!!! TODO: use Config API */
		return 0;
	}
	else
	{
		return 0;
	}

	return 0;
#endif
}


/**
 * Return non-zero if the coprocessor image is loaded
 */
static unsigned int
detect_loaded_copro_image(coprocessor_t coprocessor_type, volatile uint8_t* dagiom, dag_reg_t registers[DAG_REG_MAX_ENTRIES])
{
	assert(NULL != dagiom);

	switch (coprocessor_type)
	{
		case kCoprocessorNotFitted:
		case kCoprocessorPrototype:
		case kCoprocessorUnknown:
			break;
		
		case kCoprocessorSC128:
			if (dag_reg_find((char*) dagiom, DAG_REG_CAM_MAINT, registers) > 0)
			{
				return 1;
			}
			break;
		
		case kCoprocessorSC256:
		case kCoprocessorSC256C:
		case kCoprocessorBuiltin:
			if (dag_reg_find((char*) dagiom, DAG_REG_IDT_TCAM, registers) > 0)
			{
				return 1;
			}
			break;
		
		default:
			assert(0); /* Illegal value. */
			break;
	}
	
	return 0;
}

/**
 * Create IP address entry (32-bit entry)
 *
 * Convert the ASCII representation to binary form, using the
 * don't-care bits when creating the corresponding mask.
 */
static unsigned int
create_ip_address_entry(ip_address_t* address, const char* token)
{
	unsigned int count = strlen(token);
	unsigned int index;
    int buf_index = 31;

	/* Create the entry in host byte order then convert to network byte order. */
	memset(address, 0, sizeof(ip_address_t));

	for (index = 0; index < count; index++)
	{
		if ((token[index] == '{') || (token[index] == '}'))
		{
			/* Do nothing. */
		}
		else if (token[index] == '0')
		{
			/* Address bit is already 0. */

			/* Set mask bit to 1. */
			address->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '1')
		{
			/* Set address bit to 1. */
			address->ip4_addr |= (1 << buf_index);

			/* Set mask bit to 1. */
			address->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '-')
		{
			/* Address bit is already 0. */
			/* Mask bit is already 0. */
			buf_index--;
		}
		else
		{
			assert(0);
			dagutil_error("invalid character '%c' in IP address specification\n", token[index]);
			return 0;
		}
	}

	assert(buf_index == -1);

	address->ip4_addr = htonl(address->ip4_addr);
	address->mask = htonl(address->mask);

	if (buf_index == -1)
	{
		return 1;
	}

	return 0;
}


/**
 * Create port entry (16-bit entry)
 *
 * Convert the ASCII representation to binary form, using the
 * don't-care bits when creating the corresponding mask.
 */
static unsigned int
create_port_entry(cam_port_t* port, const char* token)
{
	unsigned int count = strlen(token);
	unsigned int index;
    int buf_index = 15;

	/* Create the entry in host byte order then convert to network byte order. */
	memset(port, 0, sizeof(cam_port_t));

	for (index = 0; index < count; index++)
	{
		if ((token[index] == '{') || (token[index] == '}'))
		{
			/* Do nothing. */
		}
		else if (token[index] == '0')
		{
			/* Port bit is already 0. */

			/* Set mask bit to 1. */
			port->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '1')
		{
			/* Set port bit to 1. */
			port->port |= (1 << buf_index);

			/* Set mask bit to 1. */
			port->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '-')
		{
			/* Port bit is already 0. */
			/* Mask bit is already 0. */
			buf_index--;
		}
		else
		{
			assert(0);
			dagutil_error("invalid character '%c' in port specification\n", token[index]);
			return 0;
		}
	}

	assert(buf_index == -1);

	port->port = htons(port->port);
	port->mask = htons(port->mask);

	if (buf_index == -1)
	{
		return 1;
	}

	return 0;
}

/**
 * Create TCP flags entry (8-bit entry)
 *
 * Convert the ASCII representation to binary form, using the
 * don't-care bits when creating the corresponding mask.
 */
static unsigned int
create_tcp_flags_entry(tcp_flags_t* flags, const char* token)
{
	unsigned int count = strlen(token);
	unsigned int index;
    int buf_index = 7;

	memset(flags, 0, sizeof(tcp_flags_t));

	for (index = 0; index < count; index++)
	{
		if ((token[index] == '{') || (token[index] == '}'))
		{
			/* Do nothing. */
		}
		else if (token[index] == '0')
		{
			/* Flags bit is already 0. */

			/* Set mask bit to 1. */
			flags->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '1')
		{
			/* Set flags bit to 1. */
			flags->flags |= (1 << buf_index);

			/* Set mask bit to 1. */
			flags->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '-')
		{
			/* Flags bit is already 0. */
			/* Mask bit is already 0. */
			buf_index--;
		}
		else
		{
			assert(0);
			dagutil_error("invalid character '%c' in TCP flags specification\n", token[index]);
			return 0;
		}
	}

	assert(buf_index == -1);
	if (buf_index == -1)
	{
		return 1;
	}

	return 0;
}
/**
 * Create ip protocol entry (8-bit entry)
 *
 * Convert the ASCII representation to binary form, using the
 * don't-care bits when creating the corresponding mask.
 */
static unsigned int
create_ip_protocol_entry(protocol_t* proto, const char* token)
{
	unsigned int count = strlen(token);
	unsigned int index;
        int buf_index = 7;

	/* Create the entry in host byte order then convert to network byte order. */
	memset(proto, 0, sizeof(protocol_t));

	for (index = 0; index < count; index++)
	{
		if ((token[index] == '{') || (token[index] == '}'))
		{
			/* Do nothing. */
		}
		else if (token[index] == '0')
		{
			/* Port bit is already 0. */

			/* Set mask bit to 1. */
			proto->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '1')
		{
			/* Set protocol bit to 1. */
			proto->protocol|= (1 << buf_index);

			/* Set mask bit to 1. */
			proto->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '-')
		{
			/* Port bit is already 0. */
			/* Mask bit is already 0. */
			buf_index--;
		}
		else
		{
			assert(0);
			dagutil_error("invalid character '%c' in ip-proto specification\n", token[index]);
			return 0;
		}
	}

	assert(buf_index == -1);

	proto->protocol= htons(proto->protocol);
	proto->mask = htons(proto->mask);

	if (buf_index == -1)
	{
		return 1;
	}

	return 0;
}

/**
 * Create l2 protocol entry (16-bit entry)
 *
 * Convert the ASCII representation to binary form, using the
 * don't-care bits when creating the corresponding mask.
 */
static unsigned int
create_l2_protocol_entry(l2_protocol_t* proto, const char* token)
{
	unsigned int count = strlen(token);
	unsigned int index;
        int buf_index = 15;

	/* Create the entry in host byte order then convert to network byte order. */
	memset(proto, 0, sizeof(l2_protocol_t));

	for (index = 0; index < count; index++)
	{
		if ((token[index] == '{') || (token[index] == '}'))
		{
			/* Do nothing. */
		}
		else if (token[index] == '0')
		{
			/* Port bit is already 0. */

			/* Set mask bit to 1. */
			proto->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '1')
		{
			/* Set protocol bit to 1. */
			proto->protocol|= (1 << buf_index);

			/* Set mask bit to 1. */
			proto->mask |= (1 << buf_index);

			buf_index--;
		}
		else if (token[index] == '-')
		{
			/* Port bit is already 0. */
			/* Mask bit is already 0. */
			buf_index--;
		}
		else
		{
			assert(0);
			dagutil_error("invalid character '%c' in l2-proto specification\n", token[index]);
			return 0;
		}
	}

	assert(buf_index == -1);

	proto->protocol= htons(proto->protocol);
	proto->mask = htons(proto->mask);

	if (buf_index == -1)
	{
		return 1;
	}

	return 0;
}
