/*
 * Copyright (C) 1993-2001 by Darren Reed.
 *
 * See the IPFILTER.LICENCE file for details on licencing.
 */
#if !defined(lint)
static const char rcsid[] = "@(#)$Id: tru64.c,v 2.11.2.6 2006/07/14 06:12:23 darrenr Exp $";
#endif
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/sysconfig.h>
#include <machine/machlimits.h>
#include <net/net_globals.h>
#include <io/common/iotypes.h>
#include <sys/errno.h>
#include <sys/buf.h>
#include <sys/secdefines.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/disklabel.h>
#include <sys/user.h>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <sys/syslog.h>
#include <io/common/pt.h>
#include <io/common/devdriver.h>
#include <io/common/devio.h>
#include <io/common/devgetinfo.h>

#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/ip_stat.h>
#include <netinet/in.h>
#include <netinet/firewall.h>

#include <netinet/if_ether.h>
#include <net/ether_driver.h>

#include "ip_compat.h"
#include "ip_fil.h"
#include "ip_nat.h"
#include "ip_state.h"
#include "ip_frag.h"
#include "ip_auth.h"
#include "ip_compat.h"
#include "ip_fil.h"

/* #undef	IPFDEBUG	*/

/* function prototypes */
int	ipfilter_attach(void);
int	ipfilter_detach(void);
void	ipfilter_ifattach(void);
void	ipfilter_ifdetach(void);
int	ipfilter_ifioctl(struct ifnet *, unsigned int, caddr_t);
#if TRU64 >= 1885
int	ipfilter_ifoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
			  struct rtentry *, char *);
#else
int	ipfilter_ifoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
			  struct rtentry *);
#endif
void	ipfilter_in_control(struct socket *, u_int, caddr_t *, struct ifnet *);
void	ipfilter_ip_input(struct mbuf *m);
int	ipfilter_ip_output(struct ifnet *, struct mbuf *, struct in_route *,
			   int, struct ip_moptions *);
void	ipfilter_configure_callback __P((int, int, int, int));
void	ipfilter_timer(void);
void	ipfilter_clock(void *arg);
int	ipfilteropen(dev_t, int, int);
int	ipfilterread(dev_t, struct uio *, int);
int	ipfilterclose(dev_t, int, int);
int	ipfilterwrite(dev_t, int, int);
int	ipfilterioctl(dev_t, u_int, caddr_t, int);

extern	int	nodev(), nulldev();
extern	task_t	first_task;
extern	ipfrwlock_t	ipf_tru64;

struct	dsent	ipfilter_devsw_entry = {
	ipfilteropen,
	ipfilterclose,
	nodev,		/* d_strategy */
	ipfilterread,
#ifdef	IPFILTER_SYNC
	ipfilterwrite,
#else
	nulldev,	/* d_write */
#endif
	ipfilterioctl,
	nodev,		/* d_dump */
	nodev,		/* d_psize */
	nulldev,	/* d_stop */
	nulldev,	/* d_reset */
	nulldev,	/* d_select */
	0,		/* d_mmap */
	0,		/* d_segmap */
	NULL,		/* d_ttys */
	DEV_FUNNEL_NULL,/* SMP-safe */
	0,		/* d_bflags */
	0		/* d_cflags */
};

struct controller *ipfilter_info[IPL_LOGSIZE];
static char ipfilter_name[] = "ipfilter";

struct	driver	ipfdriver = {
	NULL,		/* probe  */
	NULL,		/* slave */
	NULL,		/* cattach */
	NULL,		/* dattach */
	0,		/* go */
	0,		/* addr_list */
	0,		/* dev_name */
	0,		/* dev_list */
	ipfilter_name,	/* ctlr_name */
	ipfilter_info,	/* ctlr_list */
	0,		/* Want exclusive use of bdp's flag */
	0,		/* Size of first csr area */
	0,		/* Address space of first csr area */
	0,		/* Size of second csr area */
	0,		/* Address space of second csr area */
	NULL,		/* controller unattach routine */

	NULL,		/* dev_unattach */
};

#define NO_DEV -1
#define MAJOR_INSTANCE 1
#define IPF_NAME "ipf"

int ipfilter_config = FALSE;
int ipfilter_devno = NO_DEV;
int ipfilter_num_units=1;
int ipfilter_dev_number = NO_DEV;
int callback_return_status = ESUCCESS;
int driver_cfg_state;
int ipfilter_is_dynamic;

/* Variables referenced by attributes. 'majnum' must
 * be present to inform cfgmgr that this is a 5.0A
 * driver. 'ipfilter_version' is used to keep track of the version
 * numbers during development. It is printed out after
 * configure to indicate which version is being loaded.
 */
