#
# Shell script portion that sets up head and tail templates for 
# chain function compilation
#
DEFSDIR="$ETCDIR/ipfilter-defs"
OUTFILE="$ETCDIR/ipfilter-defs-compiled.conf"
INDEFS="$DEFSDIR/*.def"
PROTODEFS="$DEFSDIR/prototypes-defs"
NETDEFS="$DEFSDIR/network-defs"
PROTOFILE="$DEFSDIR/prototypes.sh"
MASQFILE="$DEFSDIR/masq-defs"
DNATFILE="$DEFSDIR/dnat-defs"
EVERYSOURCE="$INDEFS $DEFSDIR/*-defs $DEFSDIR/*.sh $ETCDIR/netscript-compile.conf $ETCDIR/ipfilter-defs.conf"
FWDCHAIN="ipfwd"
LOCALCHAIN="iplcl"
# The minimum compiled history file kept, 1-34
BACKUP_LEVELS=2

# Set up INPUT chain tokens
INPUT_NET="INPUT_NET"
INPUT_REGN="$INPUT_NET INPUT_IF"

# Read in network definitions file
eval `cat "$NETDEFS" | read_netdefn`

for F in $PROTOFILE; do
	[ -f $F ] && source $F
done

ipv4_Compile_Read_Line () {
	local KEY TYPE DIRN ARGS DPORT
	local RETCODE=0
	local DIRN_STUFF
	ARG_ERROR="fN ipv4_Compile_Read_Line: $*"

	local CHAIN="$1"
	local LEFT_IF="$2"
	local RIGHT_IF="$3"
	
	local DEF_LOG_TEXT="Chain: \$CHAIN"
	local LOG_TEXT="\"$DEF_LOG_TEXT - \""
	local LOG_OPTS="--log-ip-options --log-tcp-options -m limit --limit \$LOG_MAXRATE/second"

	while read KEY TYPE DIRN ARGS; do

                # Handle comments and empty lines
                [ -z "$KEY" ] && continue
                echo $KEY | grep -q '^#' && continue
		ARGS=`echo "$ARGS" | sed -e 's/\(.*\)#.*/\1/'`

		[ "$KEY" != "$CHAIN" ] && continue

		# Put $ARGS into positional arguments so we can shift etc...
		# Evaled so that $ environment variables work in .def files...
		case $TYPE in
		COMMENT|LOG|LOG_MSG)
			eval set -- "\"$ARGS\""
			;;
		*)  
			eval set -- "$ARGS"
			;;
		esac

		case $DIRN in
		L2R|LEFT2RIGHT|INTERNAL2EXTERNAL|INTERN2EXTERN|I2E|INT2EXT)
			if [ -z "$LEFT_IF" -o -z "$RIGHT_IF" ]; then
				echo 1>&2
				echo "  $PROGNAME: Error: $KEY $TYPE $DIRN $ARGS" 1>&2
				echo "             Direction not supported with no interfaces." 1>&2
				echo 1>&2
				RETCODE=1
				continue
			fi 
			DIRN_STUFF="-i $LEFT_IF -o $RIGHT_IF"
			;;
		R2L|RIGHT2LEFT|EXTERNAL2INTERNAL|EXTERN2INTERN|E2I|EXT2INT)
			if [ -z "$LEFT_IF" -o -z "$RIGHT_IF" ]; then
				echo 1>&2
				echo "  $PROGNAME: Error: $KEY $TYPE $DIRN $ARGS" 1>&2
				echo "             Direction not supported with no interfaces." 1>&2
				echo 1>&2
				RETCODE=1
				continue
			fi 
			DIRN_STUFF="-i $RIGHT_IF -o $LEFT_IF"
			;;
		BOTH|"-")
			unset DIRN_STUFF
			;;
		*)
				echo 1>&2
			echo "  $PROGNAME: Error: $KEY $TYPE $DIRN $ARGS" 1>&2
			echo "             Direction $DIRN not supported." 1>&2
	 		echo "             Valid directions are BOTH,L2R,R2L,INT2EXT,EXT2INT" 1>&2
				echo 1>&2
			RETCODE=1
			continue
			;;
		esac

		# Update this list when you add a new type.	
		local VALID_TYPES="COMMENT,MACRO,REJECT_SMB,DROP_MARTIANS,LOG_PORTSCAN,DROP_BROADCAST,LOG_BROADCAST,ACCEPT_EST,ACCEPT_RELATED,ACCEPT_PROTO,REJECT_PROTO,DROP_PROTO,LOG_PROTO,ACCEPT_TCP_NET,ACCEPT_UDP_NET,DROP_TCP_NET,DROP_UDP_NET,LOG_TCP_NET,LOG_UDP_NET,REJECT_TCP_NET,REJECT_UDP_NET,ACCEPT_TCP,REJECT_TCP,DROP_TCP,LOG_TCP,ACCEPT_UDP,REJECT_UDP,DROP_UDP,LOG_UDP,ACCEPT_PING,DROP_PING,LOG_PING,ACCEPT_IFACE,REJECT_IFACE,DROP_IFACE,LOG_IFACE,ACCEPT_NET,REJECT_NET,DROP_NET,LOG_NET,LOG_MSG,LOG_MSG_RESET,LOG"

	
		case $TYPE in
		MACRO)
			case $1 in
			MACRO_*)
				;;
			*)
				echo 1>&2
	                        echo "  $PROGNAME: Error: $KEY $TYPE $DIRN $ARGS" 1>&2
	                        echo "             Macro names must be of the form MACRO_*" 1>&2
				echo 1>&2
				RETCODE=1
				continue
				;;
			esac
			if ! grep -h "^[ 	]*${1}" $PROTODEFS $INDEFS \
						| ipv4_Compile_Read_Line \
						"$1" \
						$LEFT_IF $RIGHT_IF
			then
				RETCODE=1
			fi
			;;
		COMMENT)
			echo "# $*" >> $OUTFILE
			;;
		REJECT_SMB)
			echo "ipv4_createChain smb smb" >> $OUTFILE
			echo "\$IPTBL -A \$CHAIN -j smb $DIRN_STUFF" >> $OUTFILE
			;;
		DROP_MARTIANS)
			echo "ipv4_createChain martians martians" >> $OUTFILE
			echo "\$IPTBL -A \$CHAIN -j martians $DIRN_STUFF" >> $OUTFILE
			;;
		LOG_PORTSCAN)
			echo "ipv4_createChain portscan portscan" >> $OUTFILE
			echo "\$IPTBL -A \$CHAIN -j portscan -m psd $DIRN_STUFF" >> $OUTFILE
			;;
		DROP_BROADCAST)
			echo "\$IPTBL -A \$CHAIN -j DROP $DIRN_STUFF -m pkttype --pkt-type broadcast" >> $OUTFILE
			;;
		LOG_BROADCAST)
			local LOG_TXT="--log-prefix $LOG_TEXT"
			echo "\$IPTBL -A \$CHAIN -j LOG $DIRN_STUFF -m pkttype --pkt-type broadcast $LOG_OPTS $LOG_TXT" >> $OUTFILE
			;;
		ACCEPT_EST)
			echo "\$IPTBL -A \$CHAIN -j ACCEPT $DIRN_STUFF -m state --state RELATED,ESTABLISHED" >> $OUTFILE
			;;
		ACCEPT_RELATED)
			echo "\$IPTBL -A \$CHAIN -j ACCEPT $DIRN_STUFF -m state --state RELATED" >> $OUTFILE
			;;
		ACCEPT_PROTO|ACCEPT_PROTOCOL)
			! arg_check protocol "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -j ACCEPT $DIRN_STUFF -m state --state NEW -p $1" >> $OUTFILE
			;;
		REJECT_PROTO|REJECT_PROTOCOL)
			! arg_check protocol "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -j REJECT $DIRN_STUFF -m state --state NEW -p $1" >> $OUTFILE
			;;
		DROP_PROTO|DROP_PROTOCOL)
			! arg_check protocol "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -j DROP $DIRN_STUFF -p $1" >> $OUTFILE
			;;
		LOG_PROTO|LOG_PROTOCOL)
			! arg_check protocol "$1" && RETCODE=1 && continue
			local LOG_TXT="--log-prefix $LOG_TEXT"
			echo "\$IPTBL -A \$CHAIN -j LOG $DIRN_STUFF -m state --state NEW -p $1 $LOG_OPTS $LOG_TXT" >> $OUTFILE
			;;
		ACCEPT_TCP_NET|ACCEPT_UDP_NET|DROP_TCP_NET|DROP_UDP_NET|REJECT_TCP_NET|REJECT_UDP_NET|LOG_UDP_NET|LOG_TCP_NET)
			# Sort out what we are doing...
			case $TYPE in
			ACCEPT_TCP_NET)
				local PROTO="tcp"
				local TARG="ACCEPT"
				local EXTRA="-m state --state NEW"
				;;
			ACCEPT_UDP_NET)
				local PROTO="udp"
				local TARG="ACCEPT"
				local EXTRA="-m state --state NEW"
				;;
			DROP_TCP_NET)
				local PROTO="tcp"
				local TARG="DROP"
				local EXTRA=""
				;;
			DROP_UDP_NET)
				local PROTO="udp"
				local TARG="DROP"
				local EXTRA=""
				;;
			REJECT_TCP_NET)
				local PROTO="tcp"
				local TARG="REJECT"
				local EXTRA="-m state --state NEW"
				;;
			REJECT_UDP_NET)
				local PROTO="udp"
				local TARG="REJECT"
				local EXTRA="-m state --state NEW"
				;;
			LOG_TCP_NET)
				local LOG_TXT="--log-prefix $LOG_TEXT"
				local PROTO="tcp"
				local TARG="LOG"
				local EXTRA="-m state --state NEW $LOG_OPTS $LOG_TXT"
				;;
			LOG_UDP_NET)
				local LOG_TXT="--log-prefix $LOG_TEXT"
				local PROTO="udp"
				local TARG="LOG"
				local EXTRA="-m state --state NEW $LOG_OPTS $LOG_TXT"
				;;
			esac
			if [ $# -lt 2 -o $# -gt 4  ] 
			then
				echo 1>&2
				echo "  $PROGNAME: Error: $KEY $TYPE $DIRN $ARGS" 1>&2
				echo "             Usage: $TYPE [src-net [src-ports]] <dst-net> <dst-ports>" 1>&2
				echo "             This key requires 2 - 4 arguments." 1>&2
				echo 1>&2
				RETCODE=1
			fi
			case $# in
			2)
				local SRC_NET=""
				local SRC_PORTS=""
				local DST_NET="$1"
				local DST_PORTS="$2"
				;;
			3)
				local SRC_NET="$1"
				local SRC_PORTS=""
				local DST_NET="$2"
				local DST_PORTS="$3"
				;;
			4)
				local SRC_NET="$1"
				local SRC_PORTS="$2"
				local DST_NET="$3"
				local DST_PORTS="$4"
				;;
			esac
			! arg_check ipv4_net "$SRC_NET" && RETCODE=1
			! arg_check port "$SRC_PORTS" && RETCODE=1
			! arg_check ipv4_net "$DST_NET" && RETCODE=1
			! arg_check port "$DST_PORTS" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue
			
			[ -n "$SRC_NET" ] && SRC_NET="-s $SRC_NET " 
			[ -n "$SRC_PORTS" ] && SRC_PORTS="--sport $SRC_PORTS " 
			[ -n "$DST_NET" ] && DST_NET="-d $DST_NET " 
			[ -n "$DST_PORTS" ] && DST_PORTS="--dport $DST_PORTS" 
			echo "\$IPTBL -A \$CHAIN -j $TARG $DIRN_STUFF $EXTRA -p $PROTO ${SRC_NET}${SRC_PORTS}${DST_NET}${DST_PORTS}" \
				>> $OUTFILE
			;;
		ACCEPT_TCP)
			! arg_check port "$1" && RETCODE=1
			! arg_check port "$2" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue 
			local PORTS="--dport $1"
			[ -n "$2" ] && PORTS="--sport $1 --dport $2"
			echo "\$IPTBL -A \$CHAIN -j ACCEPT $DIRN_STUFF -m state --state NEW -p tcp $PORTS" >> $OUTFILE
			;;
		REJECT_TCP)
			! arg_check port "$1" && RETCODE=1
			! arg_check port "$2" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue 
			local PORTS="--dport $1"
			[ -n "$2" ] && PORTS="--sport $1 --dport $2"
			echo "\$IPTBL -A \$CHAIN -j REJECT $DIRN_STUFF -m state --state NEW -p tcp $PORTS" >> $OUTFILE
			;;
		DROP_TCP)
			! arg_check port "$1" && RETCODE=1
			! arg_check port "$2" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue 
			local PORTS="--dport $1"
			[ -n "$2" ] && PORTS="--sport $1 --dport $2"
			echo "\$IPTBL -A \$CHAIN -j DROP $DIRN_STUFF -p tcp $PORTS" >> $OUTFILE
			;;
		LOG_TCP)
			! arg_check port "$1" && RETCODE=1
			! arg_check port "$2" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue 
			local PORTS="--dport $1"
			[ -n "$2" ] && PORTS="--sport $1 --dport $2"
			local LOG_TXT="--log-prefix $LOG_TEXT"
			echo "\$IPTBL -A \$CHAIN -j LOG $DIRN_STUFF -m state --state NEW $LOG_OPTS $LOG_TXT -p tcp $PORTS" >> $OUTFILE
			;;
		ACCEPT_UDP)
			! arg_check port "$1" && RETCODE=1
			! arg_check port "$2" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue 
			local PORTS="--dport $1"
			[ -n "$2" ] && PORTS="--sport $1 --dport $2"
			echo "\$IPTBL -A \$CHAIN -j ACCEPT $DIRN_STUFF -m state --state NEW -p udp $PORTS" >> $OUTFILE
			;;
		REJECT_UDP)
			! arg_check port "$1" && RETCODE=1
			! arg_check port "$2" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue 
			local PORTS="--dport $1"
			[ -n "$2" ] && PORTS="--sport $1 --dport $2"
			echo "\$IPTBL -A \$CHAIN -j REJECT $DIRN_STUFF -m state --state NEW -p udp $PORTS" >> $OUTFILE
			;;
		DROP_UDP)
			! arg_check port "$1" && RETCODE=1
			! arg_check port "$2" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue 
			local PORTS="--dport $1"
			[ -n "$2" ] && PORTS="--sport $1 --dport $2"
			echo "\$IPTBL -A \$CHAIN -j DROP $DIRN_STUFF -p udp $PORTS" >> $OUTFILE
			;;
		LOG_UDP)
			! arg_check port "$1" && RETCODE=1
			! arg_check port "$2" && RETCODE=1
			[ $RETCODE -eq 1 ] && continue 
			local PORTS="--dport $1"
			[ -n "$2" ] && PORTS="--sport $1 --dport $2"
			local LOG_TXT="--log-prefix $LOG_TEXT"
			echo "\$IPTBL -A \$CHAIN -j LOG $DIRN_STUFF -m state --state NEW $LOG_OPTS $LOG_TXT -p udp $PORTS" >> $OUTFILE
			;;
		ACCEPT_PING)
			echo "\$IPTBL -A \$CHAIN -j ACCEPT $DIRN_STUFF -m state --state NEW -p icmp --icmp-type echo-request" >> $OUTFILE
			;;
		DROP_PING)
			echo "\$IPTBL -A \$CHAIN -j DROP $DIRN_STUFF -p icmp --icmp-type echo-request" >> $OUTFILE
			;;
		LOG_PING)
			local LOG_TXT="--log-prefix $LOG_TEXT"
			echo "\$IPTBL -A \$CHAIN -j LOG $DIRN_STUFF -m state --state NEW -p icmp --icmp-type echo-request $LOG_OPTS $LOG_TXT" >> $OUTFILE
			;;
		ACCEPT_IFACE)
			! arg_check interface "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -m state --state NEW -j ACCEPT -i $1" >> $OUTFILE
			;;
		REJECT_IFACE)
			! arg_check interface "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -m state --state NEW -j REJECT -i $1" >> $OUTFILE
			;;
		DROP_IFACE)
			! arg_check interface "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -j DROP -i $1" >> $OUTFILE
			;;
		LOG_IFACE)
			! arg_check interface "$1" && RETCODE=1 && continue
			local LOG_TXT="--log-prefix $LOG_TEXT"
			echo "\$IPTBL -A \$CHAIN -j LOG -m state --state NEW -i $1 $LOG_OPTS $LOG_TXT" >> $OUTFILE
			;;
		ACCEPT_NET)
			! arg_check ipv4_net "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -j ACCEPT $DIRN_STUFF -m state --state NEW -s $1" >> $OUTFILE
			;;
		REJECT_NET)
			! arg_check ipv4_net "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -j REJECT $DIRN_STUFF -m state --state NEW -s $1" >> $OUTFILE
			;;
		DROP_NET)
			! arg_check ipv4_net "$1" && RETCODE=1 && continue
			echo "\$IPTBL -A \$CHAIN -j DROP $DIRN_STUFF -s $1" >> $OUTFILE
			;;
		LOG_NET)
			! arg_check ipv4_net "$1" && RETCODE=1 && continue
			local LOG_TXT="--log-prefix $LOG_TEXT"
			echo "\$IPTBL -A \$CHAIN -j LOG $DIRN_STUFF -m state --state NEW -s $1 $LOG_OPTS $LOG_TXT" >> $OUTFILE
			;;
		LOG)
			[ -n "$1" ] \
				&& local LOG_TXT="--log-prefix \"$* - \""
			[ -z "$1" ] && local LOG_TXT="--log-prefix $LOG_TEXT"
			echo "\$IPTBL -A \$CHAIN -j LOG $LOG_OPTS $LOG_TXT" \
				>> $OUTFILE 
			;;
		LOG_MSG)
			LOG_TEXT="\"$* - \""
			;;
		LOG_MSG_RESET)
			LOG_TEXT="\"$DEF_LOG_TEXT - \""
			;;
		*) 
			echo 1>&2
			echo "  $PROGNAME: Error: $KEY $TYPE $DIRN $ARGS" 1>&2
			echo "             Type $TYPE not supported." 1>&2
	 		echo "             Valid types are ${VALID_TYPES}." 1>&2
			echo 1>&2
			RETCODE=1
			continue
			;;
		esac

	done

	return $RETCODE
}


