/**********************************************************************
*
* Copyright (c) 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: dagixp_filter_loader.c 4209 2006-04-12 08:30:14Z ben $
*
**********************************************************************/

/**********************************************************************
* 
* DESCRIPTION:  Utility application for loading a ruleset into a DAG 7.1S
*               card.
* 
*               Refer to the IXP Filter loader document for more details
*
**********************************************************************/


/* System headers */
#include "dag_platform.h"
#include <assert.h>

/* LibXML2 header */
#include <libxml/parser.h>
#include <libxml/tree.h>


/* Endace headers */
#include "dagapi.h"
#include "dagutil.h"
#include "dagclarg.h"
#include "dagema.h"
#include "dag_config_api.h"

/* dagixp filter headers */
#include "dagixp_filter.h"


/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused))  = "$Id: dagixp_filter_loader.c 4209 2006-04-12 08:30:14Z ben $";
static const char* const kRevisionString = "$Revision: 4209 $";



/*--------------------------------------------------------------------
 
  CONSTANTS

---------------------------------------------------------------------*/

#ifndef _WIN32
#define DEFAULT_DEVICE  "/dev/dag0"     /* default device                    */
#else /* _WIN32 */
#define DEFAULT_DEVICE  "dag0"          /* default device                    */
#endif /* _WIN32 */


#if !defined(MAX_PATH)
	#define MAX_PATH       260
#endif



#if !defined(IPPROTO_SCTP)
	#define IPPROTO_SCTP  132
#endif


/* error codes past to the xml_warning() function */
#define EUNKNOWNELEM              0x10001
#define EMISSINGATTR              0x10002




/*--------------------------------------------------------------------
 
  XMLTAG HASH VALUES

---------------------------------------------------------------------*/

#define XMLHASH_RULESET           779
#define XMLHASH_RULE              444
#define XMLHASH_IPV6              393
#define XMLHASH_IPV4              391
#define XMLHASH_IPSOURCE          928
#define XMLHASH_IPDEST            701
#define XMLHASH_ADDR              415
#define XMLHASH_MASK              432
#define XMLHASH_TCP               330
#define XMLHASH_UDP               332
#define XMLHASH_SCTP              446
#define XMLHASH_ICMP              429
#define XMLHASH_PORT              457
#define XMLHASH_TYPE              454
#define XMLHASH_IPV6FLOW          883
#define XMLHASH_FLOW              444

#define XMLHASH_SOURCEPORTLIST    1660
#define XMLHASH_DESTPORTLIST      1433
#define XMLHASH_MAX               329
#define XMLHASH_MIN               327
#define XMLHASH_RANGE             530
#define XMLHASH_BITMASK           754
#define XMLHASH_VALUE             546




/*--------------------------------------------------------------------
 
  COMMANDLINE OPTIONS

---------------------------------------------------------------------*/

enum
{
	CLA_HELP,
	CLA_VERBOSE,
	CLA_VERSION,
	CLA_DEVICE,
	CLA_STATS,
	CLA_STATSINTERVAL,
	CLA_DONTDOWNLOAD,
	CLA_RESETIXP,
	CLA_XSIMEMTEST,
	CLA_CPPMEMTEST,
	CLA_INPUTFILE,
	CLA_DONTCLEARSTATS,
	CLA_VALIDATEFILE,
	CLA_VALIDATESTREAM,
	CLA_VALIDATEAUTOSTREAM
};






/*--------------------------------------------------------------------
 
  PROTOTYPES

---------------------------------------------------------------------*/

static int
parse_ruleset_file (RulesetH ruleset_h, const char * filename);

static void
display_stats (int dagfd, int stats_interval);




/*--------------------------------------------------------------------
 
 FUNCTION:      print_usage
 
 DESCRIPTION:   
 
 PARAMETERS:    data            IN      Pointer to the entry being removed

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
print_version(void)
{
	printf("dagixp_filter_loader (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}

static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagixp_filter_loader - loads a ruleset into a DAG7.1S card\n"
		"Usage : dagixp_filter_loader -d <device> [options] -f <config file>\n");
	dagclarg_display_usage(clarg, stdout);
}	



/*--------------------------------------------------------------------
 
 FUNCTION:      verbose_restart_handler
 
 DESCRIPTION:   Callback function that displays the status of the restart
                process.
 
 PARAMETERS:    stage           IN      The stage of the restart process

 RETURNS:       Always returns 1.

 HISTORY:       

---------------------------------------------------------------------*/
int verbose_restart_handler(uint32_t stage)
{
	if ( dagutil_get_verbosity() == 0 )
		return 0;
	

	switch (stage)
	{
		case EMA_RST_INIT:
			dagutil_verbose (" * Starting the IXP restart process.\n");
			break;

		case EMA_RST_BOOTLOADER_STARTED:
			dagutil_verbose (" * Bootloader has started.\n");
			break;

		case EMA_RST_MEMORY_INIT:
			dagutil_verbose (" * Initialising one of the memory banks.\n");
			break;

		case EMA_RST_STARTING_MEM_TEST:
			dagutil_verbose (" * Starting a memory test.\n");
			break;

		case EMA_RST_FINISHED_MEM_TEST:
			dagutil_verbose (" * Finished memory test.\n");
			break;

		case EMA_RST_KERNEL_BOOTED:
			dagutil_verbose (" * IXP Processor is up and running.\n");
			break;

		case EMA_RST_DRIVER_STARTED:
			dagutil_verbose (" * IXP messaging driver has started.\n");
			break;

		case EMA_RST_COMPLETE:
			dagutil_verbose (" * Restart process complete successfully.\n");
			break;
	}

	return 0;
}