static int majnum = NO_DEV;
static int ipfilter_num_installed = 1;
static u_char ipfilter_desc[100] = "IPFilter firewall";
static u_char ipfilter_unused[300] = "";
static thread_t ipf_timeout = 0;

/* protos for functions used in configuration */
void ipfilter_print_attr_error __P((cfg_attr_t *));
void ipfilter_preconfig_callback __P((int, int, ulong, ulong));
void ipfilter_postconfig_callback __P((int, int, ulong, ulong));


struct firewall_stat {
	u_long	in_control_called;
	u_long	ip_input_mbufs;
	u_long	ip_input_accept;
	u_long	ip_input_reject;
	u_long	ip_input_tooshort;
	u_long	ip_input_toosmall;
	u_long	ip_input_badlen;
	u_long	ip_input_badhlen;
	u_long	ip_input_badvers;
	u_long	ip_input_badsum;
	u_long	ip_output_mbufs;
	u_long	ip_output_accept;
	u_long	ip_output_reject;
};
struct firewall_stat ipfilter_stat;
static	int	ipfilter_registered = 0;
static	int	ipftru64_inited = 0;
static	char	ipfname[CFG_ATTR_NAME_SZ] = "ipfilter";


cfg_subsys_attr_t ipfilter_attributes[] = {

{ "Subsystem_Description",CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
				(caddr_t)ipfilter_desc, 2, 300, 0 },
{ "Module_Config_Name",	CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
				(caddr_t)ipfname, 2, CFG_ATTR_NAME_SZ, 0 },
{ "majnum",		CFG_ATTR_INTTYPE, CFG_OP_QUERY,
				(caddr_t)&majnum, 0, 512, 0 },
{ "Num_Installed",	CFG_ATTR_INTTYPE, CFG_OP_QUERY,
				(caddr_t)&ipfilter_num_installed, 1, 1, 1 },
{ "Num_Units",		CFG_ATTR_INTTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)&ipfilter_num_units,
				1, IPL_LOGSIZE, 0 },
{ "Module_Type",	CFG_ATTR_STRTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)ipfilter_unused,
				2, CFG_ATTR_NAME_SZ, 0 },
{ "Device_User",	CFG_ATTR_STRTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)ipfilter_unused,
				0, sizeof(ipfilter_unused), 0 },
{ "Device_Group",	CFG_ATTR_STRTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)ipfilter_unused,
				0, sizeof(ipfilter_unused), 0 },
{ "Device_Mode",	CFG_ATTR_STRTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)ipfilter_unused,
				0, sizeof(ipfilter_unused), 0 },
{ "Device_Major_Req",	CFG_ATTR_STRTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)ipfilter_unused,
				0, sizeof(ipfilter_unused), 0 },
{ "Device_Char_Major",	CFG_ATTR_STRTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)ipfilter_unused,
				0, sizeof(ipfilter_unused), 0 },
{ "Device_Char_Files",	CFG_ATTR_STRTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)ipfilter_unused,
				0, sizeof(ipfilter_unused), 0 },
{ "Device_Char_Minor",	CFG_ATTR_STRTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
				(caddr_t)ipfilter_unused, 0,
				sizeof(ipfilter_unused), 0 },
{ "version",		CFG_ATTR_STRTYPE, CFG_OP_QUERY,
				(caddr_t)ipfilter_version, 0, 5, 0 },
{ "fr_chksrc",		CFG_ATTR_INTTYPE, CFG_OP_QUERY |
				CFG_OP_CONFIGURE | CFG_OP_RECONFIGURE,
				(caddr_t)&fr_chksrc, 0, 1, 0 },
{ "fr_minttl",		CFG_ATTR_INTTYPE, CFG_OP_QUERY |
				CFG_OP_CONFIGURE | CFG_OP_RECONFIGURE,
				(caddr_t)&fr_minttl, 0, 255, 0 },
{ "fr_pass",		CFG_ATTR_INTTYPE, CFG_OP_QUERY |
				CFG_OP_CONFIGURE | CFG_OP_RECONFIGURE,
				(caddr_t)&fr_minttl, 0, 0xffffffff, 0 },
{ "fr_flags",		CFG_ATTR_INTTYPE, CFG_OP_QUERY |
				CFG_OP_CONFIGURE | CFG_OP_RECONFIGURE,
				(caddr_t)&fr_minttl, 0, 0xffffffff, 0 },
{ "fr_active",		CFG_ATTR_INTTYPE, CFG_OP_QUERY,
				(caddr_t)&fr_minttl, 0, 1, 0 },
{ "fr_running",		CFG_ATTR_INTTYPE, CFG_OP_QUERY,
				(caddr_t)&fr_minttl, 0, 1, 0 },
{ "fr_control_forwarding",
			CFG_ATTR_INTTYPE, CFG_OP_QUERY |
				CFG_OP_CONFIGURE | CFG_OP_RECONFIGURE,
				(caddr_t) &fr_control_forwarding, 0, 1, 0 },
{ "fr_update_ipid",	CFG_ATTR_INTTYPE, CFG_OP_QUERY |
				CFG_OP_CONFIGURE | CFG_OP_RECONFIGURE,
				(caddr_t) &fr_update_ipid, 0, 1, 0 },
{ "",			0, 0, 0, 0, 0, 0 }
};


