/*
 * Copyright (c) 2004-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: filters.c 11429 2009-06-23 07:14:05Z vladimir $
 */

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

/* Project headers. */
#include "negation_tree.h"
#include "utilities.h"

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


/* CVS Header. */
static const char * kFiltersCvsHeader __attribute__ ((unused)) = "$Id: filters.c 11429 2009-06-23 07:14:05Z vladimir $";


/* Displays some debugging info to stdout. */
#define VERBOSE 1


#ifndef INLINE
#if defined(__linux__) || defined(__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__))
#define INLINE inline
#elif defined(_WIN32)
#define INLINE __inline
#else
#define INLINE
#endif /* Platform-specific code. */
#endif /* INLINE */

/* Internal routines. */
static INLINE void* internal_malloc(unsigned int bytes);
static INLINE void internal_free(void* ptr, unsigned int bytes);

static void aggregate_port_numbers(port_array_t* result, uint16_t first, uint16_t last);
static void aggregate_addresses(ip_address_t* first_addr, ip_address_t* second_addr);
static unsigned int count_differing_bits(ip_address_t* first_addr, ip_address_t* second_addr);
static void new_address_array(addr_array_t* result, unsigned int length);
static void expand_port_array(port_array_t* result, unsigned int expansion);
static void create_filter_entry(temp_filter_list_t* list,
                         uint16_t colour,
                         uint8_t action,
                         protocol_t protocol,
                         ip_address_t* source_ip,
                         ip_address_t* dest_ip,
                         cam_port_t* src_port,
                         cam_port_t* dest_port);
static void optimize_address_list(addr_array_t* result);

/* Implementation of internal routines. */
static INLINE void*
internal_malloc(unsigned int bytes)
{
	return malloc(bytes);
}


static INLINE void
internal_free(void* ptr, unsigned int bytes)
{
	assert(ptr);

	free(ptr);
}


/**
 * Create an address array
 *
 * Entries are zeroed. Calls exit if unable to allocate memory.
 * TODO: return error code in case of failure
 *
 * \param result  pointer to the resulting array
 * \param length  number of items
 */
static void
new_address_array(addr_array_t* result, unsigned int length)
{
	unsigned int size = sizeof(ip_address_t) * length;

	if (!result)
	{
		fprintf(stderr, "error: invalid addr_array_t*\n");
		assert(0);
		exit(EXIT_FAILURE);
	}

	result->count = length;
	result->entries = NULL;
	if (size)
	{
		unsigned int index;

		result->entries = (ip_address_t*) internal_malloc(size);
		if (!result->entries)
		{
			fprintf(stderr, "error: could not allocate %u bytes of memory for address array entries: %s\n", 
					size, strerror(errno));
			exit(EXIT_FAILURE);
		}

		memset(result->entries, 0, size);

		for (index = 0; index < length; index++)
		{
			result->entries[index].mask = 0xffffffff;
		}
	}
}


/**
 * Expand port array
 *
 * Increase the size of the port array.  Entries are zeroed, masks are
 * set to 0xffff
 *
 * Calls exit if unable to allocate memory.
 * TODO: return error code in case of failure
 *
 * \param result      pointer to the port array
 * \param exopansion  number of new elements to be added
 */