/*--------------------------------------------------------------------
 
 FUNCTION:      main
 
 DESCRIPTION:   
 
 PARAMETERS:    data            IN      Pointer to the entry being removed

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
int
main(int argc, char *argv[])
{ 
	ClArgPtr        clarg = NULL;
	char            dagname_buf[DAGNAME_BUFSIZE];
	char            dagname[DAGNAME_BUFSIZE];
	char            in_filename[MAX_PATH];
	int             argindex = 0;
	int             clarg_result = 0;
	int             code = 0;
	int             dagstream;
	daginf_t*       daginf;
	RulesetH        ruleset_h = NULL;
	int             restart_ixp = 0;
	uint32_t        restart_flags = 0;
	int             dagfd = -1;
	int             clear_stats = 1;
	int             print_stats = 0;
	int             stats_interval = -1;
	int             download_to_card = 1;
	int             retval = EXIT_FAILURE;
	dag_card_ref_t  card_ref;
	dag_component_t root_component;
	dag_component_t erfmux;
	attr_uuid_t     line_steering_attr;
	attr_uuid_t     ixp_steering_attr;
	attr_uuid_t     host_steering_attr;
	
	
	

	dagutil_set_progname("dagixp_filter_loader");

	
	printf("\nEndace DAG7.1S Filter Loader\n");
	printf("(c) 2006 Endace Technology Ltd.\n\n");

	
	
	/* set the default dag card */
	strcpy(dagname, DEFAULT_DEVICE);
	
	
	/* clean the buffers */
	memset (in_filename,  0, MAX_PATH);


	/* Set up the command line options. */
	clarg = dagclarg_init(argc, (const char* const *) argv);


	/* General options. */
	dagclarg_add(clarg, "Display help (this page).", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');
	
	dagclarg_add(clarg, "Increase verbosity.", "--verbose", 'v', CLA_VERBOSE);

	dagclarg_add_string(clarg, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add_string(clarg, "File containing the ruleset to load.", "--rule_file", 'f', "filename", in_filename, MAX_PATH, CLA_INPUTFILE);
	dagclarg_add       (clarg, "Don't download the ruleset to the card, if not\n"
		                       "                                 specified the ruleset is always downloaded to\n"
							   "                                 the card.", "--do_not_download", 'y', CLA_DONTDOWNLOAD);
	dagclarg_add       (clarg, "Print filtering statistics.", "--statistics", 's', CLA_STATS);
	dagclarg_add_int   (clarg, "Interval to repeat statistics in seconds.", "--interval", 'i', "seconds", &stats_interval, CLA_STATSINTERVAL);
	dagclarg_add       (clarg, "Don't clear the previous statistics.", "--do_not_clear_stats", 'u', CLA_DONTCLEARSTATS);
	dagclarg_add       (clarg, "Restart the IXP processor on the card.", "--restart", 'x', CLA_RESETIXP);
	dagclarg_add       (clarg, "Run CPP memory tests on the card, ignored if\n"
		                       "                                 --restart is not also specified.", "--cpp_memtest", 'm', CLA_CPPMEMTEST); 
	dagclarg_add       (clarg, "Run XSI memory tests on the card, ignored if\n"
		                       "                                 --restart is not also specified.", "--xsi_memtest", 'n', CLA_XSIMEMTEST); 
	dagclarg_add       (clarg, "Display version information.", "--version.", 'V', CLA_VERSION);
	



	/* Parse the command line options. */
	clarg_result = dagclarg_parse(clarg, NULL, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg); 
				return EXIT_SUCCESS;
				break;
			
			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				break;

			case CLA_DEVICE:
				if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
				{
					dagutil_verbose("device=%s\n", dagname);
					dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
				}
				break;

			case CLA_STATS:
				print_stats = 1;
				break;

			case CLA_STATSINTERVAL:
				if ( stats_interval <= 0 )
					stats_interval = 1;
				break;
			
			case CLA_DONTCLEARSTATS:
				clear_stats = 0;
				break;

			case CLA_RESETIXP:
				restart_ixp = 1;
				break;
			
			case CLA_CPPMEMTEST:
				restart_flags |= EMA_RUN_CPP_DRAM_MEMORY_TEST;
				break;

			case CLA_XSIMEMTEST:
				restart_flags |= EMA_RUN_DRAM_MEMORY_TEST;
				break;

			case CLA_DONTDOWNLOAD:
				download_to_card = 0;
				break;
			
			case CLA_INPUTFILE:
				break;

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

			default:
				if (argv[argindex][0] == '-')
				{
					/* Unknown option. */
					dagutil_error("unknown option %s\n", argv[argindex]);
					print_usage(clarg);
					return EXIT_FAILURE;
				}
				break;
		}
		
		clarg_result = dagclarg_parse(clarg, NULL, &argindex, &code);
		if ( (clarg_result < 0) && (CLA_STATSINTERVAL == code) )
		{
			/* No argument found for -i.
			 * As a special case, we permit -i to have no argument and assume a default of 1. 
			 */
			stats_interval = 1;
			clarg_result = 1;
		}
	}

	if (-1 == clarg_result)
	{
		/* Error occuried */
		if (argindex < argc)
		{
			dagutil_error("while processing option %s\n", argv[argindex]);
		}

		dagclarg_display_usage(clarg, stderr);
		return EXIT_FAILURE;
	}


	/* Check if a filename was specified, if not then clear the download to card flag */
	if ( in_filename[0] == '\0' )
		download_to_card = 0;
	

	/* Check if we need to open a handle to the DAG card (we may not if the rule file isn't being downloaded) */
	if ( print_stats || restart_ixp || download_to_card )
	{
		/* Open the DAG card */
		if ( (dagfd = dag_open(dagname)) == -1 )
		{
			dagutil_error ("dag_open(\"%s\") failed\n", dagname);
			goto Exit;
		}

		/* Sanity check, that we are actually using a 7.1s card */
		daginf = dag_info(dagfd);
		if (daginf == NULL)
		{
			dagutil_error ("dag_info %s: %s\n", dagname, strerror(errno));
			goto Exit;
		}
		if (daginf->device_code != PCI_DEVICE_ID_DAG7_10)
		{
			dagutil_error ("%s is not a DAG7.1s card.\n", dagname);
			goto Exit;
		}
	}



	/* Restart the IXP on the card if asked to */
	if ( restart_ixp )
	{
		dagutil_verbose_level (0, "Restarting the IXP processor on the DAG card, please wait this may take some time.\n");
		if ( dagema_reset_processor_with_cb(dagfd, restart_flags, verbose_restart_handler) == -1 )
		{
			dagutil_error ("failed to restart the IXP processor (error code %d).\n", dagema_get_last_error());
			goto Exit;
		}
	}



	/* Initialise the dagixp filter library */
	if ( dagixp_filter_startup() != 0 )
	{
		dagutil_error ("Failed to initialise the dagixp_filter library (errorcode: %d).\n", dagixp_filter_get_last_error());
		return EXIT_FAILURE;
	}
		


	
	/* Check whether a file has been specified, if so parse it */
	if ( in_filename[0] != '\0' )
	{
		/* Initialise a ruleset */
		if ( (ruleset_h = dagixp_filter_create_ruleset()) == NULL )
		{
			dagutil_error ("Failed to create a new ruleset (errorcode: %d).\n", dagixp_filter_get_last_error());
			goto Exit;
		}
		
		
		/* Parse the rule file */
		if ( parse_ruleset_file(ruleset_h, in_filename) == -1 )
		{
			dagutil_error ("Failed to parse the rules file.\n");
			goto Exit;
		}
		if ( download_to_card == 0 )
		{
			printf ("Ruleset file parsed successifully.\n");
		}
		
		
		/* Check if we should download the ruleset to the card */
		if ( download_to_card == 1 )
		{
			/* This is a temporary fix for a firmware feature, it sends a tx reset to enable packets
			* to flow from the IXP to the line. 
			*/
			if ( dag_attach_stream(dagfd, 1, 0, 8192) >= 0 )
			{
				if ( dag_start_stream(dagfd, 1) >= 0 )
					dag_stop_stream (dagfd, 1);

				dag_detach_stream (dagfd, 1);
			}




			/* Configure the ERF MUX to route packet records:
			*   line -> IXP
			*   IXP  -> host or line (based on the direction bit)
			*   host -> IXP
			*/
			card_ref = dag_config_init (dagname);
			if ( card_ref == NULL )
			{
				dagutil_error ("dag_config_init failed\n");
				goto Exit;
			}

			root_component = dag_config_get_root_component (card_ref);
			if ( root_component == NULL )
			{
				dag_config_dispose (card_ref);
				dagutil_error ("dag_config_get_root_component failed\n");
				goto Exit;
			}

			erfmux = dag_component_get_subcomponent (root_component, kComponentErfMux, 0);
			line_steering_attr = dag_component_get_attribute_uuid (erfmux, kUint32AttributeLineSteeringMode);
			ixp_steering_attr  = dag_component_get_attribute_uuid (erfmux, kUint32AttributeIXPSteeringMode);
			host_steering_attr = dag_component_get_attribute_uuid (erfmux, kUint32AttributeHostSteeringMode);
		
			/* Steer the packets from the line to the IXP */
			dag_config_set_uint32_attribute (card_ref, line_steering_attr, kSteerIXP);
			assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, line_steering_attr));
			
			/* Use the direction bits in the packet record to determine the steering of the packet */
			dag_config_set_uint32_attribute (card_ref, ixp_steering_attr, kSteerDirectionBit);
			assert (kSteerDirectionBit == dag_config_get_uint32_attribute (card_ref, ixp_steering_attr));

			/* Steer packets from the host to the IXP */
			dag_config_set_uint32_attribute (card_ref, host_steering_attr, kSteerIXP);
			assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, host_steering_attr));

			

			dag_config_dispose (card_ref);



			/* Open a connection to the XSCale core */
			dagutil_verbose ("Opening a connection to the DAG card.\n");
			if (dagema_open_conn(dagfd) != 0)
			{
				dagutil_error ("failed to open a connection to the EMA (error code %d).\n", dagema_get_last_error());
				goto Exit;
			}

			/* Clean all the rulesets prior to use */
			dagutil_verbose ("Clearing any existing rulesets in the DAG card.\n");
			if (dagixp_filter_clear_iface_rulesets(dagfd, kAllIntefaces) == -1)
			{
				dagutil_error ("failed to clear all rulesets (error code %d).\n", dagixp_filter_get_last_error());
				dagema_close_conn (dagfd, 0);
				goto Exit;
			}
			
			/* Clear the current statistics */
			if (clear_stats == 1)
			{
				dagutil_verbose ("Clearing all filtering statistics.\n");
				if (dagixp_filter_hw_reset_filter_stat(dagfd, kAllStatistics) == -1)
				{
					dagutil_error ("failed to clear all the statistics (error code %d)\n", dagixp_filter_get_last_error());
					dagema_close_conn (dagfd, 0);
					goto Exit;
				}
			}

			

			/* Download the ruleset to the card */
			dagutil_verbose ("Downloading the ruleset to the card.\n");
			if (dagixp_filter_download_ruleset(dagfd, 0, ruleset_h) == -1)
			{
				dagutil_error ("failed to download the ruleset (error code %d)\n", dagixp_filter_get_last_error());
				dagema_close_conn (dagfd, 0);
				goto Exit;
			}
				
			/* Activate the ruleset on the card */
			dagutil_verbose ("Activating the ruleset.\n");
			if (dagixp_filter_activate_ruleset(dagfd, 0, ruleset_h) == -1)
			{
				dagutil_error ("failed to active the ruleset (error code %d)\n", dagixp_filter_get_last_error());
				dagema_close_conn (dagfd, 0);
				goto Exit;
			}

			/* Close the connection to the card */
			dagema_close_conn (dagfd, 0);
		}
		

		/* We can now delete the ruleset to free up some memory */
		if ( ruleset_h != NULL )
		{
			dagixp_filter_delete_ruleset (ruleset_h);
			ruleset_h = NULL;
		}

	}



	/* Display the current statistics if asked to by the user */
	if ( print_stats )
	{
		display_stats (dagfd, stats_interval);
	}

	retval = EXIT_SUCCESS;



	
	/* Clean up */