/*---------------------------------------------------------
 * Configure interface
 * 1 - Handle attribute verification.
 * 2 - Handle the differences between static and dynamic
 *     configuration.
 * Returns: EBUSY,EINVAL,ENOTSUP,ESRCH for fail,
 *          ESUCCESS for success
 *--------------------------------------------------------*/
cfg_status_t ipfilter_configure(op, indata, indata_size, outdata, outdata_size)
cfg_op_t op;
caddr_t indata;
ulong indata_size;
caddr_t outdata;
ulong outdata_size;
{
	int ipfilter_cfg_state;
	cfg_attr_t *attr_ptr;
	int status, i;

#ifdef	IPFDEBUG
	printf("ipfilter_configure(%x,%x,%d,%x,%d)\n",
	       op, indata, indata_size, outdata, outdata_size);
#endif

	status = ESUCCESS;

	if (cfgmgr_get_state("ipfilter", &ipfilter_cfg_state) != ESUCCESS) {
		printf("ipfilter cfgmgr_get_state failed\n");
		return EINVAL;
	}

	switch (op)
	{
	case CFG_OP_CONFIGURE :
#ifdef IPFDEBUG
		printf("ipfilter_config=%d\n",ipfilter_config);
		printf("driver_cfg_state=%d\n",driver_cfg_state);
#endif
		if (ipfilter_config == TRUE) {
			/*
			 * We have already been configured
			 */
			return EINVAL;
		}
		/*
		 * cfgmgr takes care of error checking, but we have to
		 * investigate what happened and report to user.
		 */
		attr_ptr = (cfg_attr_t *)indata;
		for(i = 0; i < indata_size; i++) {
			switch(attr_ptr->type)
			{
			case CFG_ATTR_STRTYPE:
				break;
			default:
				switch(attr_ptr->status)
				{
				case CFG_FRAME_SUCCESS:
					break;
				default:
					ipfilter_print_attr_error(attr_ptr);
					break;
				}
			}
			attr_ptr++;
		}

		if (cfgmgr_get_state((char *)ipfilter_name,
				     &driver_cfg_state) != ESUCCESS) {
			printf("ipfilter_conf:Error determining state of ipf subsystem\n");
			/*
			 * Error determining state of the "ipf" subs..
			 */
			return(EINVAL);
		}

		if (driver_cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED)  {
			callback_return_status = ESUCCESS;
			ipfilter_is_dynamic = 0;
			status = register_callback(ipfilter_configure_callback,
						   CFG_PT_OLD_CONF_ALL,
						   CFG_ORD_DONTCARE, 0L);
			if (status != ESUCCESS)
				break;
			status = register_callback(ipfilter_preconfig_callback,
						   CFG_PT_PRECONFIG,
						   CFG_ORD_NOMINAL, 0L);
			if (status != ESUCCESS)
				break;
			status = register_callback(ipfilter_postconfig_callback,
						   CFG_PT_POSTCONFIG,
						   CFG_ORD_NOMINAL, 0L);
		} else {
			/*
			 * ipf.mod is getting loaded dynamically.
			 */
			ipfilter_is_dynamic = 1;
			status = ipfilter_preconfig();
			if (status != ESUCCESS) {
				printf("ipfilter_conf:preconfig failed\n");
				return (status);
			}

			status = ipfilter_postconfig();
			if (status != ESUCCESS) {
				printf("ipfilter_conf:postconfig failed\n");
				return (status);
			}
			status = ipfilter_attach();
#ifdef IPFDEBUG
			printf("ipfilter_attach=%d\n",status);
#endif
		}
		break;

	case CFG_OP_QUERY :
		break;

	case CFG_OP_RECONFIGURE :
		break;

	case CFG_OP_UNCONFIGURE :
		if (ipfilter_is_dynamic) {

			status = devsw_del(ipfilter_name, MAJOR_INSTANCE);
			if (status == NO_DEV) {
				printf("ipfilter_configure: call to devsw_del failed\n");
				return ESRCH;
			}

			ipfilter_is_dynamic = 0;
			ipfilter_config = FALSE;

			printf("ipfilter_configure: dynamic module unconfigured\n");
		} else {
			/* static configuration */
			printf("ipfilter_configure: Can't unconfigure a static mod\n");
			return ESUCCESS;
		}
		if (ipfilter_cfg_state & SUBSYSTEM_STATICALLY_CONFIGURED) {
			status = EINVAL;
			break;
		}
		status = ipfilter_detach();
		break;

	default :
		status = ENOTSUP;
		break;
	}

#ifdef IPFDEBUG
	printf("ipfilter_configure(%d)=%d\n",op,status);
#endif
	return status;
}