static void
expand_port_array(port_array_t* result, unsigned int expansion)
{
	cam_port_t* old_entries;
	unsigned int old_count;
	unsigned int new_size;
	unsigned int index;
	
	if (0 == expansion)
	{
		/* No expansion required. */
		return;
	}

	if (!result)
	{
		fprintf(stderr, "error: invalid port_array_t*\n");
		assert(0);
		exit(EXIT_FAILURE);
	}

	old_count = result->count;
	old_entries = result->entries;
	new_size = sizeof(cam_port_t) * (expansion + result->count);

	result->count += expansion;
	result->entries = (cam_port_t*) internal_malloc(new_size);
	if (!result->entries)
	{
		fprintf(stderr, "error: could not allocate %u bytes of memory for port array entries: %s\n", 
				new_size, strerror(errno));
		assert(0);
		exit(EXIT_FAILURE);
	}

	/* Zero the new entries. */
	memset(result->entries, 0, new_size);

	if (old_entries)
	{
		/* Copy the old entries to the new array. */
		memcpy(result->entries, old_entries, sizeof(cam_port_t) * old_count);

		/* Dispose of the old entries. */
		internal_free(old_entries, sizeof(cam_port_t) * old_count);
		old_entries = NULL;
	}

	/* Add default masks for the new entries. */
	for (index = old_count; index < result->count; index++)
	{
		result->entries[index].mask = 0xffff;
	}
}


temp_filter_entry_t*
new_temp_filter_entry(filter_rule_t* rule)
{
	temp_filter_entry_t* result = internal_malloc(sizeof(temp_filter_entry_t));

	if (result)
	{
		memset(result, 0, sizeof(temp_filter_entry_t));
		result->rule = rule;
	}

	return result;
}


void
dispose_temp_filter_entry(temp_filter_entry_t* temp_entry)
{
	assert(temp_entry);

	if (temp_entry->src_addresses.entries)
	{
		internal_free(temp_entry->src_addresses.entries, sizeof(ip_address_t) * temp_entry->src_addresses.count);
	}

	if (temp_entry->dst_addresses.entries)
	{
		internal_free(temp_entry->dst_addresses.entries, sizeof(ip_address_t) * temp_entry->dst_addresses.count);
	}

	if (temp_entry->src_ports.entries)
	{
		internal_free(temp_entry->src_ports.entries, sizeof(cam_port_t) * temp_entry->src_ports.count);
	}

	if (temp_entry->dst_ports.entries)
	{
		internal_free(temp_entry->dst_ports.entries, sizeof(cam_port_t) * temp_entry->dst_ports.count);
	}

	internal_free(temp_entry, sizeof(temp_filter_entry_t));
}


/* TODO: the following three functions are essentially the same, they
 * differ in data size only, therefore making a good candidate for
 * refactoring */
void
display_filter_ip_address(FILE* outfile, ip_address_t* address)
{
	unsigned int host_value = ntohl(address->ip4_addr & address->mask);
	unsigned int host_mask = ntohl(address->mask);
	unsigned int index;
	char buffer[33];

	buffer[32] = '\0';

	for (index = 0; index < 32; index++)
	{
		if ((host_mask & (1 << index)) == 0)
		{
			/* Don't care. */
			buffer[31 - index] = '-';
		}
		else if (host_value & (1 << index))
		{
			buffer[31 - index] = '1';
		}
		else
		{
			buffer[31 - index] = '0';
		}
	}

	fprintf(outfile, "%s",buffer);
}


void
display_filter_port(FILE* outfile, cam_port_t* port)
{
	unsigned int host_value = ntohs(port->port & port->mask);
	unsigned int host_mask = ntohs(port->mask);
	unsigned int index;
	char buffer[17];

	buffer[16] = '\0';

	for (index = 0; index < 16; index++)
	{
		if ((host_mask & (1 << index)) == 0)
		{
			/* Don't care. */
			buffer[15 - index] = '-';
		}
		else if (host_value & (1 << index))
		{
			buffer[15 - index] = '1';
		}
		else
		{
			buffer[15 - index] = '0';
		}
	}

	fprintf(outfile, "%s", buffer);
}


void
display_filter_tcp_flags(FILE* outfile, tcp_flags_t* tcp_flags)
{
	uint8_t value = tcp_flags->flags;
	uint8_t mask = tcp_flags->mask;
	unsigned int index;
	char buffer[9];

	buffer[8] = '\0';

	for (index = 0; index < 8; index++)
	{
		if ((mask & (1 << index)) == 0)
		{
			/* Don't care. */
			buffer[7 - index] = '-';
		}
		else if (value & (1 << index))
		{
			buffer[7 - index] = '1';
		}
		else
		{
			buffer[7 - index] = '0';
		}
	}

	fprintf(outfile, "%s", buffer);
}