Exit:
	
	if ( ruleset_h != NULL )
		dagixp_filter_delete_ruleset (ruleset_h);
	
	dagixp_filter_shutdown ();

	if ( dagfd != -1 )
		dag_close (dagfd);
	
	return retval;
}



/*--------------------------------------------------------------------
 
 FUNCTION:      display_stats
 
 DESCRIPTION:   Displays the DSM counter values on stdout.
 
 PARAMETERS:    dagfd           IN      File handle to the dag card
                config_h        IN      Handle to the virtual configuration
                                        created for the card.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
display_stats (int dagfd, int stats_interval)
{
	uint32_t  recv_low, recv_high;
	uint32_t  accepted_low, accepted_high;
	uint32_t  in_play;
	int       stat;
	uint32_t  stats[kMsfErrStatusWord-kPacketsDroppedErfType+1];




	/* Open another connection to the XSCale core */
	dagutil_verbose ("Opening a connection to the DAG card.\n");
	if (dagema_open_conn(dagfd) != 0)
	{
		dagutil_error ("failed to open a connection to the EMA (error code %d).\n", dagema_get_last_error());
		return;
	}



	/* Display the stats every so many seconds */
	do
	{

		/* read the number of packets received */
		if ( dagixp_filter_hw_get_filter_stat(dagfd, kPacketsRecv, &recv_low, &recv_high) != 0 )
		{
			dagutil_error ("failed to get the statistic %d (error code %d)\n", kPacketsRecv, dagixp_filter_get_last_error());
			return;
		}

		/* read the number of packets accepted */
		if ( dagixp_filter_hw_get_filter_stat(dagfd, kPacketsAccepted, &accepted_low, &accepted_high) != 0 )
		{
			dagutil_error ("failed to get the statistic %d (error code %d)\n", kPacketsAccepted, dagixp_filter_get_last_error());
			return;
		}

		/* read the number of packets in play */
		if ( dagixp_filter_hw_get_filter_stat(dagfd, kPacketsInPlay, &in_play, NULL) != 0 )
		{
			dagutil_error ("failed to get the statistic %d (error code %d)\n", kPacketsInPlay, dagixp_filter_get_last_error());
			return;
		}


		/* Display the standard statistics */
		printf ("  Packets Recv: %-16"PRIu64" Packets Accepted: %-16"PRIu64" In Play: %u\n",
				((((uint64_t)recv_high)     << 32) + ((uint64_t)recv_low)),
				((((uint64_t)accepted_high) << 32) + ((uint64_t)accepted_low)),
				(unsigned int)in_play); 




		/* dump out the detailed statistics */	
		if (dagutil_get_verbosity() > 0)
		{
			/* read the number of packets recieved */
			for (stat=kPacketsDroppedErfType; stat<=kMsfErrStatusWord; stat++)
				if ( dagixp_filter_hw_get_filter_stat(dagfd, stat, &(stats[stat - kPacketsDroppedErfType]), NULL) != 0 )
				{
					dagutil_error ("failed to get the statistic %d (error code %d)\n", stat, dagixp_filter_get_last_error());
					return;
				}

			printf ("\n  Invalid ERF type:        %-10u Invalid/unknown HDLC header: %-10u\n", (unsigned int)stats[0], (unsigned int)stats[1]); 
			printf ("  Invalid ERF size:        %-10u Buffer limit reached:        %-10u\n", (unsigned int)stats[2], (unsigned int)stats[3]); 
			printf ("  RX Fifo Full:            %-10u TX Fifo Full:                %-10u\n", (unsigned int)stats[4], (unsigned int)stats[5]); 
			printf ("  Unknown IPv6 Ext Header: %-10u MPLS Stack Overflow:         %-10u\n", (unsigned int)stats[6], (unsigned int)stats[9]); 
			printf ("  Sequence Buffer Full:    %-10u Packet to Large:             %-10u\n", (unsigned int)stats[7], (unsigned int)stats[8]); 
			printf ("  MSF Error SOP:           %-10u MSF Error EOP:               %-10u\n", (unsigned int)stats[10], (unsigned int)stats[11]); 
			printf ("  MSF Error BOP:           %-10u MSF Error NULL Packet:       %-10u\n", (unsigned int)stats[12], (unsigned int)stats[13]); 
			printf ("  Invalid MSF Status Word: %-10u\n", (unsigned int)stats[14]); 
			printf ("--------------------------------------------------------------------------------\n\n");
		}
	
		/* sleep for the next iteration */
		if ( stats_interval > 0 )
			sleep (stats_interval);

	} while (stats_interval > 0);


	/* Close the connection to the card again */
	dagema_close_conn (dagfd, 0);

}




