/*
 * net_helpers.c: Helper functions for the Network.
 *
 * Author and Copyright (C) 1999-2005 Amon Ott (ao@rsbac.org)
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License as
 *      published by the Free Software Foundation, version 2.
 *
 * Last modified 24/04/2002.
 */

#include <rsbac/types.h>
#ifdef __KERNEL__
#include <rsbac/network.h>
#endif

__u32 rsbac_net_make_mask_u32(__u8 bits)
{
	__u32 res;
	__u8 res0 = 0;
	__u8 res1 = 0;
	__u8 res2 = 0;
	__u8 res3 = 0;
	int i;

	if (bits >= 32)
		return (__u32) - 1;
	if (!bits)
		return 0;
	if (bits >= 24) {
		bits -= 24;
		res0 = 255;
		res1 = 255;
		res2 = 255;
		for (i = 0; i < bits; i++)
			res3 |= 1 << (7 - i);
	} else if (bits >= 16) {
		bits -= 16;
		res0 = 255;
		res1 = 255;
		res3 = 0;
		for (i = 0; i < bits; i++)
			res2 |= 1 << (7 - i);
	} else if (bits >= 8) {
		bits -= 8;
		res0 = 255;
		res2 = 0;
		res3 = 0;
		for (i = 0; i < bits; i++)
			res1 |= 1 << (7 - i);
	} else {
		res1 = 0;
		res2 = 0;
		res3 = 0;
		for (i = 0; i < bits; i++)
			res0 |= 1 << (7 - i);
	}
	res = (res3 << 24) | (res2 << 16) | (res1 << 8) | res0;
	return res;
}

#ifdef __KERNEL__
/* The lookup data param is always second, so we use it as description here! */
int rsbac_net_compare_data(void *data1, void *data2)
{
	struct rsbac_net_temp_data_t *temp = data1;
	struct rsbac_net_description_t *desc = data2;

	if (!temp || !desc)
		return 1;
	if ((temp->address_family != RSBAC_NET_ANY)
	    && (temp->address_family != desc->address_family)
	    )
		return 1;
	switch (desc->address_family) {
	case AF_INET:
		{
			__u32 mask;
			int i;

			if(temp->address.inet.nr_addr == 0)
				return 1;
			if ((temp->type != RSBAC_NET_ANY)
			    && (desc->type != temp->type)
			    )
				return 1;
			if ((temp->protocol != RSBAC_NET_ANY)
			    && (desc->protocol != temp->protocol)
			    )
				return 1;
			if(temp->ports.nr_ports > 0) {
				i=0;
				while(i < temp->ports.nr_ports) {
					if ((desc->port >= temp->ports.ports[i].min)
					&& (desc->port <= temp->ports.ports[i].max))
						break;
					i++;
				}
				if(i == temp->ports.nr_ports)
					return 1;
			}
			if (temp->netdev[0]
			    && (!desc->netdev[0]
				|| strncmp(desc->netdev, temp->netdev,
					   RSBAC_IFNAMSIZ))
			    )
				return 1;
			if (!desc->address)
				return 1;
			i=0;
			while(i < temp->address.inet.nr_addr) {
				mask = rsbac_net_make_mask_u32(temp->address.inet.valid_bits[i]);
				if ((((*(__u32 *) desc->address) & mask) ==
					(temp->address.inet.addr[i] & mask))
				    )
				    return 0;
				i++;
			}
			return 1;
		}

		/* Other address families: only socket type checks for now */
	default:
		if ((temp->type != RSBAC_NET_ANY)
		    && (desc->type != temp->type)
		    )
			return 1;
		return 0;
	}
	return 1;
}
#endif