void ipfilter_configure_callback(conf, order, arg, evarg)
int conf, order, arg, evarg;
{
	int status;

#ifdef	IPFDEBUG
	printf("ipfilter_configure_callback(%x,%x,%x,%x)\n",
	       conf, order, arg, evarg);
#endif

	status = ipfilter_attach();

	if (status != ESUCCESS) {
		cfgmgr_set_status("ipfilter");
	}
}


/*
 * This hook intercepts mbufs directly off the IP input
 * queue(s) and gives them to the routine at (*ip_input_hook)(m).
 * The hook must either free the mbuf, or enqueue it back to
 * an IP input queue by calling ip_continue_input(m). It is
 * assumed that the hook returns to the caller in a timely
 * manner, enqueuing the mbuf on its own list for further
 * processing at a later time (if necesary).
*/
void ipfilter_ip_input(m)
struct mbuf *m;
{
	struct mbuf *m0;
	struct ip *ip;
	int hlen, len;

#ifdef	IPFDEBUG
	printf("ipfilter_ip_input(%x)\n", m);
#endif
	READ_ENTER(&ipf_tru64);
	if (ipfilter_registered < 1 || ipftru64_inited == 0) {
		RWLOCK_EXIT(&ipf_tru64);
		ip_continue_input(m);
		return;
	}

	ipfilter_stat.ip_input_mbufs++;

	ip = mtod(m, struct ip *);

	/*
	 * Because this gets packets directly off the IP input queue, we need
	 * to do all of the input packet checking.  This is IPv4 only.
	 */
	if (m->m_len < sizeof(struct ip) &&
	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
		ipfilter_stat.ip_input_toosmall++;
		RWLOCK_EXIT(&ipf_tru64);
		return;
	}

	if (IP_V(ip) != IPVERSION) {
		ipfilter_stat.ip_input_badvers++;
		goto bad;
	}

	hlen = IP_HL(ip) << 2;
	if (hlen < sizeof(*ip)) {
		ipfilter_stat.ip_input_badhlen++;
		goto bad;
	}

	if (hlen > m->m_len) {
		if ((m = m_pullup(m, hlen)) == 0) {
			RWLOCK_EXIT(&ipf_tru64);
			return;
		}
		ip = mtod(m, struct ip *);
	}

	/*
	 * If header checksum verification fails, do no more.
	 */
	if (in_cksum(m, hlen) != 0) {
		ipfilter_stat.ip_input_badsum++;
		goto bad;
	}

	/*
	 * Convert fields to host representation.
	 */
	ip->ip_len = ntohs(ip->ip_len);
	len = ip->ip_len;
	if (len < hlen) {
		ipfilter_stat.ip_input_badlen++;
		goto bad;
	}
	if (m->m_pkthdr.len < len) {
		ipfilter_stat.ip_input_tooshort++;
		goto bad;
	}
	if (m->m_pkthdr.len > len) {
		if (m->m_len == m->m_pkthdr.len) {
			m->m_len = len;
			m->m_pkthdr.len = len;
		} else
			m_adj(m, len - m->m_pkthdr.len);
	}
	ip->ip_off = ntohs(ip->ip_off);

	if (fr_check(ip, hlen, m->m_pkthdr.rcvif, 0, &m) == 0) {
		if (m != NULL) {
			m->m_flags &= ~M_PROTOCOL_SUM|M_NOCHECKSUM|M_CHECKSUM;
			/*
			 * Convert back to network byte order.
			 */
			ip->ip_len = htons(ip->ip_len);
			ip->ip_off = htons(ip->ip_off);

			ipfilter_stat.ip_input_accept++;
			RWLOCK_EXIT(&ipf_tru64);
			ip_continue_input(m);
			return;
		}
	}
bad:
	RWLOCK_EXIT(&ipf_tru64);
	if (m != NULL)
		m_freem(m);
	return;
}