/*--------------------------------------------------------------------
 
 FUNCTION:      simple_str_hash
 
 DESCRIPTION:   Very simple hash function, used for transversing the 
                XML document.
 
 PARAMETERS:    str             IN      String to create a hash for

 RETURNS:       The hash of the string

 HISTORY:       

---------------------------------------------------------------------*/
static int
simple_str_hash (const xmlChar * str)
{
	int len;
	int hash;
	
	assert(str);
	
	len = hash = xmlStrlen(str);
	
	if ( len > 0 )
		while (len-- > 0)
			hash += (int) str[len];
		
	return hash;
}




/*--------------------------------------------------------------------
 
 FUNCTION:      xml_warning
 
 DESCRIPTION:   Displays an error message corresponding to the supplied
                error code and element.
 
 PARAMETERS:    errorcode         IN      Error code that indicates what
                                          type of message to display.
                node              IN      Pointer to the offending node.

 RETURNS:       The hash of the string

 HISTORY:       

---------------------------------------------------------------------*/
static void
xml_warning (int errorcode, const xmlNodePtr node)
{
	switch (errorcode)
	{
		case EUNKNOWNELEM:
			dagutil_warning ("Unknown or unexpected element \'<%s>\' on line %d.\n", node->name, node->line);
			break;
		
		case EMISSINGATTR:
			dagutil_warning ("Missing reuired attribute for element \'<%s>\' on line %d.\n", node->name, node->line);
			break;
			
	}
}





/*--------------------------------------------------------------------
 
 FUNCTION:      process_icmp_type_bitmask_element
 
 DESCRIPTION:   Process a <bitmask> element and it's two child nodes
                <value> and <mask>.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <bitmask> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with the new fields.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_icmp_type_bitmask_element (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h)
{
	xmlNodePtr     child_node;
	int            hash;
	xmlChar *      attr;
	xmlChar *      contents;
	int            base;
	uint8_t        type = 0x00;
	uint8_t        mask = 0xFF;
	
	
	
	/* loop through the child elements, which should only be <type> or <mask> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;


		/* determine the type of the element */
		hash = simple_str_hash (child_node->name);
		if ( (hash != XMLHASH_VALUE) && (hash != XMLHASH_MASK) )
		{	
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}

		
		/* get the 'hex' attribute value */
		attr = xmlGetProp (child_node, BAD_CAST"hex");
		if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
			base = 10;
		else
			base = 16;
		if (attr != NULL)   xmlFree (attr);
		
		
		
		/* get the text contents */
		contents = xmlNodeListGetString (doc, child_node->xmlChildrenNode, 1);
	
		if ( hash == XMLHASH_VALUE )
			type = (uint8_t) strtoul ((char*)contents, NULL, base);
		else
			mask = (uint8_t) strtoul ((char*)contents, NULL, base);
		
		xmlFree (contents);
	}

	
	dagutil_verbose_level (3, "             icmp type value:%d [0x%02X] mask:%d [0x%02X]\n", 
	                          type, type, mask, mask);
	
	
	/* add the port rnage to the rule */
	dagixp_filter_add_icmp_type_bitmask (rule_h, type, mask);

}




/*--------------------------------------------------------------------
 
 FUNCTION:      process_icmp_type_range_element
 
 DESCRIPTION:   Process a <range> element and it's two required 
                child nodes <min> and <max>.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <range> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with the new fields.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_icmp_type_range_element (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h)
{
	xmlNodePtr     child_node;
	int            hash;
	xmlChar *      attr;
	xmlChar *      contents;
	int            base;
	int32_t        min_type = -1;
	int32_t        max_type = -1;
	
	
	
	/* loop through the child elements, which should only be <min-type> or <max-type> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;


		/* determine the type of the element */
		hash = simple_str_hash (child_node->name);
		if ( (hash != XMLHASH_MAX) && (hash != XMLHASH_MIN) )
		{	
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}

		
		
		
		/* get the 'hex' attribute value */
		attr = xmlGetProp (child_node, BAD_CAST"hex");
		if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
			base = 10;
		else
			base = 16;
		if (attr != NULL)   xmlFree (attr);
		
		
		
		/* get the text contents */
		contents = xmlNodeListGetString (doc, child_node->xmlChildrenNode, 1);
	
		if ( hash == XMLHASH_MIN )
			min_type = (uint8_t) strtoul ((char*)contents, NULL, base);
		else
			max_type = (uint8_t) strtoul ((char*)contents, NULL, base);
		
		xmlFree (contents);
	}

	
	/* check both the min and max value where set */
	if ( min_type == -1 || max_type == -1 )
	{
		dagutil_warning ("missing <min> or <max> element in the <range> element on line %d.\n", cur_node->line);
		return;
	}
		

	dagutil_verbose_level (3, "             icmp type min:%d [0x%02X] max:%d [0x%02X]\n", 
	                          min_type, min_type, max_type, max_type);

	
	/* add the port rnage to the rule */
	dagixp_filter_add_icmp_type_range (rule_h, (uint8_t)min_type, (uint8_t)max_type);

}






/*--------------------------------------------------------------------
 
 FUNCTION:      process_icmp_type_single_element
 
 DESCRIPTION:   Process a <type> element that is a member a
                <icmp-type-list>.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <type> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with the new fields.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_icmp_type_single_element (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h)
{
	xmlChar * attr;
	xmlChar * contents;
	int       base;
	uint8_t   type;
	
	
	/* get the 'hex' attribute value */
	attr = xmlGetProp (cur_node, BAD_CAST"hex");
	if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
		base = 10;
	else
		base = 16;
	if (attr != NULL)   xmlFree (attr);
	
	
	
	/* get the text contents */
	contents = xmlNodeListGetString (doc, cur_node->xmlChildrenNode, 1);

	type = (uint8_t) strtoul ((char*)contents, NULL, base);
	
	xmlFree (contents);

	dagutil_verbose_level (3, "             icmp type %d [0x%02X]\n", type, type);
	
	dagixp_filter_add_icmp_type_range (rule_h, type, type);
		
}




/*--------------------------------------------------------------------
 
 FUNCTION:      process_icmp_type_elements
 
 DESCRIPTION:   Process all the elements inside a <icmp> node. Currently
                the only element allowed as a child is thee <icmp-type-list>
                node.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <icmp> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with then new fields.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_icmp_type_elements (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h)
{
	xmlNodePtr     child_node;
	xmlNodePtr     list_node;
	
	
	
	/* loop through the child elements, which should only be <addr> or <mask> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;


		/* determine the type of the element */
		if ( !xmlStrEqual(child_node->name, BAD_CAST"icmp-type-list") )
		{	
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}
			
		
		/* iterate through the port list entries */
		for (list_node=child_node->children; list_node; list_node = list_node->next)
		{
			if (list_node->type != XML_ELEMENT_NODE)
				continue;
			
			
			switch (simple_str_hash(list_node->name))
			{
				case XMLHASH_TYPE:
					process_icmp_type_single_element (doc, list_node, rule_h);
					break;
				
				case XMLHASH_RANGE:
					process_icmp_type_range_element (doc, list_node, rule_h);
					break;
				
				case XMLHASH_BITMASK:
					process_icmp_type_bitmask_element (doc, list_node, rule_h);
					break;
				
				default:
					xml_warning (EUNKNOWNELEM, list_node);
			}
		}

		
			
	}
		

}