void
display_filter_entry(FILE* outfile, cam_entry_t* entry)
{
	fprintf(outfile, "%4u %s %4s ", entry->colour, get_action_string(entry->action), get_protocol_string(entry->protocol));

	/* Source */
	fprintf(outfile, "src-ip {");
	display_filter_ip_address(outfile, &entry->source);

	fprintf(outfile, "} src-port {");
	display_filter_port(outfile, &entry->src_port);

	/* Destination. */
	fprintf(outfile, "} dst-ip {");
	display_filter_ip_address(outfile, &entry->dest);

	fprintf(outfile, "} dst-port {");
	display_filter_port(outfile, &entry->dest_port);
	fprintf(outfile, "} tcp-flags {");
	display_filter_tcp_flags(outfile, &entry->tcp_flags);

	fprintf(outfile, "}\n");
}


/**
 * Create temporary filter entry 
 *
 * Create new entry and append it to the filter list.
 *
 * \param list      list of filters to add the new entry to
 * \param colour    coloring to be used on matching packets
 * \param action    action to be done on matching packets
 * \param protocol  layer 3 protocol
 * \param source_ip definition of source IP addresses (possible don't-care bits)
 * \param dest_ip   defintion of destination IP addresses (possible don't-care bits)
 * \param src_port  layer 4 source port defintion (possible don't-care bits)
 * \param dest_port layer 4 destinatio port defintion (possible don't-care bits)
 */
static void
create_filter_entry(temp_filter_list_t* list,
                    uint16_t colour,
                    uint8_t action,
                    protocol_t protocol,
                    ip_address_t* source_ip,
                    ip_address_t* dest_ip,
                    cam_port_t* src_port,
                    cam_port_t* dest_port)
