#!/bin/sh
#
IPFBASE=/etc/opt/ipf

PATH=/bin:/sbin:/usr/sbin:${PATH}:/opt/ipf/bin
IPFILCONF=${IPFBASE}/ipf.conf
IP6FILCONF=${IPFBASE}/ipf6.conf
IPNATCONF=${IPFBASE}/ipnat.conf
IPPOOLCONF=${IPFBASE}/ippool.conf
PFILCHECKED=no
if [ -d /var/run ] ; then
	PIDFILE=/var/run/ipmon.pid
else
	PIDFILE=${IPFBASE}/ipmon.pid
fi


getpid()
{
	if [ -f /usr/bin/pgrep ] ; then
		rval=`pgrep $1`
	else
		rval=`ps -ef | awk "/$1/ { print \\$2; } " -`
	fi

	return $rval
}

logmsg()
{
	logger -p local0.emerg -t ipfilter "$1"
	echo "$1" >&2
}


checkpfil()
{
	if [ $PFILCHECKED = yes ] ; then
		return
	fi
	if [ -z "`ndd /dev/pfil qif_status 2>/dev/null`" ] ; then
		logmsg "pfil not available to support ipfilter"
		exit 1
	fi
	if [ `uname -r|cut -d. -f2` -gt 7 ] ; then
		realnic=`/sbin/ifconfig -a modlist 2>/dev/null | grep -c pfil`
	else
		for i in `ifconfig -a | cut -d: -f1 | egrep -v '[ 	]|^lo'`
		do
			if strconf -m pfil < /dev/$i >/dev/null 2>&1 ; then
				realnic=1;
				break;
			fi
		done
	fi
	if [ $realnic -eq 0 ] ; then
		logmsg "pfil not configured for firewall/NAT operation"
	fi
	PFILCHECKED=yes
}


getids()
{
	ipfid=`modinfo 2>&1 | awk '/ipf / { print $1 } ' - 2>/dev/null`
	ipfruleid=`modinfo 2>&1 | awk '/ipfrule/ { print $1 } ' - 2>/dev/null`
	if [ -f $PIDFILE ] ; then
		pid=`cat $PIDFILE 2>/dev/null`
	else
		getpid ipmon
		pid=$?
	fi
}


block_default_workaround() {
	ipf -F a
	echo "constructing minimal name resolution rules..."
	NAMESERVERS=`cat /etc/resolv.conf  2>/dev/null| \
		     nawk '/nameserver/ {printf "%s ", $2}' 2>/dev/null`
	if [ -z "$NAMESERVERS" ] ; then
		return
	fi
	for NS in $NAMESERVERS ; do
		IF_TO_NS=`route -n get $NS  2>/dev/null| \
			  nawk '$1 == "interface:" { print $NF ; exit }' \
			  2>/dev/null`
		if [ -z "$IF_TO_NS" ] ; then
			continue
		fi
		IP_TO_NS=`ifconfig $IF_TO_NS  2>/dev/null| \
			nawk 'NR == "2" { print $2 ; exit }' 2>/dev/null`
		if [ -z "$IP_TO_NS" ] ; then
			continue
		fi
		echo "pass out quick on $IF_TO_NS proto udp from $IP_TO_NS to $NS port = 53 keep state" | \
		ipf -f -
	done
}


load_ipf_config() {
	bad=0
	if [ -r ${IPFILCONF} ]; then
		checkpfil
		if `ipf -V | \
		      nawk '$1 == "Default:" && $2 == "pass" { exit 1 }'` ; then
			block_default_workaround
		fi
		ipf -IFa -f ${IPFILCONF}
		if [ $? != 0 ]; then
			echo "$0: load of ${IPFILCONF} into alternate set failed"
			bad=1
		fi
	fi
	if [ -r ${IP6FILCONF} ]; then
		checkpfil
		ipf -6IFa -f ${IP6FILCONF}
		if [ $? != 0 ]; then
			echo "$0: load of ${IPFILCONF} into alternate set failed"
			bad=1
		fi
	fi
	if [ $bad -eq 0 ] ; then
		ipf -s -y
	else
		echo Not switching config due to load error.
	fi
}


load_ipnat_config() {
	if [ -r ${IPNATCONF} ]; then
		checkpfil
		ipnat -CF -f ${IPNATCONF}
		if [ $? != 0 ]; then
			echo "$0: load of ${IPNATCONF} failed"
		else
			ipf -y
		fi
	fi
}


load_ippool_config() {
	if [ -r ${IPPOOLCONF} ]; then
		checkpfil
		ippool -F
		ippool -f ${IPPOOLCONF}
		if [ $? != 0 ]; then
			echo "$0: load of ${IPPOOLCONF} failed"
		fi
	fi
}


case "$1" in
	start)
		getids
		[ -n "$pid" ] && kill -TERM $pid 2>/dev/null
		[ -n "$ipfruleid" ] && modunload -i $ipfruleid 2>/dev/null
		[ -n "$ipfid" ] && modunload -i $ipfid 2>/dev/null
		modload /usr/kernel/drv/ipf
		if [ -f /usr/kernel/drv/ipfrule ] ; then
			modload /usr/kernel/drv/ipfrule
		fi
		load_ippool_config
		load_ipf_config
		load_ipnat_config
		ipmon -Ds
		;;

	stop)
		getids
		[ -n "$pid" ] && kill -TERM $pid
		/bin/rm -f $PIDFILE
		[ -n "$ipfruleid" ] && modunload -i $ipfruleid 2>/dev/null
		[ -n "$ipfid" ] && modunload -i $ipfid
		;;

	pause)
		getids
		ipfs -l
		ipfs -NS -w
		ipf -D
		if [ -f $PIDFILE ] ; then
			if kill -0 $pid; then
				kill -TERM $pid
			else    
				cp /dev/null $PIDFILE
			fi
		fi      
		;;

	resume)
		getids
		ipf -E
		ipfs -R
		load_ippool_config
		load_ipf_config
		load_ipnat_config
		if [ -f $PIDFILE -a x$pid != x ] ; then
			ipmon -Ds
		fi
		;;

	reload)
		load_ippool_config
		load_ipf_config
		load_ipnat_config
		;;

	reipf)
		load_ipf_config
		;;

	reipnat)
		load_ipnat_config
		;;

	*)
		echo "Usage: $0 (start|stop|reload|reipf|reipnat|pause|resume)" >&2
		exit 1
		;;

esac
exit 0