/*--------------------------------------------------------------------
 
 FUNCTION:      process_port_bitmask_element
 
 DESCRIPTION:   Process a <bitmask> element and it's two child nodes
                <value> and <mask>.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <bitmask> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with the new fields.
                target           IN      Will be either XMLHASH_SOURCEPORTLIST
                                         or XMLHASH_DESTPORTLIST depending
										 on the type of the parent.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_port_bitmask_element (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h, int target)
{
	xmlNodePtr     child_node;
	int            hash;
	xmlChar *      attr;
	xmlChar *      contents;
	int            base;
	uint16_t       port = 0x0000;
	uint16_t       mask = 0xFFFF;
	
	
	
	/* loop through the child elements, which should only be <port> or <mask> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;


		/* determine the type of the element */
		hash = simple_str_hash (child_node->name);
		if ( (hash != XMLHASH_VALUE) && (hash != XMLHASH_MASK) )
		{	
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}

		
		/* get the 'hex' attribute value */
		attr = xmlGetProp (child_node, BAD_CAST"hex");
		if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
			base = 10;
		else
			base = 16;
		if (attr != NULL)   xmlFree (attr);
		
		
		
		/* get the text contents */
		contents = xmlNodeListGetString (doc, child_node->xmlChildrenNode, 1);
	
		if ( hash == XMLHASH_VALUE )
			port = (uint16_t) strtoul ((char*)contents, NULL, base);
		else
			mask = (uint16_t) strtoul ((char*)contents, NULL, base);
		
		xmlFree (contents);
	}

	
	/*  */
	dagutil_verbose_level (3, "             %s port value:%d [0x%04X] mask:%d [0x%04X]\n", 
	                          (target == XMLHASH_SOURCEPORTLIST) ? "source" : "destination",
	                          port, port, mask, mask);

		
	/* add the port rnage to the rule */
	if ( target == XMLHASH_SOURCEPORTLIST )
		dagixp_filter_add_source_port_bitmask (rule_h, port, mask);
	else
		dagixp_filter_add_dest_port_bitmask (rule_h, port, mask);
		

}




/*--------------------------------------------------------------------
 
 FUNCTION:      process_port_range_element
 
 DESCRIPTION:   Process a <range> element and it's two required 
                child nodes <min> and <max>.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <range> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with the new fields.
                target           IN      Will be either XMLHASH_SOURCEPORTLIST
                                         or XMLHASH_DESTPORTLIST depending
										 on the type of the parent.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_port_range_element (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h, int target)
{
	xmlNodePtr     child_node;
	int            hash;
	xmlChar *      attr;
	xmlChar *      contents;
	int            base;
	int32_t        min_port = -1;
	int32_t        max_port = -1;
	
	
	
	/* loop through the child elements, which should only be <max-port> or <min-port> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;


		/* determine the type of the element */
		hash = simple_str_hash (child_node->name);
		if ( (hash != XMLHASH_MAX) && (hash != XMLHASH_MIN) )
		{	
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}

		
		
		
		/* get the 'hex' attribute value */
		attr = xmlGetProp (child_node, BAD_CAST"hex");
		if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
			base = 10;
		else
			base = 16;
		if (attr != NULL)   xmlFree (attr);
		
		
		
		/* get the text contents */
		contents = xmlNodeListGetString (doc, child_node->xmlChildrenNode, 1);
	
		if ( hash == XMLHASH_MIN )
			min_port = (uint16_t) strtoul ((char*)contents, NULL, base);
		else
			max_port = (uint16_t) strtoul ((char*)contents, NULL, base);
		
		xmlFree (contents);
	}

	
	/* check both the min and max value where set */
	if ( min_port == -1 || max_port == -1 )
	{
		dagutil_warning ("missing <min> or <max> element in the <range> element on line %d.\n", cur_node->line);
		return;
	}
		
		
	dagutil_verbose_level (3, "             %s port min:%d [0x%04X] max:%d [0x%04X]\n", 
	                          (target == XMLHASH_SOURCEPORTLIST) ? "source" : "destination",
	                          min_port, min_port, max_port, max_port);


	/* add the port rnage to the rule */
	if ( target == XMLHASH_SOURCEPORTLIST )
		dagixp_filter_add_source_port_range (rule_h, (uint16_t)min_port, (uint16_t)max_port);
	else
		dagixp_filter_add_dest_port_range (rule_h, (uint16_t)min_port, (uint16_t)max_port);
		

}






/*--------------------------------------------------------------------
 
 FUNCTION:      process_port_single_element
 
 DESCRIPTION:   Process a <port> element that is a member in either a
                <source-port-list> or a <dest-port-list>.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <port> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with the new fields.
                target           IN      Will be either XMLHASH_SOURCEPORTLIST
                                         or XMLHASH_DESTPORTLIST depending
										 on the type of the parent.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_port_single_element (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h, int target)
{
	xmlChar * attr;
	xmlChar * contents;
	int       base;
	uint16_t  port;
	
	
	/* get the 'hex' attribute value */
	attr = xmlGetProp (cur_node, BAD_CAST"hex");
	if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
		base = 10;
	else
		base = 16;
	if (attr != NULL)   xmlFree (attr);
	
	
	
	/* get the text contents */
	contents = xmlNodeListGetString (doc, cur_node->xmlChildrenNode, 1);

	port = (uint16_t) strtoul ((char*)contents, NULL, base);
	
	xmlFree (contents);

	
	dagutil_verbose_level (3, "             %s port %d [0x%04X]\n", 
	                          (target == XMLHASH_SOURCEPORTLIST) ? "source" : "destination",
	                          port, port);

	
	if ( target == XMLHASH_SOURCEPORTLIST )
		dagixp_filter_add_source_port_range (rule_h, port, port);
	else
		dagixp_filter_add_dest_port_range (rule_h, port, port);

}




/*--------------------------------------------------------------------
 
 FUNCTION:      process_port_elements
 
 DESCRIPTION:   Process all the elements inside either a <tcp>, <udp> or
                <sctp> node. Currently the only elements allowed as a child
                are the <source-port-list> or <dest-port-list> nodes.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <tcp>, <udp> or 
                                         <sctp> elements (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with then new fields.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_port_elements (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h)
{
	xmlNodePtr     child_node;
	xmlNodePtr     list_node;
	int            hash;
	
	
	
	/* loop through the child elements, which should only be <addr> or <mask> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;


		/* determine the type of the element */
		hash = simple_str_hash (child_node->name);
		if ( (hash != XMLHASH_SOURCEPORTLIST) && (hash != XMLHASH_DESTPORTLIST) )
		{	
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}
			
		
		/* iterate through the port list entries */
		for (list_node=child_node->children; list_node; list_node = list_node->next)
		{
			if (list_node->type != XML_ELEMENT_NODE)
				continue;

			switch (simple_str_hash(list_node->name))
			{
				case XMLHASH_PORT:
					process_port_single_element (doc, list_node, rule_h, hash);
					break;
				
				case XMLHASH_RANGE:
					process_port_range_element (doc, list_node, rule_h, hash);
					break;
				
				case XMLHASH_BITMASK:
					process_port_bitmask_element (doc, list_node, rule_h, hash);
					break;
				
				default:
					xml_warning (EUNKNOWNELEM, list_node);
			}
		}

		
			
	}
		

}