/*
 * This hook intercepts mbufs directly from the IP output
 * routine and gives them to the routine at
 * (*ip_output_hook)(ifp, m, in_ro, flags, imo).  The
 * hook must either free the mbuf, or set an mbuf flag
 * (m->m_flags |= M_OUTPUT_PROCESSING_DONE) and call
 * ip_output() again to transmit it.  It is assumed that
 * the hook returns to the caller in a timely manner, enqueuing
 * the mbuf on its own list for further processing at a later
 * time (if necesary).
 */
int ipfilter_ip_output(ifp, m, in_ro, flags, imo)
struct ifnet *ifp;
struct mbuf *m;
struct in_route *in_ro;
int flags;
struct ip_moptions *imo;
{
	struct ip *ip;
	int hlen;

#ifdef	IPFDEBUG
	printf("ipfilter_ip_output(%x,%x,%x,%x,%x)\n",
	       ifp, m, in_ro, flags, imo);
#endif
	READ_ENTER(&ipf_tru64);
	if (ipfilter_registered < 1 || ipftru64_inited == 0) {
		RWLOCK_EXIT(&ipf_tru64);
		return (ip_output(m, NULL, in_ro, flags, imo));
	}

	ipfilter_stat.ip_output_mbufs++;

	ip = mtod(m, struct ip *);
	hlen = IP_HL(ip);
	hlen <<= 2;

	if (fr_check(ip, hlen, ifp, 1, &m) == 0) {
		if (m != NULL) {
			m->m_flags |= M_OUTPUT_PROCESSING_DONE;
			RWLOCK_EXIT(&ipf_tru64);
			return (ip_output(m, NULL, in_ro, flags, imo));
		}
	}
	RWLOCK_EXIT(&ipf_tru64);
	return 0;
}


/*
 * This is a passive routine.  It does not implement any ioctl's, just merely
 * "snoops" ioctls being sent so that we can be aware of things like address
 * changes on the interfaces.
 */
void ipfilter_in_control(so, cmd, data, ifp)
struct socket *so;
u_int cmd;
caddr_t *data;
struct ifnet *ifp;
{
#ifdef	IPFDEBUG
	printf("ipfilter_in_control(%x,%x,%x,%x)\n", so, cmd, data, ifp);
#endif

	ipfilter_stat.in_control_called++;

	READ_ENTER(&ipf_tru64);
	if (ipfilter_registered < 1 || ipftru64_inited == 0) {
		RWLOCK_EXIT(&ipf_tru64);
		return;
	}

	if (cmd == (ioctlcmd_t)SIOCAIFADDR)
		frsync(NULL);

	RWLOCK_EXIT(&ipf_tru64);
}


/* "protocol" ifnet structure for accessing ipf statistics */
static struct {
	struct ifnet	ifnet;
	char		pad[sizeof(struct ether_driver) - sizeof(ifnet)];
} ipfilter_if;


/*
 * Routine to attach ipf module
 */