/* TODO: how about the TCP flags? */
{
	temp_filter_entry_t* new_entry = new_temp_filter_entry(NULL);
	cam_entry_t* cam_entry = &new_entry->cam_entry;

	if (!cam_entry)
	{
		fprintf(stderr, "error: could not allocate memory for new filter entry: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	cam_entry->colour = colour;
	cam_entry->protocol = protocol;
	cam_entry->action = action;
		
	/* Copy IP addresses. */
	memcpy(&cam_entry->source, source_ip, sizeof(ip_address_t));
	memcpy(&cam_entry->dest, dest_ip, sizeof(ip_address_t));
		
	/* Copy ports. */
	memcpy(&cam_entry->src_port, src_port, sizeof(cam_port_t));
	memcpy(&cam_entry->dest_port, dest_port, sizeof(cam_port_t));

	/* Append to list. */
	if (!list->head)
	{
		list->head = new_entry;
		list->tail = new_entry;
	}
	else
	{
		list->tail->next = new_entry;
		list->tail = new_entry;
	}
}


static void
aggregate_port_numbers(port_array_t* result, uint16_t first, uint16_t last)
{
	unsigned int entry_count = 0;
	unsigned int temp_first = first;
	unsigned int cam_index;

	/* Count required number of CAM entries. */
	while (temp_first <= last)
	{
		unsigned int slab = 1;

		while ((temp_first % (2 * slab) == 0) && ((1 + last - temp_first) >= (2 * slab)))
		{
			slab *= 2;
		}

		entry_count++;
		temp_first += slab;
	}

	/* Construct CAM entries. */
	cam_index = result->count;
	temp_first = first;
	expand_port_array(result, entry_count);
	while (temp_first <= last)
	{
		unsigned int slab = 1;
		unsigned int bits = 0;
		uint16_t mask = 0;
		unsigned int index;

		while ((temp_first % (2 * slab) == 0) && ((1 + last - temp_first) >= (2 * slab)))
		{
			slab *= 2;
			bits++;
		}
				
		/* Adjust CAM entry mask to size of slab. */
		for (index = 0; index < bits; index++)
		{
			mask |= (1 << index);
		}

		result->entries[cam_index].port = htons(temp_first);
		result->entries[cam_index].mask = htons(~mask);

		cam_index++;
		temp_first += slab;
	}
}


/* Determine how many CAM entries are required to deal with this port.
 * Return the relevant ports in an array.
 */
void
create_port_filter_entries(port_array_t* result, ipf_port_t* port)
{
	uint16_t first = ntohs(port->first);
	uint16_t last = ntohs(port->last);

	/* Initialise results. */
	result->count = 0;
	result->entries = NULL;

	if (port->any)
	{
		/* Don't care about port value, no software check required. */
		expand_port_array(result, 1);

		result->entries[0].port = 0;
		result->entries[0].mask = 0;
	}
	else if (port->inverse)
	{
		if (first == last)
		{
			unsigned int index;

			/* Single port number. */
			expand_port_array(result, 16);

			/* One entry per bit in the port number. */
			for (index = 0; index < 16; index++)
			{
				result->entries[index].port = htons(~first);
				result->entries[index].mask = htons(1 << index);
			}
		}
		else
		{
			/* Multiple port numbers. */
			/* ![a:b] expands to [1:a-1] and [b+1:65535]. */

			/* Construct CAM entries to match inverted port range. */
			aggregate_port_numbers(result, 0, first - 1);
			aggregate_port_numbers(result, last + 1, 65535);

			port->inverse = 0;
		}
	}
	else if (first == last)
	{
		/* Single port value. */
		expand_port_array(result, 1);

		result->entries[0].port = htons(first);
		result->entries[0].mask = 0xffff;
	}
	else
	{
		/* Construct CAM entries to match port range. */
		aggregate_port_numbers(result, first, last);
	}
}


/**
 * Count differing bits
 *
 * Compare the bits considering three different values for a bit (0,1,
 * don't care).
 *
 * \param first_addr    pointer to first IP address definiton
 * \param second_addr   pointer to second IP address definition
 * 
 * \return number of differing bits
 */
static unsigned int
count_differing_bits(ip_address_t* first_addr, ip_address_t* second_addr)
{
	unsigned int count = 0;
	unsigned int index;

	for (index = 0; index < 32; index++)
	{
		uint32_t mask = (1 << index);
		uint8_t first;   /* Find bit value: 0, 1 or 2 (= don't care). */
		uint8_t second;  /* Find bit value: 0, 1 or 2 (= don't care). */

		if (first_addr->mask & mask)
		{
			if (first_addr->ip4_addr & mask)
			{
				first = 1;
			}
			else
			{
				first = 0;
			}
		}
		else
		{
			first = 2;
		}

		if (second_addr->mask & mask)
		{
			if (second_addr->ip4_addr & mask)
			{
				second = 1;
			}
			else
			{
				second = 0;
			}
		}
		else
		{
			second = 2;
		}

		if (first != second)
		{
			count++;
		}
	}

	assert(count > 0);

	return count;
}


static void
aggregate_addresses(ip_address_t* first_addr, ip_address_t* second_addr)
{
	unsigned int index;

	/* Find the single bit that differs. */
	for (index = 0; index < 32; index++)
	{
		uint32_t mask = (1 << index);
		uint8_t first;   /* Find bit value: 0, 1 or 2 (= don't care). */
		uint8_t second;  /* Find bit value: 0, 1 or 2 (= don't care). */

		if (first_addr->mask & mask)
		{
			if (first_addr->ip4_addr & mask)
			{
				first = 1;
			}
			else
			{
				first = 0;
			}
		}
		else
		{
			first = 2;
		}

		if (second_addr->mask & mask)
		{
			if (second_addr->ip4_addr & mask)
			{
				second = 1;
			}
			else
			{
				second = 0;
			}
		}
		else
		{
			second = 2;
		}

		if (first != second)
		{
			/* Change the bit to a don't care in the first address, and remove the second address. */
			first_addr->mask &= ~(1 << index);
			second_addr->ip4_addr = 0;
			return;
		}
	}

	assert(0); /* There was supposed to be a differing bit, but we didn't find it. */
}


static void
optimize_address_list(addr_array_t* result)
{
	addr_array_t input = {result->count, result->entries};

	/* Repeatedly aggregate addresses that only differ in one bit position. */
	for (;;)
	{
		bool aggregated = false;
		unsigned int first;
		
		for (first = 0; (first < input.count) && (!aggregated); first++)
		{
			unsigned int second;

			for (second = 0; (second < input.count) && (!aggregated); second++)
			{
				if (first != second)
				{
					ip_address_t* first_addr = &result->entries[first];
					ip_address_t* second_addr = &result->entries[second];

					if ((first_addr->ip4_addr != 0) && (second_addr->ip4_addr != 0))
					{
						if (count_differing_bits(first_addr, second_addr) == 1)
						{
							/* Can aggregate these two addresses. */
							aggregate_addresses(first_addr, second_addr);
							aggregated = true;
							result->count--;
							break;
						}
					}
				}
			}
		}

		if (!aggregated)
		{
			break; /* Otherwise try again. */
		}
	}

	if (result->count != input.count)
	{
		unsigned int index;
		unsigned int result_index = 0;

		/* Clean up the result. */

		result->entries = NULL; /* Preserved in input.entries. */
		new_address_array(result, result->count);
		for (index = 0; index < input.count; index++)
		{
			if (input.entries[index].ip4_addr != 0)
			{
				/* Valid address, copy to output. */
				result->entries[result_index].ip4_addr = input.entries[index].ip4_addr;
				result->entries[result_index].mask = input.entries[index].mask;
				result_index++;
			}
		}

		for (index = 0; index < result->count; index++)
		{
			assert(result->entries[index].ip4_addr != 0);
		}

		/* Dispose of old result array. */
		internal_free(input.entries, input.count * (sizeof(ip_address_t)));
	}
}


/**
 * Determine how many CAM entries are required to deal with this address.
 */
void
create_address_filter_entries(addr_array_t* result,
                              temp_filter_entry_t* temp_entry,
                              uint8_t inverse,
                              list_node_t* head)
{
	unsigned int list_length = get_list_length(head);
	ip_address_t* entry = NULL;
	unsigned int index = 0;
	list_node_t* node;

	/* Initialise results. */
	result->count = 0;
	result->entries = NULL;

	if (inverse == 0)
	{
		new_address_array(result, list_length);

		/* Standard case. */
		entry = result->entries;
		node = head;
		while (node)
		{
			entry->ip4_addr = node->address->ip4_addr;
			entry->mask = node->address->mask;
			entry++;

			node = node->next;
		}

		optimize_address_list(result);
	}
	else if (list_length > 1)
	{
		/* Work out how many exact-match entries there will be. */
		uint32_t same_bits = 0xffffffff; /* 1 in i'th position if bit i was the same for all addresses. */
		unsigned int common_bits;
		NegationTreePtr tree = negation_tree_init();
		NegationTreeListNodePtr tree_node;
		unsigned int tree_node_count;

		node = head;
		while (node)
		{
			negation_tree_add(tree, ntohl(node->address->ip4_addr), ntohl(node->address->mask));

			node = node->next;
		}
		negation_tree_display_paths(tree, stdout);
		tree_node_count = 0;
		tree_node = negation_tree_get_list(tree);
		while (tree_node)
		{
			struct in_addr ip_address;
			int index;
			char value_buf[32];
			char ip_buffer[33];

			for (index = 31; index > -1; index--)
			{
				uint32_t bit_mask = (1 << index);

				if ((tree_node->mask & bit_mask) == 0)
				{
					ip_buffer[31 - index] = '-';
				}
				else if (tree_node->value & bit_mask)
				{
					ip_buffer[31 - index] = '1';
				}
				else
				{
					ip_buffer[31 - index] = '0';
				}
			}
			ip_buffer[32] = '\0';

			ip_address.s_addr = htonl(tree_node->value);
			strncpy(value_buf, inet_ntoa(ip_address), 32);

			ip_address.s_addr = htonl(tree_node->mask);
#if VERBOSE
			printf("IP address: %s (value = %s, mask = %s)\n", ip_buffer, value_buf, inet_ntoa(ip_address));
#endif /* VERBOSE */

			tree_node_count++;
			tree_node = tree_node->next;
		}

#if VERBOSE
		printf("%u IP addresses counted before optimization pass.\n", tree_node_count);
#endif /* VERBOSE */

		node = head;
		while (node && node->next)
		{
			for (index = 0; index < 32; index++)
			{
				uint32_t single_bit = (1 << index);

				if ((node->address->mask & single_bit) == 0)
				{
					/* Bits differed - set corresponding position in same_bits to 0. */
					same_bits = same_bits & ~(single_bit);
				}
				else if ((node->next->address->mask & single_bit) == 0)
				{
					/* Bits differed - set corresponding position in same_bits to 0. */
					same_bits = same_bits & ~(single_bit);
				}
				else if ((node->next->address->ip4_addr & single_bit) != (node->address->ip4_addr & single_bit))
				{
					/* Bits differed - set corresponding position in same_bits to 0. */
					same_bits = same_bits & ~(single_bit);
				}
			}

			node = node->next;
		}

		/* Count how many negation tree entries we need to include. */
		tree_node_count = 0;
		tree_node = negation_tree_get_list(tree);
		while (tree_node)
		{
			unsigned int include = 1;
			int index;

			for (index = 0; index < 32; index++)
			{
				uint32_t mask = (1 << index);

				if ((same_bits & mask) && (htonl(tree_node->mask) & mask))
				{
					/* This bit indicates a definite non-match. */
					uint32_t exact_match_value = ~(head->address->ip4_addr & head->address->mask);
					
					if ((exact_match_value & mask) == (htonl(tree_node->value & tree_node->mask) & mask))
					{
						struct in_addr ip_address;
						char value_buf[32];

						/* No need to include this tree node. */
						include = 0;

						ip_address.s_addr = htonl(tree_node->value);
						strncpy(value_buf, inet_ntoa(ip_address), 32);

						ip_address.s_addr = htonl(tree_node->mask);
#if VERBOSE
						printf("Excluded address: value = %s, mask = %s\n", value_buf, inet_ntoa(ip_address));
#endif /* VERBOSE */
						break;
					}
				}
			}

			if (include)
			{
				tree_node_count++;
			}

			tree_node = tree_node->next;
		}
#if VERBOSE
		printf("%u IP addresses counted after optimization pass.\n", tree_node_count);
#endif /* VERBOSE */
		

		/* Need one exact-match entry for each one bit in same_bits. */
		common_bits = count_one_bits(same_bits);
		
		/* And one for each remaining tree node. */
		new_address_array(result, tree_node_count + common_bits);

#if 0
/* Obsolete code for potential matches. */
		if ((2 << common_bits) == list_length)
		{
			/* Special case: all potential matches are misses (e.g. 4 entries with only 2 don't care bits). */
			/* Create and return 'common_bits' entries that definitely don't match. */
			new_address_array(result, common_bits);
		}
		else
		{
			/* Create and return 'common_bits' entries that definitely don't match, plus one potential match. */
			new_address_array(result, 1 + common_bits);
		}
#endif /* 0 */

		entry = result->entries;
		for (index = 0; index < 32; index++)
		{
			/* Set up entries that definitely don't match.
			 * These have don't cares (0) everywhere in the mask for bits that differ between addresses,
			 * and for bits that are the same in all addresses the ip4_addr field contains the negation
			 * of the value of that bit.
			 */
			if (same_bits & (1 << index))
			{
				entry->ip4_addr = ~(head->address->ip4_addr & head->address->mask);
				entry->mask = (1 << index);
				entry++;
			}
		}

		/* Add in the entries from the negation tree. */
		tree_node = negation_tree_get_list(tree);
		while (tree_node)
		{
			unsigned int include = 1;
			int index;

			for (index = 0; index < 32; index++)
			{
				uint32_t mask = (1 << index);

				if ((same_bits & mask) && (htonl(tree_node->mask) & mask))
				{
					/* This bit indicates a definite non-match. */
					uint32_t exact_match_value = ~(head->address->ip4_addr & head->address->mask);
					
					if ((exact_match_value & mask) == (htonl(tree_node->value & tree_node->mask) & mask))
					{
						/* No need to include this tree node. */
						include = 0;
						break;
					}
				}
			}

			if (include)
			{
				entry->ip4_addr = htonl(tree_node->value);
				entry->mask = htonl(tree_node->mask);
				entry++;
			}

			tree_node = tree_node->next;
		}

		negation_tree_dispose(tree);

#if 0
/* Obsolete code for potential matches. */
		/* Create the single entry that catches potential matches. */
		if ((2 << common_bits) == list_length)
		{
			/* Special case: all potential matches are misses (e.g. 4 entries with only 2 don't care bits). */
		}
		else
		{
			entry->address.ip4_addr = (head->address->ip4_addr & head->address->mask);
			entry->address.mask = 0;
			entry->potential_match = 1;
				
			for (index = 0; index < 32; index++)
			{
				if ((same_bits & (1 << index)) == 0)
				{
					entry->address.mask |= (1 << index);
				}
			}
		}
#endif /* 0 */
	}
	else if (list_length == 1)
	{
		/* One entry per non-masked bit in the active portion of the address. */
		node = head;
		new_address_array(result, count_one_bits(node->address->mask));

		entry = &result->entries[0];
		for (index = 32 - count_one_bits(node->address->mask); index < 32; index++)
		{
			entry->mask = htonl(1 << index);
			entry->ip4_addr = ~(node->address->ip4_addr);
			entry++;
		}
	}
}


void
expand_temp_filter_entry(temp_filter_list_t* list, temp_filter_entry_t* temp_entry)
{
	filter_rule_t* rule = temp_entry->rule;
	unsigned int src_ip_index;

	/* Create one "real" CAM entry for each (source IP, dest IP, source port, dest port) combination. */
	for (src_ip_index = 0; src_ip_index < temp_entry->src_addresses.count; src_ip_index++)
	{
		ip_address_t* src_ip = &temp_entry->src_addresses.entries[src_ip_index];
		unsigned int dst_ip_index;

		for (dst_ip_index = 0; dst_ip_index < temp_entry->dst_addresses.count; dst_ip_index++)
		{
			ip_address_t* dst_ip = &temp_entry->dst_addresses.entries[dst_ip_index];
			unsigned int src_port_index;

			for (src_port_index = 0; src_port_index < temp_entry->src_ports.count; src_port_index++)
			{
				cam_port_t* src_port = &temp_entry->src_ports.entries[src_port_index];
				unsigned int dst_port_index;

				for (dst_port_index = 0; dst_port_index < temp_entry->dst_ports.count; dst_port_index++)
				{
					cam_port_t* dst_port = &temp_entry->dst_ports.entries[dst_port_index];
					uint16_t colour;

					/* Create entry and add it to the list. */
					colour = rule->exact_colour;
					create_filter_entry(list, colour, rule->action, rule->protocol, src_ip, dst_ip, src_port, dst_port);
				}
			}
		}
	}
}