/*--------------------------------------------------------------------
 
 FUNCTION:      process_ipv6_flow_element
 
 DESCRIPTION:   Process an <ipv6-flow> element for by examining the two
                child nodes <flow> and <mask>.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML 
                                         ruleset file.
                cur_node         IN      Pointer to the <ipv6-flow> 
                                         element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with then new fields.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_ipv6_flow_element (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h)
{
	xmlNodePtr     child_node;
	int            hash;
	xmlChar *      attr;
	xmlChar *      contents;
	int            base;
	uint32_t       flow;
	uint32_t       mask;
	
	
	/* default values */
	flow = 0x00000;
	mask = 0xFFFFF;
	
	
	/* loop through the child elements, which should only be <max-port> or <min-port> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;


		/* determine the type of the element */
		hash = simple_str_hash (child_node->name);
		if ( (hash != XMLHASH_FLOW) && (hash != XMLHASH_MASK) )
		{	
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}

				
		
		/* get the 'hex' attribute value */
		attr = xmlGetProp (child_node, BAD_CAST"hex");
		if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
			base = 10;
		else
			base = 16;
		if (attr != NULL)   xmlFree (attr);
		
		
		
		/* get the text contents */
		contents = xmlNodeListGetString (doc, child_node->xmlChildrenNode, 1);
	
		if ( hash == XMLHASH_FLOW )
			flow = (uint16_t) strtoul ((char*)contents, NULL, base);
		else
			mask = (uint16_t) strtoul ((char*)contents, NULL, base);
		
		xmlFree (contents);
	}

	
	/* trim the values */
	flow &= 0xFFFFF;
	mask &= 0xFFFFF;
	
		
	dagutil_verbose_level (3, "           flow label value:%d [%05X] mask:%d [%05X]\n", 
	                          flow, flow, mask, mask);


	/* add the port rnage to the rule */
	dagixp_filter_set_ipv6_flow_label_field (rule_h, flow, mask);
	
}




/*--------------------------------------------------------------------
 
 FUNCTION:      process_ipv6_addr_elements
 
 DESCRIPTION:   Process an <ip-source> or <ip-dest> element for ipv6, 
                by looping through the child elements and adding the 
                the mask and value to the rule.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <ip-source> or 
                                         <ip-dest> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with then new fields.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_ipv6_addr_elements (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h)
{
	xmlNodePtr      child_node;
	xmlChar *       attr;
	xmlChar *       key;
	char *          str_contents;
	char *          str_second;
	char *          token;
	int             base;
	char            delim[] = ":";
	uint32_t        is_addr;
	uint16_t        words[8];
	uint16_t        sec_words[8];
	int32_t         pos;
	uint32_t        i;
	uint32_t        is_source;
	struct in6_addr addr;
	struct in6_addr mask;
	
	
	/* check if a source or destination tag */
	if ( xmlStrEqual(cur_node->name, BAD_CAST"ip-source") )
		is_source = 1;
	else
		is_source = 0;
	
	
	
	/* clear the address and mask values */
	memset (&addr, 0x00, sizeof(addr));
	memset (&mask, 0xFF, sizeof(mask));

	
	/* loop through the child elements, which should only be <addr> or <mask> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;

		
		/* get the node name */
		if ( xmlStrEqual(child_node->name, BAD_CAST"addr") )
			is_addr = 1;
		else if ( xmlStrEqual(child_node->name, BAD_CAST"mask") )
			is_addr = 0;
		else
		{
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}
		
		
		/* get the 'hex' attribute value */
		attr = xmlGetProp (child_node, BAD_CAST"hex");
		if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
			base = 10;
		else
			base = 16;
		if (attr != NULL)   xmlFree (attr);


		/* get the text contents and duplicate */
		key = xmlNodeListGetString (doc, child_node->xmlChildrenNode, 1);
		if ( key == NULL )
			continue;

		str_contents = (char*)xmlStrdup (key);
		
		/* free the string allocated */
		xmlFree (key);
		
		
	
		
		
		/* clear the address values before parsing */
		for (i=0; i<8; i++)
			words[i] = sec_words[i] = 0x0000;

		
		/* look for a double : which indicates a series of 0's, if found we split the string into two */
		str_second  = strstr (str_contents, "::");
		if ( str_second != NULL )
		{
			*str_second = '\0';
			str_second += 2;
			
			if ( !isxdigit(*str_second) )
				str_second = NULL;
		}
		
		/* parse the first part of the string */
		pos = 0;
		token = strtok ((char*)str_contents, delim);
		while ( (token != NULL) && (pos < 8) )
		{
			words[pos++] = (uint16_t)strtol (token, NULL, base);
			token = strtok (NULL, delim);
		}
		
		/* parse the second half */
		if ( str_second != NULL && str_second[0] != '\0' )
		{
			pos = 0;
			token = strtok ((char*)str_second, delim);
			while ( (token != NULL) && (pos < 8 ) )
			{
				sec_words[pos++] = (uint16_t)strtoul (token, NULL, base);
				token = strtok (NULL, delim);
			}
			
			for (i=(8-pos); i<8; i++)
				words[i] = sec_words[(i-(8-pos))];
		}
		
		xmlFree (str_contents);		
		
		
		
		
		
#if defined(_WIN32)		
		if ( is_addr )
			for (i=0; i<8; i++)
			{
				addr.u.Byte[i*2 + 0] = (uint8_t)(words[i] >> 8);
				addr.u.Byte[i*2 + 1] = (uint8_t)(words[i]     );
			}
		else
			for (i=0; i<8; i++)
			{
				mask.u.Byte[i*2 + 0] = (uint8_t)(words[i] >> 8);
				mask.u.Byte[i*2 + 1] = (uint8_t)(words[i]     );
			}
#else	
		if ( is_addr )
			for (i=0; i<8; i++)
			{
				addr.s6_addr[i*2 + 0] = (uint8_t)(words[i] >> 8);
				addr.s6_addr[i*2 + 1] = (uint8_t)(words[i]     );
			}
		else
			for (i=0; i<8; i++)
			{
				mask.s6_addr[i*2 + 0] = (uint8_t)(words[i] >> 8);
				mask.s6_addr[i*2 + 1] = (uint8_t)(words[i]     );
			}