# Create a forwarding rule function
ipv4_compile_chain () {
	ARG_ERROR="fN ipv4_compile_chain: $*"
	local F	BASE_CHAIN=""
	local RETCODE=0
	local INPUT_CHAIN=0
	local NO_LACE=0
	local SLAVE_CHAIN=""
	local CHAIN_PRIORITY="65"
	local USAGE="  $PROGNAME fN usage: ipv4_compile_chain [-i] [-n] [-b base-chain] [-p priority] [-s slave-chain] <chain-name> <default-target> <from-net> <from-if> [<to-net> <to-if>]"

	local OPTIND=1
	while getopts inb:p:s: F; do
		case $F in 
		b) 	BASE_CHAIN="$OPTARG";;
		i)	INPUT_CHAIN=1;;
		n)	NO_LACE=1;;
		p)	CHAIN_PRIORITY="$OPTARG";;
		s) 	SLAVE_CHAIN="$SLAVE_CHAIN $OPTARG";;
		\?) 	echo 1>&2
			echo "$USAGE" 1>&2
			echo "      ARGS: $*" 1>&2
			echo 1>&2
			return 1
			;;
		esac
	done
	shift $(($OPTIND - 1))
	
	if [ \( $NO_LACE -gt 0 -a $# -ne 2 \) -o \
		\( $NO_LACE -eq 0 -a $INPUT_CHAIN -eq 0 -a $# -ne 6 \) -o \
		 \( $NO_LACE -eq 0 -a $INPUT_CHAIN -gt 0 -a $# -ne 4 \) ]
	then
                echo 1>&2
                echo "$USAGE" 1>&2
		echo "      Wrong number of arguments." 1>&2
		echo "      ARGS: $*" 1>&2
                echo 1>&2
		return 1
	fi
	# OK we are sure have the right number of args, lets syntax check
	# as much as possible before bombing out
	local CHAIN="$1"
	local DEF_TARGET="$2"
	local LEFT_NET="$3"
	local LEFT_IF="$4"
	local RIGHT_NET="$5"
	local RIGHT_IF="$6"

	! local LEFT_IFIN=`if_expand -i $LEFT_IF` && RETCODE=1
	! local LEFT_IFOUT=`if_expand -o $LEFT_IF` && RETCODE=1
	LEFT_IF=`echo $LEFT_IF | sed -e 's/^\(.*\)[=].*$/\1/'`
	! local RIGHT_IFIN=`if_expand -i $RIGHT_IF` && RETCODE=1
	! local RIGHT_IFOUT=`if_expand -o $RIGHT_IF` && RETCODE=1
	RIGHT_IF=`echo $RIGHT_IF | sed -e 's/^\(.*\)[=].*$/\1/'`

	! arg_check chain "$BASE_CHAIN" && RETCODE=1
	local C
	for C in $SLAVE_CHAIN; do 
		! arg_check chain "$C" && RETCODE=1
	done
	unset C
	! arg_check chain "$CHAIN" && RETCODE=1
	! arg_check ipv4_net "$LEFT_NET" && RETCODE=1
	! arg_check interface "$LEFT_IF" && RETCODE=1
	! arg_check ipv4_net "$RIGHT_NET" && RETCODE=1
	! arg_check interface "$RIGHT_IF" && RETCODE=1

	[ -z "$BASE_CHAIN" ] && local BASE_CHAIN="$CHAIN"

	case $DEF_TARGET in
	RETURN|DROP|droplog|log)
		;;
	*)
		echo "$PROGNAME fN ipv4_compile_forward: default targets are RETURN,DROP,droplog,log" 1>&2
		echo "      ARGS: $*" 1>&2
		RETCODE=1
	esac

	case $CHAIN_PRIORITY in
	[0-9][0-9]);;
	*)
	        echo "$PROGNAME fN ipv4_compile_forward: priority is from 00-99" 1>&2
                echo "      ARGS: $*" 1>&2
		RETCODE=1
		;;
	esac

	# Time to bomb
	if [ $RETCODE -gt 0 ]; then
		return 1
	fi

	# Work out lacing blocks depeing on INPUT stuff etc...
	local IN_IF 
	local LOAD_CHAIN="IPV4_LOAD_FWD_CHAINS"
	local SRC_NET LACEUP UNLACE
	
	[ $NO_LACE -eq 0 ] && case $# in
	4)	
		LOAD_CHAIN="IPV4_LOAD_LCL_CHAINS"
		LACEUP="
	local INDEX=\`ipdefs_getIndex \$CHAIN $LOCALCHAIN \"\$${LOAD_CHAIN}\"\`
	if [ \"\$INDEX\" != \"-A\" ]; then
		\$IPTBL -I $LOCALCHAIN \$INDEX -j \$CHAIN -s $LEFT_NET -i $LEFT_IF $LEFT_IFIN
	else
		\$IPTBL -A $LOCALCHAIN -j \$CHAIN -s $LEFT_NET -i $LEFT_IF $LEFT_IFIN 
	fi
		
"
		UNLACE="
	qt \$IPTBL -D $LOCALCHAIN -j \$CHAIN -s $LEFT_NET -i $LEFT_IF $LEFT_IFIN
	qt \$IPTBL -D $LOCALCHAIN -j \$CHAIN -s $LEFT_NET -i $LEFT_IF -d 0/0 $LEFT_IFIN
"
		;;
	6)
		LOAD_CHAIN="IPV4_LOAD_FWD_CHAINS"
		LACEUP="
	local INDEX=\`ipdefs_getIndex \$CHAIN $FWDCHAIN \"\$${LOAD_CHAIN}\"\`
	if [ \"\$INDEX\" != \"-A\" ]; then
		\$IPTBL -I $FWDCHAIN \$INDEX -j \$CHAIN \\
			-s $LEFT_NET -i $LEFT_IF -d $RIGHT_NET -o $RIGHT_IF $LEFT_IFIN $RIGHT_IFOUT
		\$IPTBL -I $FWDCHAIN \$INDEX -j \$CHAIN \\
			-s $RIGHT_NET -i $RIGHT_IF -d $LEFT_NET -o $LEFT_IF $RIGHT_IFIN $LEFT_IFOUT
	else
		\$IPTBL -A $FWDCHAIN -j \$CHAIN \\
			-s $RIGHT_NET -i $RIGHT_IF -d $LEFT_NET -o $LEFT_IF $RIGHT_IFIN $LEFT_IFOUT
		\$IPTBL -A $FWDCHAIN -j \$CHAIN \\
			-s $LEFT_NET -i $LEFT_IF -d $RIGHT_NET -o $RIGHT_IF $LEFT_IFIN $RIGHT_IFOUT
	fi
"
		UNLACE="
	qt \$IPTBL -D $FWDCHAIN -j \$CHAIN \\
		-s $LEFT_NET -i $LEFT_IF -d $RIGHT_NET -o $RIGHT_IF $LEFT_IFIN $RIGHT_IFOUT
	qt \$IPTBL -D $FWDCHAIN -j \$CHAIN \\
		-s $RIGHT_NET -i $RIGHT_IF -d $LEFT_NET -o $LEFT_IF $RIGHT_IFIN $LEFT_IFOUT
"
		;;
	*)
		# ErroR!
		echo "$PROGNAME fN ipv4_compile_forward: wrong number of args fed to fN." 1>&2
		echo "      ARGS: $*" 1>&2
		return 1
		;;
	esac	

	# Head of function
	cat - >> $OUTFILE <<EOP1

ipf4_${CHAIN} () {
	local CHAIN="${CHAIN}"
	local SLAVE

	if [ "\$1" = "-h" ]; then
		echo 1>&2
		echo "  Usage $CHAIN: [-h] [-r] [remove]" 1>&2
		echo 1>&2
		exit 1
	fi

	# Unlace the chain appropriately if needed
	$UNLACE

	# Clean out any existing chain
	qt \$IPTBL -F \$CHAIN
EOP1
	
	if [ -n "$SLAVE_CHAIN" ]; then
		cat - >> $OUTFILE <<EOP6
	
	case \$1 in
	remove|-r)
		qt \$IPTBL -X \$CHAIN
		for SLAVE in $SLAVE_CHAIN; do
			ipf4_\${SLAVE} remove
		done 
		return 0
		;;
	*|"")
		for SLAVE in $SLAVE_CHAIN; do
			ipf4_\${SLAVE}
		done 
	esac

	\$IPTBL -N \$CHAIN > /dev/null 2>&1

EOP6
	else
		cat - >> $OUTFILE <<EOP5
	case \$1 in
	remove|-r)
		qt \$IPTBL -X \$CHAIN
		return 0
		;;
	*|"")
		;;
	esac

	\$IPTBL -N \$CHAIN > /dev/null 2>&1

EOP5
	fi
	
	# Make sure that the log chain exists
	case $DEF_TARGET in
	log)
		echo "	# Make sure that the log chain exists"  \
				>> $OUTFILE
		echo "	ipv4_createChain log log log REJECT" \
				>> $OUTFILE
		;;
	droplog)
		echo "	# Make sure that the log chain exists"  \
				>> $OUTFILE
		echo "	ipv4_createChain droplog log droplog DROP" \
				>> $OUTFILE
		;;
	esac
	
	cat - >> $OUTFILE <<EOP4
		
	# START DEFINED RULES HERE

EOP4
	
	# Add rules from configuration file, or from template function
	if qt type tmplfn_${BASE_CHAIN}; then
		if ! tmplfn_${BASE_CHAIN} $CHAIN $LEFT_IF $RIGHT_IF
		then
			RETCODE=1
		fi
	else
		if ! grep -h "^[   ]*${BASE_CHAIN}" $PROTODEFS $INDEFS \
				| ipv4_Compile_Read_Line \
					"$BASE_CHAIN" \
					$LEFT_IF $RIGHT_IF
		then
			RETCODE=1
		fi
	fi

	# Add tail of function	
	cat - >> $OUTFILE <<EOP2
	
	# FINISH DEFINED RULES

	# Set up default target
	\$IPTBL -A \$CHAIN -j $DEF_TARGET

	# Lace chain to forwarding chain if needed
	$LACEUP
}
EOP2

	# Add chain to list of chains to create
	echo -e "${LOAD_CHAIN}=\"$CHAIN_PRIORITY-$CHAIN \$$LOAD_CHAIN\""

	return $RETCODE

}

ipv4_Compile_Masq () {
        local TARGET SRCNET SRCIF TOIP DSTNET DSTIF PROTO SPORT DPORT
	local RETCODE=0
	ARG_ERROR="fN ipv4_Compile_Masq: $*"

        local CHAIN="POSTROUTING"

	cat - >> $OUTFILE <<EOP7

ipf4_POSTROUTING () {
	local CHAIN="POSTROUTING"

	if [ "\$1" = "-h" ]; then
		echo 1>&2
		echo "  Usage $CHAIN: [-h] [-r] [remove]" 1>&2
		echo 1>&2
		exit 1
	fi

	# Clear out Chain
	\$IPTBL -t nat -F \$CHAIN
	\$IPTBL -t nat -P \$CHAIN ACCEPT

	case \$1 in
	remove|-r)
		return 1
		;;
	esac

	# NAT rules start here...
EOP7

        while read ARGS; do

                # Handle comments and empty lines
                [ -z "$ARGS" ] && continue
                echo $ARGS | grep -q '^#' && continue
                ARGS=`echo "$ARGS" | sed -e 's/\(.*\)#.*/\1/'`

                # Put $ARGS into positional arguments so we can shift etc...
                # Evaled so that $ environment variables work in .def files...
                eval set -- "$ARGS"
		
		TARGET="$1"
		case $TARGET in
		SNAT|MASQUERADE)
			;;
		*)
			echo "$PROGNAME: Error: $ARGS" 1>&2
			echo "           Target $TARGET not supported." 1>&2
	 		echo "           Valid Targets are SNAT,MASQUERADE" 1>&2
			RETCODE=1
			continue
			;;
		esac

		SRCNET="$2"
		case $SRCNET in
		'-')
			SRCNET="0/0"
			;;
		esac
		! arg_check ipv4_net "$SRCNET" && RETCODE=1 && continue		

		SRCIF="$3"
		SRCIF=`echo $SRCIF | sed -e 's/^\(.*\)[=].*$/\1/'`
		! arg_check -d interface "$SRCIF" && RETCODE=1 && continue
		case $SRCIF in
		'-')
			unset SRCIF
			;;
		*)
			SRCIF="-i $SRCIF"
			;;
		esac

		TOIP="$4"
		! arg_check -d ipv4_addr "$TOIP" && RETCODE=1 && continue
		case $TOIP in
		'-')
			unset TOIP
			;;
		*)	
			TOIP="--to $TOIP"
			;;
		esac

		DSTNET="$5"
		case $DSTNET in
		'-'|'')
			DSTNET="0/0"
			;;
		esac
		! arg_check ipv4_net "$DSTNET" && RETCODE=1 && continue

		DSTIF="$6"
		DSTIF=`echo $DSTIF | sed -e 's/^\(.*\)[=].*$/\1/'`
		! arg_check -d interface "$DSTIF" && RETCODE=1 \
			&& continue
		case $DSTIF in
		'-'|'')
			unset DSTIF
			;;
		*)	
			DSTIF="-o $DSTIF"
			;;
		esac

		PROTO="$7"
		! arg_check -d  protocol "$PROTO" && RETCODE=1 \
			&& continue
		case $PROTO in
		'-'|'')
			unset PROTO
			;;
		*)
			PROTO="-p $PROTO"
			;;
		esac

		case $7 in
		6|tcp|17|udp|icmp|1|''|'-')
			;;
		*)
			if [ -n "$8" -o -n "$9" ]; then
				echo "$PROGNAME: Error: $ARGS" 1>&2
				echo "           Ports and icmp types can only be used with udp and tcp." 1>&2
				RETCODE=1
				continue
			fi
			;;
		esac

		SPORT="$8"
		! arg_check -d  port "$SPORT" && RETCODE=1 \
			&& continue
		case $SPORT in
		'-'|'')
			unset SPORT
			;;
		*)
			case $7 in
			udp|17|tcp|6)
				SPORT="--sport $SPORT"
				;;
			icmp|1)
				SPORT="--icmp-type $SPORT"
				;;
			esac
			;;
		esac
		
		DPORT="$9"
		! arg_check -d  port "$DPORT" && RETCODE=1 \
			&& continue
		case $DPORT in
		'-'|'')
			unset DPORT
			;;
		*)	
			DPORT="--dport $DPORT"
			;;
		esac

		case $TARGET in
		MASQUERADE)
			echo "	\$IPTBL -t nat -A \$CHAIN -j $TARGET -s $SRCNET -d $DSTNET $DSTIF $PROTO $DPORT $SPORT" \
				>> $OUTFILE
			;;
		SNAT)
			echo "	\$IPTBL -t nat -A \$CHAIN -j $TARGET $TOIP -s $SRCNET -d $DSTNET $DSTIF $PROTO $DPORT $SPORT" \
				>> $OUTFILE
			;;
		esac
	done

	cat - >> $OUTFILE <<EOP8

	return 0
}
EOP8
	
	return $RETCODE
}


if_expand () {
	local USAGE="  $prog_name: fn if_expand {-i|-o} if-name"
	local OPTIND=1
	while getopts io F; do
		case $F in 
		i)	DIRN='in'
			;;
		o)	DIRN='out' 
			;; 
		\?) 	echo 1>&2
			echo "$USAGE" 1>&2
			echo "      ARGS: $*" 1>&2
			echo 1>&2
			return 1
			;;
		esac
	done
	shift $(($OPTIND - 1))

	[ -z "$1" ] && return 0

	local PROTO="--proto esp"

	case $1 in 
	*=ipsec)
		echo "-m policy --dir $DIRN --pol ipsec $PROTO"
		return 0
		;;
	*=clear)
		echo "-m policy --dir $DIRN --pol none"
		return 0
		;;
	*=*)
		echo 1>&2
	        echo "  $PROGNAME: Argument syntax error" 1>&2
		echo "             $ARG_ERROR" 1>&2
		echo "             Argument type: interface, Value: $1" 1>&2
		echo "             Interface crypto types are '=clear' or '=ipsec'" 1>&2
		echo 1>&2
		return 1
		;;
	esac

	return 1
}

ipv4_Compile_Dnat () {
        local SRCNET SRCIF  PROTO DSTIP DPORT TOIP TOPORT
	local RETCODE=0
	ARG_ERROR="fN ipv4_Compile_Dnat: $*"

        local CHAIN="POSTROUTING"

	cat - >> $OUTFILE <<EOP9

ipf4_PREROUTING () {
	local CHAIN="PREROUTING"

	if [ "\$1" = "-h" ]; then
		echo 1>&2
		echo "  Usage $CHAIN: [-h] [-r] [remove]" 1>&2
		echo 1>&2
		exit 1
	fi



	# Clear out Chain
	\$IPTBL -t nat -F \$CHAIN
	\$IPTBL -t nat -P \$CHAIN ACCEPT

	case \$1 in
	remove|-r)
		return 1
		;;
	esac

	# NAT rules start here...
EOP9

        while read ARGS; do

                # Handle comments and empty lines
                [ -z "$ARGS" ] && continue
                echo $ARGS | grep -q '^#' && continue
                ARGS=`echo "$ARGS" | sed -e 's/\(.*\)#.*/\1/'`

                # Put $ARGS into positional arguments so we can shift etc...
                # Evaled so that $ environment variables work in .def files...
                eval set -- "$ARGS"
		
		SRCNET="$1"
		case $SRCNET in
		'-')
			SRCNET="0/0"
			;;
		esac
		! arg_check ipv4_net "$SRCNET" && RETCODE=1 && continue

		SRCIF="$2"
		SRCIF=`echo $SRCIF | sed -e 's/^\(.*\)[=].*$/\1/'`
		! arg_check -d interface "$SRCIF" && RETCODE=1 && continue
		case $SRCIF in
		'-')
			unset SRCIF
			;;
		*)
			SRCIF="-i $SRCIF"
			;;
		esac

		PROTO="$3"
		! arg_check -d protocol "$PROTO" && RETCODE=1 && continue
		case $PROTO in
		'-')
			unset PROTO
			;;
		*)
			PROTO="-p $PROTO"
			;;
		esac

		DSTIP="$4"
		case $DSTIP in
		'-'|'')
			DSTIP='0/0'
			;;
		esac
		! arg_check ipv4_net "$DSTIP" && RETCODE=1 && continue

		DPORT="$5"
		! arg_check -d port "$DPORT" && RETCODE=1 && continue
		case $DPORT in
		'-')
			unset DPORT
			;;
		*)	
			DPORT="--dport $DPORT"
			;;
		esac


		case $3 in
		6|tcp|17|udp|icmp|1|'-')
			;;
		*)
			if [ -n "$DPORT"  ]; then
				echo "$PROGNAME: Error: $ARGS" 1>&2
				echo "           Ports and icmp types can only be used with udp and tcp." 1>&2
				RETCODE=1
				continue
			fi
			;;
		esac

		TOIP="$6"
		case $TOIP in
		'-'|'')
			echo "$PROGNAME: Error: $ARGS" 1>&2
			echo "           DNAT target IP must be given." 1>&2
			RETCODE=1
			continue
			;;
		esac
		! arg_check ipv4_addr "$TOIP" && RETCODE=1 && continue

		case $3 in 
		udp|17|tcp|6)
			TOPORT="$7"
			! arg_check -d portmasq "$TOPORT" && RETCODE=1 \
				&& continue
			case $TOPORT in
			'-'|'')
				echo "$PROGNAME: Error: $ARGS" 1>&2
				echo "           DNAT target port must be given for tcp and udp." 1>&2
				RETCODE=1
				continue
				;;
			*)
				TOIP="${TOIP}:${TOPORT}"
			esac
			;;
		esac

		echo "	\$IPTBL -t nat -A \$CHAIN -j DNAT $PROTO -s $SRCNET $SRCIF -d $DSTIP $DPORT --to-destination $TOIP" \
				>> $OUTFILE
	done

	cat - >> $OUTFILE <<EOP10

	return 0
}
EOP10
	
	return $RETCODE
}

#
# Argument format checking code 
# 
# - this may make the compile 3-4 times slower, but it will catch must 
#   of the typos
#
arg_error () {
	echo 1>&2
	echo "  $PROGNAME: Argument syntax error" 1>&2
	echo "             $ARG_ERROR" 1>&2
	echo "             Argument type: ${1}, Value: $2" 1>&2
	echo 1>&2	

} 

arg_check () {
	local ACCEPT_MINUS=0

	local OPTIND=1
	while getopts d F; do
		case $F in
		d)
			ACCEPT_MINUS=1
			;;
		\?)     
			echo 1>&2
			echo "$PROGNAME - fN arg_check: fatal error - please give correct arguments" 1>&2
			echo 1>&2
			exit 1
			;;
		esac
        done
	shift $(( $OPTIND - 1 ))

	local STR="$2"

	[ $ACCEPT_MINUS -gt 0 -a "$STR" = "-" ] && return 0
	[ -z "$STR" ] && return 0

	case $1 in
	ipv4_net)
		 echo "$STR" | grep -q '^0\/0$\|^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$\|^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\/[0-9]\{1,2\}$\|^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$' \
			&& return 0
		;;
	ipv4_addr)
		 echo "$STR" | grep -q '^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$' \
			&& return 0
		;;
	ipv6_net)
		echo "$STR" | grep -q '^\:\:/0$\|^[0-9a-f]\{1,4\}:[0-9a-f:]*:[0-9a-f]\{0,4\}$\|^[0-9a-f]\{1,4\}:[0-9a-f:]*:[0-9a-f]\{0,4\}\/[0-9]\{1,3\}$' \
			&& return 0
		;;
	ipv6_addr)
		echo "$STR" | grep -q '^\:\:/0$\|^[0-9a-f]\{1,4\}:[0-9a-f:]*:[0-9a-f]\{0,4\}$\|^[0-9a-f]\{1,4\}:[0-9a-f:]*:[0-9a-f]\{0,4\}$' \
			&& return 0
		;;
	chain)
		echo "$STR" | grep -q '^[_a-zA-Z0-9]\+$' && return 0
		;;
	interface)
		echo "$STR" | grep -q '^[-_+a-zA-Z0-9]\+$' && return 0
		;;
	protocol)
		cat /etc/protocols | sed -e 's/^\(.*\)#.*$/\1/' \
			| grep -q "\<${STR}\>" && return 0
		if echo "$STR" | grep -q '^[0-9]\{1,3\}$' \
			&& [ $STR -ge 0 -a $STR -le 255 ];
		then
			return 0
		fi
		;;
	port)
		local S 
		RET=0
		STR=`echo "$STR" | sed -e 's/:/ /'`
		for S in $STR; do
			if cat /etc/services | sed -e 's/^\(.*\)#.*$/\1/' \
				| grep -q "\<${S}\>"; then
				continue
			fi
			if echo "$S" | grep -q '^[0-9]\{1,5\}$' \
				&& [ $S -ge 0 -a $S -le 65535 ]; then 
				continue
			fi 
			RET=1
		done
		[ $RET -eq 0 ] && return 0
		;;
	portmasq)
		local S
		RET=0
		STR=`echo "$STR" | sed -e 's/-/ /'`
		for S in $STR; do
			if echo "$S" | grep -q '^[0-9]\{1,5\}$' \
				&& [ $S -ge 1 -a $S -le 65535 ]; then 
				continue
			fi
			RET=1
		done
		[ $RET -eq 0 ] && return 0
		;;
	*)
		echo 1>&2
		echo "$PROGNAME - fN arg_check: fatal error - please give correct arguments" 1>&2
		echo 1>&2
		exit 1
		;;
	esac

	# If we get here, its an ERROR!!!
	arg_error $1 $2
	return 1
}