int
ipfilter_attach(void)
{
	struct firewall ipf;
	int status;

#ifdef	IPFDEBUG
	printf("ipfilter_attach(void)\n");
#endif

	RWLOCK_INIT(&ipf_tru64, 1);
	RWLOCK_INIT(&ipf_global, 1);
	RWLOCK_INIT(&ipf_mutex, 1);
	RWLOCK_INIT(&ipf_frcache, 1);
	ipftru64_inited = 1;

	status = ipfattach();
#ifdef	IPFDEBUG
	printf("ipfattach() = %d\n", status);
#endif
	if (status != ESUCCESS) {
		(void) ipfdetach();
		return status;
	}

	ipfilter_registered = 1;

	/*
	 * Initialize the struct, else the kernel might interpret
	 * a field as being valid when it's not, which would probably
	 * be fatal.
	 */
	bzero(&ipf, sizeof(struct firewall));

	/*
	 * Tell which version of the ipf structure we are passing-in.
	 * The only currently supported version is #1.
	 */
	ipf.version = FIREWALL_VERSION1;

	/*
	 * Tell which protocol family we wish to register.
	 * The only currently supported family is PF_INET.
	 */
	ipf.pf = PF_INET;

	/*
	 * Tell the kernel to disable the RPC/NFS "fastpath" code and
	 * send all NFS output packets through the "slow" IP output code.
	 */
	ipf.flags = FW_DISABLE_RPC_FASTPATH;

	/*
	 * Fill-in the hooks we wish to register.  At a minumum there
	 * must be an input and output hook.
	 */
	ipf.in_control_hook = ipfilter_in_control;
	ipf.fw_ip_input_hook = ipfilter_ip_input;
	ipf.fw_ip_output_hook = ipfilter_ip_output;

	/* make the call */
	status = register_firewall(&ipf);

	/*
	 * Additionally, if we wish to attach a protocol statistics structure,
	 * do it now.  We will only attach to counter ioctls on success.
	 */
	if (status == ESUCCESS) {
		char *defpass;

		ipfilter_registered = 2;
		ipfilter_ifattach();
		fr_running = 1;

		/*
		 * Start timeout thread
		 */
		ipf_timeout = kernel_thread_w_arg(first_task, ipfilter_timer,
						  NULL);
		timeout(ipfilter_clock, NULL,
			(hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT);

		if (FR_ISPASS(fr_pass))
			defpass = "pass";
		else if (FR_ISBLOCK(fr_pass))
			defpass = "block";
		else
			defpass = "no-match -> block";

		printf("%s initialized.  Default = %s all, Logging = %s%s\n",
			ipfilter_version, defpass,
#ifdef  IPFILTER_LOG
			"enabled",
#else
			"disabled",
#endif
#ifdef  IPFILTER_COMPILED
			" (COMPILED)"
#else
			""
#endif
			);
	}

	return (status);
}


/*
 * Routine to detach ipf module
 */
int
ipfilter_detach(void)
{
	int status;
	struct firewall ipf;

#ifdef	IPFDEBUG
	printf("ipfilter_detach(void)\n");
#endif

	WRITE_ENTER(&ipf_tru64);
	/*
	 * Initialize the struct, else the kernel might interpret
	 * a field as being valid when it's not, which would probably
	 * be fatal.
	 */
	bzero(&ipf, sizeof(struct firewall));

	/*
	 * If we had attached a protocol statistics structure, detach it now.
	 * We do this before un-registering our hooks.
	 */
	ipfilter_ifdetach();

	/*
	 * Tell which version of the ipf structure we are passing-in.
	 * The only currently supported version is #1.
	 */
	ipf.version = FIREWALL_VERSION1;

	/*
	 * Tell which protocol family we wish to register.
	 * The only currently supported family is PF_INET.
	 */
	ipf.pf = PF_INET;

	/*
	 * Tell the kernel to re-enable the RPC/NFS "fastpath" code and stop
	 * sending all NFS output packets through the "slow" IP output code.
	 */
	ipf.flags = FW_ENABLE_RPC_FASTPATH;

	/*
	 * We don't need to fill-in the hook pointers as they
	 * are not used in the unregister_ipf() routine.
	 */

	/* make the call */
	status = ESUCCESS;
	if (ipfilter_registered > 1) {
		status = unregister_firewall(&ipf);
		ipfilter_registered = 1;
	}

	if ((status == ESUCCESS) && (ipfilter_registered > 0)) {
		status = ipfdetach();
#ifdef	IPFDEBUG
		printf("ipfdetach() = %d\n", status);
#endif
		ipfilter_registered = 0;
	}

	if (status == ESUCCESS) {
		fr_running = 0;
		RWLOCK_EXIT(&ipf_tru64);
		if (ipftru64_inited == 1) {
			RW_DESTROY(&ipf_tru64);
			RW_DESTROY(&ipf_global);
			RW_DESTROY(&ipf_frcache);
			RW_DESTROY(&ipf_mutex);
			ipftru64_inited = 0;
		}
#if 0
		if (ipf_timeout != 0) {
			thread_terminate(ipf_timeout);
			ipf_timeout = 0;
		}
#else
		/*
		 * Deschedule the timeout, kill the thread that is wiating on
		 * it and then wait one second for that thread to die.
		 */
		untimeout(ipfilter_clock, NULL);
		thread_wakeup_one((vm_offset_t)&ipf_timeout);

		while (ipf_timeout != 0) {
			assert_wait_mesg((vm_offset_t)&ipf_timeout,
					 TRUE, "ipftimeout");
			thread_set_timeout(hz);
			thread_block();
		}
#endif
		printf("%s unloaded\n", ipfilter_version);
	} else {
		RWLOCK_EXIT(&ipf_tru64);
	}

	return (status);
}


/*
 * Routine to attach protocol statistics structure
 */
void ipfilter_ifattach(void)
{
	struct ifnet *ifp = &(ipfilter_if.ifnet);

#ifdef	IPFDEBUG
	printf("ipfilter_ifattach(void)\n");
#endif

	ifp->if_name = "ipf";
	ifp->if_version = "Network Packet Filter";
	ifp->if_mtu = IP_MAXPACKET;
	ifp->if_flags = IFF_UP;
	ifp->if_ioctl = ipfilter_ifioctl;
	ifp->if_output = ipfilter_ifoutput;
	ifp->if_type = IFT_OTHER;
	ifp->if_hdrlen = 8;
	ifp->if_affinity = NETALLCPU;
	ifp->if_addrlen = 0;

	if_proto_attach(ifp);
}


/*
 * Routine to detach protocol statistics structure
 */
void ipfilter_ifdetach(void)
{
	struct ifnet *ifp = &(ipfilter_if.ifnet);

#ifdef	IPFDEBUG
	printf("ipfilter_ifdetach(void)\n");
#endif

	if_proto_detach(ifp);
}


/*
 * Routine to handle protocol ioctls
 */
int ipfilter_ifioctl(ifp, cmd, data)
register struct ifnet *ifp;
unsigned int cmd;
caddr_t data;
{
	return(EOPNOTSUPP);
}

/* Stub output routine.  Should never be called, but just in case... */
#if TRU64 >= 1885
int ipfilter_ifoutput(ifp, m0, dst, rt, cp)
char *cp;
#else
int ipfilter_ifoutput(ifp, m0, dst, rt)
#endif
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
struct rtentry *rt;
{
	return(EOPNOTSUPP);
}


/* called from ipfilter_configure(CFG_OP_CONFIGURE,...) */
void ipfilter_print_attr_error(cfg_attr_t *attr_ptr)
{
	printf("%s:", attr_ptr->name);

	switch(attr_ptr->status)
	{
	case CFG_ATTR_EEXISTS:
		printf("Attribute does not exist\n");
		break;
	case CFG_ATTR_EOP:
		printf("Attribute does not support operation\n");
		break;
	case CFG_ATTR_ESUBSYS:
		printf("Subsystem Failure\n");
		break;
	case CFG_ATTR_ESMALL:
		printf("Attribute size/value too small\n");
		break;
	case CFG_ATTR_ELARGE:
		printf("Attribute size/value too large\n");
		break;
	case CFG_ATTR_ETYPE:
		printf("Attribute invalid type\n");
		break;
	case CFG_ATTR_EINDEX:
		printf("Attribute invalid index\n");
		break;
	case CFG_ATTR_EMEM:
		printf("Attribute memory allocation error\n");
		break;
	default:
		printf("**Unknown attribute: ");
		printf("%x\n", attr_ptr->status);
	}
}

/*---------------------------------------------------------
* pre-configuration
* Returns: ENOMEM for fail.
*          ESUCCESS for success.
*--------------------------------------------------------*/
int ipfilter_preconfig()
{
	int status;

	return ESUCCESS;
}

void
ipfilter_preconfig_callback(int point, int order, ulong argument, ulong event_arg)
{
	int status;
	struct hwconfig *hwc;
	hwc = create_hwconfig_struct();

	if (hwc == NULL)
		return;

	/*
	 * Add ourselves to the switch table.
	 */
	majnum = devsw_add(ipfilter_name, MAJOR_INSTANCE, NO_DEV, &ipfilter_devsw_entry);
	if (majnum == NO_DEV) {
		printf("devsw_add_failed\n");
		(void)cfgmgr_set_status(ipfilter_name);
		callback_return_status = ENODEV;
		return;
	}
	printf("devsw_add_majnum=%d\n", majnum);

	hwc->type = HWCONFIG_CONTROLLER_ALL | HWCONFIG_CONFIGURE_REQUEST;

	hwc->parent_bus = HWCONFIG_ALL;
	hwc->parent_bus_instance = DRIVER_WILDNUM;
	hwc->driver_name = ipfilter_name;
	hwc->driver_struct = &ipfdriver;
	hwc->instance_info = -1;

	status = driver_framework(hwc);
	free_hwconfig_struct(hwc);

	if (status != ESUCCESS) {
		printf("Cannot configure driver %s-- status = %d\n",
		       ipfilter_name, status);
		return;
	}

}

/*---------------------------------------------------------
* post configure
* Return: ENODEV on fail.
*         ESUCCESS on success.
*--------------------------------------------------------*/
int ipfilter_postconfig()
{

	majnum = devsw_add(ipfilter_name,MAJOR_INSTANCE, majnum, &ipfilter_devsw_entry);

	if (majnum == ENODEV) {
		printf("ipfilter_postconfig: call to devsw_add failed\n");
		return ENODEV;
	}

	printf("ipfilter_postconfig:major # = %d\n",majnum);

	ipfilter_devno = majnum;
	ipfilter_config = TRUE;

	printf("%s configured\n", ipfilter_version);

	return ESUCCESS;
}

void
ipfilter_postconfig_callback(int point, int order, ulong argument, ulong event_arg)
{
	int status;

	if (callback_return_status != ESUCCESS)
			return;

	status  = ipfilter_postconfig();
	if (status != ESUCCESS) {
		cfgmgr_set_status(ipfilter_name);
		callback_return_status = status;
	}
}


/*---------------------------------------------------------
 * Open
 * Return: ENXIO on fail.
 *         ESUCCESS on success.
 *--------------------------------------------------------*/
int ipfilteropen(dev_t dev, int flag, int format)
{
	int unit;

	unit = minor(dev);

	if ((IPL_LOGMAX < unit) || (unit < 0))
		unit = ENXIO;
	else
		unit = ESUCCESS;

	return unit;
}

/*---------------------------------------------------------
* Close
* Return: ESUCCESS on success.
*         ENXIO on fail.
*--------------------------------------------------------*/
int ipfilterclose(dev_t dev, int flag, int format)
{
	int unit;

#ifdef  IPFDEBUG
	printf("ipfilter_close(%x,%x,%x)\n", dev, flag, format);
#endif

	unit = minor(dev);

	if ((IPL_LOGMAX < unit) || (unit < 0))
		unit = ENXIO;
	else
		unit = ESUCCESS;

	return unit;
}


/*---------------------------------------------------------
* Read -
* return: ESUCCESS on success.
*         fail: An error number from errno.h
*--------------------------------------------------------*/
int ipfilterread(dev_t dev, struct uio *uio, int flag)
{
#ifdef	IPFILTER_LOG
	int unit, status;

	unit = minor(dev);

# ifdef  IPFDEBUG
	printf("ipfread(%x,%lx,%x)\n", dev, uio, flag);
# endif
	status = ipflog_read(unit, uio);

	return status;
#else
	return ENXIO;
#endif
}


/*---------------------------------------------------------
* Write -
* return: ESUCCESS on success.
*         fail: An error number from errno.h
*--------------------------------------------------------*/
#ifdef	IPFILTER_SYNC
int
ipfilterwrite(dev_t dev, struct uio *uio)
{
	int unit;

# ifdef  IPFDEBUG
	printf("ipfilter_write(%x,%lx)\n", dev, uio);
# endif
        if (getminor(dev) != IPL_LOGSYNC)
                return ENXIO;
        return ipfsync_write(uio);

}
#endif


/*---------------------------------------------------------
* IOCTL
* Return: ESUCCESS on success.
*         fail: An error number from errno.h
*--------------------------------------------------------*/
int ipfilterioctl(dev_t dev, unsigned int cmd, caddr_t data, int flag)
{
	int err;

#ifdef	IPFDEBUG
	printf("ipfilterioctl(%d(%d),%x,%lx,%x)\n", dev,
		getminor(dev), cmd, data, flag);
#endif
	READ_ENTER(&ipf_tru64);
	if (ipfilter_registered < 1 || ipftru64_inited == 0) {
		RWLOCK_EXIT(&ipf_tru64);
#ifdef IPFDEBUG
		printf("ipfilter_registered %d ipftru64_inited %d\n",
			ipfilter_registered, ipftru64_inited);
#endif
		return EIO;
	}
	err = iplioctl(dev, cmd, data, flag);
	RWLOCK_EXIT(&ipf_tru64);
	return err;
}


void ipfilter_clock(void *arg)
{
	thread_wakeup_one((vm_offset_t)&ipf_timeout);
	if (fr_running != 0) {
		timeout(ipfilter_clock, NULL,
			(hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT);
	}
}


void ipfilter_timer()
{
#if 0
	lock_data_t ipfdelaylock;

	lock_init(&ipfdelaylock, TRUE);
#else
	simple_lock_data_t ipfdelaylock;

	simple_lock_init(&ipfdelaylock);
	simple_lock(&ipfdelaylock);
#endif

	while (1) {
#if 0
		lock_write(&ipfdelaylock);
		assert_wait_mesg((vm_offset_t)&ipfdelaylock, TRUE, "ipftimer");
		lock_done(&ipfdelaylock);

		thread_set_timeout((hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT);

		thread_block();
#else
		if (fr_running == 0)
			break;
		mpsleep((vm_offset_t)&ipf_timeout, PCATCH,
			"ipftimer", 0, &ipfdelaylock,
			MS_LOCK_SIMPLE | MS_LOCK_ON_ERROR);
#endif

		if (fr_running == 0)
			break;
		simple_unlock(&ipfdelaylock);
		fr_slowtimer(NULL);
		simple_lock(&ipfdelaylock);
	}

	simple_unlock(&ipfdelaylock);
	simple_lock_terminate(&ipfdelaylock);

	if (ipf_timeout != 0) {
		thread_terminate(ipf_timeout);
		ipf_timeout = 0;
	}
	thread_halt_self();
}