#endif

	}
	

	
	/* verbose output */
	dagutil_verbose_level (3, "           ipv6 %12s "
	                          "address[%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X] "
	                          "mask[%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X]\n",
	                          (is_source == 1) ? "source" : "destination",
#if defined(_WIN32)		
	                          addr.u.Byte[0],  addr.u.Byte[1],  addr.u.Byte[2],  addr.u.Byte[3],  addr.u.Byte[4],  
	                          addr.u.Byte[5],  addr.u.Byte[6],  addr.u.Byte[7],  addr.u.Byte[8],  addr.u.Byte[9],
	                          addr.u.Byte[10], addr.u.Byte[11], addr.u.Byte[12], addr.u.Byte[13], addr.u.Byte[14], addr.u.Byte[15],
	                          mask.u.Byte[0],  mask.u.Byte[1],  mask.u.Byte[2],  mask.u.Byte[3],  mask.u.Byte[4],  
	                          mask.u.Byte[5],  mask.u.Byte[6],  mask.u.Byte[7],  mask.u.Byte[8],  mask.u.Byte[9],
	                          mask.u.Byte[10], mask.u.Byte[11], mask.u.Byte[12], mask.u.Byte[13], mask.u.Byte[14], mask.u.Byte[15]
#else
	                          addr.s6_addr[0],  addr.s6_addr[1],  addr.s6_addr[2],  addr.s6_addr[3],  addr.s6_addr[4],  
	                          addr.s6_addr[5],  addr.s6_addr[6],  addr.s6_addr[7],  addr.s6_addr[8],  addr.s6_addr[9],
	                          addr.s6_addr[10], addr.s6_addr[11], addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15],
	                          mask.s6_addr[0],  mask.s6_addr[1],  mask.s6_addr[2],  mask.s6_addr[3],  mask.s6_addr[4],  
	                          mask.s6_addr[5],  mask.s6_addr[6],  mask.s6_addr[7],  mask.s6_addr[8],  mask.s6_addr[9],
	                          mask.s6_addr[10], mask.s6_addr[11], mask.s6_addr[12], mask.s6_addr[13], mask.s6_addr[14], mask.s6_addr[15]
#endif
		);


	/* set the details in the rule */
	if ( is_source == 1 )
		dagixp_filter_set_ipv6_source_field (rule_h, &addr, &mask);
	else
		dagixp_filter_set_ipv6_dest_field (rule_h, &addr, &mask);
		
	
}




/*--------------------------------------------------------------------
 
 FUNCTION:      process_ipv4_addr_elements
 
 DESCRIPTION:   Process an <ip-source> or <ip-dest> element for ipv4, 
                by looping through the child elements and adding the 
                the mask and value to the rule.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <ip-source> or 
                                         <ip-dest> element (node).
                rule_h           IN/OUT  Handle to the rule that is 
                                         updated with then new fields.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_ipv4_addr_elements (xmlDocPtr doc, xmlNodePtr cur_node, RuleH rule_h)
{
	xmlNodePtr     child_node;
	xmlChar *      attr;
	xmlChar *      contents;
	int            base;
	uint32_t       is_addr;
	uint8_t        bytes[4];
	uint32_t       pos;
	uint32_t       value;
	uint32_t       is_source;
	char           delim[] = ".";
	char *         token;
	struct in_addr addr;
	struct in_addr mask;
	
	
	/* determine if source or destination address */
	if ( xmlStrEqual(cur_node->name, BAD_CAST"ip-source") )
		is_source = 1;
	else
		is_source = 0;
	
	
	/* clear the address and mask values */
	memset (&addr, 0x00, sizeof(addr));
	memset (&mask, 0xFF, sizeof(mask));

	
	/* loop through the child elements, which should only be <addr> or <mask> */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;

		
		/* get the node name */
		if ( xmlStrEqual(child_node->name, BAD_CAST"addr") )
			is_addr = 1;
		else if ( xmlStrEqual(child_node->name, BAD_CAST"mask") )
			is_addr = 0;
		else
		{
			xml_warning (EUNKNOWNELEM, child_node);
			continue;
		}
		
		
		/* get the 'hex' attribute value */
		attr = xmlGetProp (child_node, BAD_CAST"hex");
		if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"false") )
			base = 10;
		else
			base = 16;
		if (attr != NULL)   xmlFree (attr);


		
		/* get the text contents */
		contents = xmlNodeListGetString (doc, child_node->xmlChildrenNode, 1);
		if ( contents == NULL )
			continue;
		
		
				
		/* parse the address */
		bytes[0] = bytes[1] = bytes[2] = bytes[3] = 0x00;
		
		pos = 0;
		token = strtok ((char*)contents, delim);
		while ( (token != NULL) && (pos < 4) )
		{
			bytes[pos++] = (uint8_t)strtoul (token, NULL, base);
			token = strtok (NULL, delim);
		}
		
		value = ((uint32_t)bytes[0] << 24) | ((uint32_t)bytes[1] << 16) |
		        ((uint32_t)bytes[2] <<  8) | ((uint32_t)bytes[3]);
		value = htonl (value);
		
#if defined(_WIN32)		
		if ( is_addr )  addr.S_un.S_addr = value;
		else            mask.S_un.S_addr = value;
#else	
		if ( is_addr )  addr.s_addr      = value;
		else            mask.s_addr      = value;
#endif

		/*  */
		xmlFree (contents);		
	}
	


	/* verbose output */
	dagutil_verbose_level (3, "           ipv4 %12s "
	                          "address[%d.%d.%d.%d] "
	                          "mask[%d.%d.%d.%d]\n",
	                          (is_source == 1) ? "source" : "destination",
#if defined(_WIN32)		
	                          addr.S_un.S_un_b.s_b1, addr.S_un.S_un_b.s_b2, addr.S_un.S_un_b.s_b3, addr.S_un.S_un_b.s_b4,
	                          mask.S_un.S_un_b.s_b1, mask.S_un.S_un_b.s_b2, mask.S_un.S_un_b.s_b3, mask.S_un.S_un_b.s_b4
#else
	                          (uint8_t)(addr.s_addr >> 0), (uint8_t)(addr.s_addr >> 8), (uint8_t)(addr.s_addr >> 16), (uint8_t)(addr.s_addr >> 24),
	                          (uint8_t)(mask.s_addr >> 0), (uint8_t)(mask.s_addr >> 8), (uint8_t)(mask.s_addr >> 16), (uint8_t)(mask.s_addr >> 24)
#endif
		);



	/* set the details in the rule */
	if ( is_source == 1 )
		dagixp_filter_set_ipv4_source_field (rule_h, &addr, &mask);
	else
		dagixp_filter_set_ipv4_dest_field (rule_h, &addr, &mask);
		
	
}




/*--------------------------------------------------------------------
 
 FUNCTION:      process_rule_element
 
 DESCRIPTION:   Processes a <rule> element. Creates a new rule and then 
                iterates through the child elements to populate the rule.
 
 PARAMETERS:    doc              IN      Pointer to the parsed XML configuration
                                         file.
                cur_node         IN      Pointer to the <rule> element (node)
                ruleset_h        IN      Handle to the ruleset.

 RETURNS:       nothing

 HISTORY:       

---------------------------------------------------------------------*/
static void
process_rule_element (xmlDocPtr doc, xmlNodePtr cur_node, RulesetH ruleset_h)
{
	xmlNodePtr          child_node;
	xmlNodePtr          filter_node;
	xmlChar *           attr;
	enum { ipv4, ipv6 } rule_type = ipv4;
	RuleH               rule_h = NULL;

	uint16_t            tag;	
	steering_t          steering;
	uint16_t            snap_length;
	action_t            action;
	uint16_t            priority;
	
	
	
	
	/* before we create the rule we should get all the attributes */
	
	/* get the tag attribute */									 
	attr = xmlGetProp (cur_node, BAD_CAST"tag");
	if ( attr == NULL )
		tag = 0;
	else
		tag = (uint16_t) atoi ((char*)attr);
	if ( attr != NULL)  xmlFree(attr);
	
	if ( tag > 0x3FFF )
	{
		dagutil_warning ("the tag attribute for <rule> on line %d, is greater than 16383 it will be trimed.\n", cur_node->line);
		tag = 0x3FFF;
	}
	
	
	/* get the steering value */
	attr = xmlGetProp (cur_node, BAD_CAST"steering");
	if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"host") )
		steering = kHost;
	else
		steering = kLine;
			
	if ( attr != NULL)  xmlFree(attr);

	

	/* get the snap length value */
	attr = xmlGetProp (cur_node, BAD_CAST"snap");
	if ( attr == NULL )
		snap_length = 65535;
	else
		snap_length = atoi ((char*)attr);
		
	if ( attr != NULL)  xmlFree(attr);
	
	
	
	/* get the action value */
	attr = xmlGetProp (cur_node, BAD_CAST"action");
	if ( attr == NULL || xmlStrEqual(attr, BAD_CAST"accept") )
		action = kAccept;
	else
		action = kReject;
		
	if ( attr != NULL)  xmlFree(attr);
	
	
	
	/* get the priority value */
	attr = xmlGetProp (cur_node, BAD_CAST"priority");
	if ( attr == NULL )
		priority = 0;
	else
		priority = atoi ((char*)attr);
		
	if ( attr != NULL)  xmlFree(attr);


	
	/* display the rule information */
	dagutil_verbose_level (3, "----- Rule [%s tag:%d steering:%s snap:%d priority:%d]\n",
	                       (action == kAccept) ? "accept" : "reject", tag, 
	                       (steering == kHost) ? "host" : "line", snap_length, priority);
	
	
	
	
	/* check what type of rule it is by looking at the child item */
	for (child_node=cur_node->children; child_node; child_node = child_node->next)
	{
		if (child_node->type != XML_ELEMENT_NODE)
			continue;

		if ( xmlStrEqual(child_node->name, BAD_CAST"ipv4") )
		{
			rule_h = dagixp_filter_create_ipv4_rule (ruleset_h, action, tag, priority);
			rule_type = ipv4;
			break;
		}
	
		else if ( xmlStrEqual(child_node->name, BAD_CAST"ipv6") )
		{
			rule_h = dagixp_filter_create_ipv6_rule (ruleset_h, action, tag, priority);
			rule_type = ipv6;
			break;
		}
		
		else
		{
			xml_warning (EUNKNOWNELEM, child_node);
			return;
		}
	}
	
	if ( rule_h == NULL )
	{
		dagutil_warning ("failed to create new rule (errorcode: %d)\n", dagixp_filter_get_last_error());
		return;
	}
	
	
	
	
	/* set the rule meta data */
	if ( dagixp_filter_set_rule_snap_length(rule_h, snap_length) == -1 )
		dagutil_warning ("failed to set the snap length of the rule (errorcode: %d)\n", dagixp_filter_get_last_error());
		
	if ( dagixp_filter_set_rule_steering(rule_h, steering) == -1 )
		dagutil_warning ("failed to set the steering attribute of the rule (errorcode: %d)\n", dagixp_filter_get_last_error());
	
	
	
	
	/* iterate over the rule elements and add them to the rule */
	for (filter_node=child_node->children; filter_node; filter_node = filter_node->next)
	{
		if (filter_node->type != XML_ELEMENT_NODE)
			continue;
		
		switch ( simple_str_hash(filter_node->name) )
		{
			case XMLHASH_IPSOURCE:
			case XMLHASH_IPDEST:
				if ( rule_type == ipv4 )
					process_ipv4_addr_elements (doc, filter_node, rule_h);
				else
					process_ipv6_addr_elements (doc, filter_node, rule_h);
				break;
					
			case XMLHASH_IPV6FLOW:
				if ( rule_type != ipv6 )
					xml_warning (EUNKNOWNELEM, filter_node);
				else
					process_ipv6_flow_element (doc, filter_node, rule_h);
				break;
			
			case XMLHASH_TCP:
				dagixp_filter_set_protocol_field (rule_h, IPPROTO_TCP);
				process_port_elements (doc, filter_node, rule_h);
				break;
			
			case XMLHASH_UDP:
				dagixp_filter_set_protocol_field (rule_h, IPPROTO_UDP);
				process_port_elements (doc, filter_node, rule_h);
				break;

			case XMLHASH_SCTP:
				dagixp_filter_set_protocol_field (rule_h, IPPROTO_SCTP);
				process_port_elements (doc, filter_node, rule_h);
				break;
				
			
			case XMLHASH_ICMP:
				dagixp_filter_set_protocol_field (rule_h, IPPROTO_ICMP);
				process_icmp_type_elements (doc, filter_node, rule_h);
				break;
						

			default:
				xml_warning (EUNKNOWNELEM, filter_node);
		}
		
	}
		
	
	
}





/*--------------------------------------------------------------------
 
 FUNCTION:      parse_ruleset_file
 
 DESCRIPTION:   Parses the given XML configuration file and loads all 
                the details into the given viruleset.
 
 PARAMETERS:    ruleset_h        IN      Handle to the ruleset
                                         to load the rules into.
                filename         IN      The name of the file to parse.

 RETURNS:       Returns 0 if the file was parsed successifully, otherwise
                -1 is returned.

 HISTORY:       

---------------------------------------------------------------------*/
static int
parse_ruleset_file (RulesetH ruleset_h, const char * filename)
{
	xmlDocPtr       doc;
	xmlNodePtr      root_element;
	xmlNodePtr      cur_node;
	xmlChar *       attr;
	int             retval = -1;
	
	
	
	
	/* sanity checking */
	if ( filename == NULL || filename[0] == '\0' )
	{
		dagutil_error ("Invalid input filename\n");
		return -1;
	}
	

	/*
	 * this initialize the library and check potential ABI mismatches
	 * between the version it was compiled for and the actual shared
	 * library used.
	 */
	LIBXML_TEST_VERSION

	
	/* parse the file */
	doc = xmlReadFile (filename, NULL, 0);
	if ( doc == NULL )
	{
		dagutil_error ("Failed to parse %s\n", filename);
		return -1;
	}
		

	/* get the root element and confirm it is 'dsm-rules' */
	root_element = xmlDocGetRootElement(doc);
	if ( root_element == NULL )
	{
		dagutil_error ("Failed to get the document root element\n");
		goto Exit;
	}
	

	
	/* validate the root element and the version */
	if ( !xmlStrEqual(root_element->name, BAD_CAST"ruleset") )
	{
		dagutil_error ("The document root element is not <ruleset>.\n");
		goto Exit;
	}
	
	/* check the version */
	attr = xmlGetProp (root_element, BAD_CAST"version");
	if ( attr == NULL || !xmlStrEqual(attr, BAD_CAST"1.0") )
	{
		if (attr != NULL)   xmlFree (attr);
		dagutil_error ("Invalid or missing version attribute in the <ruleset> root element.\n");
		goto Exit;
	}
	xmlFree (attr);
	

	

	/* loop through the child <rule> nodes */
	for (cur_node=root_element->children; cur_node; cur_node = cur_node->next)
	{
		if (cur_node->type != XML_ELEMENT_NODE)
			continue;

		
		if ( xmlStrEqual(cur_node->name, BAD_CAST"rule") )
			process_rule_element(doc, cur_node, ruleset_h);
		else
			xml_warning (EUNKNOWNELEM, cur_node);
	}
	



	
	retval = 0;
	
	dagutil_verbose ("Ruleset file parsed successifully, %d rules parsed.\n", dagixp_filter_ruleset_rule_count(ruleset_h));
	

Exit:
	/* clean up */
	xmlFreeDoc (doc);
	xmlCleanupParser();
	
	return retval;
}
