#! /bin/bash

# This is a wrapper script to simulate ipfwadm 2.3a.  It ain't pretty,
# but it should work (for valid commands).  `-V' is translated to `-W'
# or ignored if a `-W' option is already there, but always warned
# about.

# Paul.Russell@rustcorp.com.au, Nov-1997.

# 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; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Version: 1.0.1: Fixed -t (no longer de-hexizes).
#
# Version: 1.0.2: Fixed undocumented `-a masq'.
#                 Should be OK now with bash v1.
#                 If we can't find ip_fwnames, call /sbin/ipfwadm.real
#
if [ -n "$DEBUG_IPFWADM" ]; then IPCHAINS=print_ipchains;
else IPCHAINS=/sbin/ipchains;
fi
PROC_FIREWALL_NAMES="/proc/net/ip_fwnames"
SPECIAL_CHAIN="IpFwAdM!"
START_MARK=10000

barf()
{
    echo "$@"
    echo
    echo If this command worked with the original ipfwadm 2.3, please
    echo submit a bug report to \`ipchains@wantree.com.au\'.
    echo
    echo The best way to do this is to submit the output of \`$0 --version\',
    echo the command used to obtain this error, any previous ipfwadm
    echo commands, and the output of \`ipchains-save\'.
    echo
    echo Then try flushing all the rules \`ipchains -F; ipchains -X\',
    echo setting the DEBUG_IPFWADM variable \`export DEBUG_IPFWADM=1\' or
    echo \`setenv DEBUG_IPFWADM 1\' and rerunning the command\(s\) which
    echo caused this error.
    exit 1
}

print_ipchains()
{
    echo ipchains "$@" 1>&2
    /sbin/ipchains "$@"
}

setup_chains()
{
    if [ `wc -l < $PROC_FIREWALL_NAMES` != 3 -a -z "$DEBUG_IPFWADM" ]
    then
	echo You cannot mix the \`ipfwadm\' wrapper with ipchains. 1>&2
	echo You must delete all user chains and flush all built-in chains 1>&2
	echo if you want to use the \`ipfwadm\' wrapper. 1>&2
	exit 1
    fi
    $IPCHAINS -N acctin
    $IPCHAINS -N acctout
    $IPCHAINS -N acctboth
    $IPCHAINS -N inp
    $IPCHAINS -N out
    $IPCHAINS -N fwd

    # Let all fragments through like the old code used to.
    $IPCHAINS -A input -f -j ACCEPT
    $IPCHAINS -A output -f -j ACCEPT
    $IPCHAINS -A forward -f -j ACCEPT

    # Jump to accounting rules.  Order of traversal of acct rules
    # doesn't matter.
    $IPCHAINS -A input -j acctin
    $IPCHAINS -A input -j acctboth
    $IPCHAINS -A output -j acctout
    $IPCHAINS -A output -j acctboth

    # Now go to `real' chains.
    $IPCHAINS -A input -j inp
    $IPCHAINS -A output -j out
    $IPCHAINS -A forward -j fwd

    # Create dummy chains to mark this as an ipfwadm-emulation firewall.
    $IPCHAINS -N $SPECIAL_CHAIN
    # Insert min and max mark values.
    $IPCHAINS -A $SPECIAL_CHAIN -m $START_MARK
    $IPCHAINS -A $SPECIAL_CHAIN -m $(($START_MARK + 1))
}

# SIGH.  We use identical marks to indicate which rules are actually
# the same rule (to simulate multiple ports, and -y without -P tcp).

# We start the marks at 1,000,000, so we can insert before them or append
# after them.

# In the accounting chain, marks are unique between the three acct* chains,
# so we can tell ordering.

print_count()
{
    count=$(($1))
    if let $(($count > 99999))
    then
	cntkb=$((($count + 500) / 1000))
	if let $((cntkb > 9999))
	then
	    cntmb=$((($count + 500000) / 1000000))
	    printf "%4sM " $cntmb
	else
	    printf "%4sK " $cntkb
	fi
    else
	printf "%5s " $count
    fi
}

dump_rule()
{
# ARGS: $LIST_VERBOSE $EXPAND_NUMBERS $BIDIR $SYN_NO_PROTO $SRCPORTS $DSTPTS
# $PCNT $BCNT $TARG $PROTO $FLAGS $TOSA $TOSX $IFNM $NUM $SRCIP $DSTIP $REDIR

# The ipfwadm code looks like: (* = -e only)
# *    *               *    *    *    *    *       
# pcnt bcnt kind proto bkyo TOSA TXOR IFNM IFADD SRC DST SPTs -> DPTs REDIR
    if [ -n "$1" ]
    then
	# Packet and byte counts.
	if [ -n "$2" ]; then printf "%8u " $7; else print_count $7; fi
	if [ -n "$2" ]; then printf "%8u " $8; else print_count $8; fi
    fi

    # Kind
    case "$9" in
	in) printf "%-3s " "$9" ;;
	out) printf "%-3s " "$9" ;;
	i/o) printf "%-3s " "$9" ;;
	*) printf "%-5s " "$9" ;;
    esac

    # Proto
    printf "%-5s" "${10}"

    if [ -n "$1" ]
    then
	# Flags
	if [ "$3" != 0 ]; then printf "b"; else printf "-"; fi
	case "${11}" in
	    *!y*) printf "k-" ;;
	    *y*) printf "-y" ;;
	    *) printf "--" ;;
	esac
	case "${11}" in
	    *l*) printf "l " ;;
	    *) printf "- " ;;
	esac

	# TOS
	printf "${12} ${13} "

	# Interface name
	printf "%-7.16s " "${14}"

	# Interface address
	if [ -n "${15}" ]; then printf "%-15s " 0.0.0.0;
	else printf "%-15s " any;
	fi
    fi

    # Source and dest.
    printf "%-20s " "${16}"
    printf "%-20s" "${17}"

    # Source Ports.
    if [ "${10}" != tcp -a "${10}" != udp -a "${10}" != icmp ]
    then
	echo " n/a"
	return
    fi
    printf " "
    printf "$5" | tr ' ' ','

    if [ "${10}" = icmp ]
    then
	echo
	return
    fi

    # Dest ports.
    if [ "$5" != "n/a" ]
    then
	printf " -> " 
	printf "$6" | tr ' ' ','
    fi

    # redirect ports.
    if [ "$9" = "acc/r" ]
    then
	printf " => %s" "${18}"
    fi
    echo
}

get_policy() # CHAIN
{
    case "`ipchains -L $1 | head -1`" in
    *ACCEPT*)
	echo accept;;
    *MASQ*)
	echo accept/masquerade;;
    *REJECT*)
	echo reject;;
    *DENY*)
	echo deny;;
    *)
	barf "Unknown policy for \`$1' - `ipchains -L $1 2>&1`"
    esac
}

list_chain() # $CHAIN $LIST_VERBOSE $NUMERIC $EXPAND_NUMBERS
{
# if (!(format & FMT_NOCOUNTS)) {
# 	if (format & FMT_KILOMEGA) {
# 		fprintf(fp, FMT("%5s ","%s "), "pkts");
# 		fprintf(fp, FMT("%5s ","%s "), "bytes");
# 	} else {
# 		fprintf(fp, FMT("%8s ","%s "), "pkts");
# 		fprintf(fp, FMT("%10s ","%s "), "bytes");
# 	}
# }
    case "$1" in
    acct*) ;;
    inp) printf "IP firewall input rules, default policy: " 
	get_policy input
	;;
    out) printf "IP firewall output rules, default policy: " 
	get_policy output
	;;
    fwd) printf "IP firewall forward rules, default policy: " 
	get_policy forward
	;;
    *) barf "Unknown chain for list_chain - \`$1'"
	;;
    esac

    if [ -n "$2" ]
    then
	if [ -z "$4" ]
	then
	    printf "%5s " pkts
	    printf "%5s " bytes
	else
	    printf "%8s " pkts
	    printf "%10s " bytes
	fi
    fi

# if (!(format & FMT_NOKIND)) {
# 	if (chain == CHN_ACCT)
# 		fprintf(fp, FMT("%-3s ","%s "), "dir");
# 	else
# 		fprintf(fp, FMT("%-5s ","%s "), "type");
# }
    case "$1" in
    acct*) printf "%-3s " dir ;;
    *) printf "%-5s " type ;;
    esac

# fputs("prot ", fp);
    printf "prot "

# if (format & FMT_OPTIONS)
# 	fputs("opt  ", fp);
# if (format & FMT_TOS)
# 	fputs("tosa tosx ", fp);
# if (format & FMT_VIA) {
# 	fprintf(fp, FMT("%-7s ","(%s "), "ifname");
# 	fprintf(fp, FMT("%-15s ","%s) "), "ifaddress");
# }
    if [ -n "$2" ]
    then
	printf "opt  tosa tosx %-7s %-15s " ifname ifaddress
    fi

# fprintf(fp, FMT("%-20s ","%s "), "source");
# fprintf(fp, FMT("%-20s ","%s "), "destination");
# fputs("ports\n", fp);
# }
    printf "%-20s %-20s ports" source destination
    echo

    case "$1" in
	acct*) shift;
		(list_chain_real acctin "$@" "1"
		 list_chain_real acctout "$@" "1" 
		 list_chain_real acctboth "$@" "1") | sort -n | cut -c11-;;
	*) list_chain_real "$@" ;;
    esac
}

list_chain_real() # $CHAIN $LIST_VERBOSE $NUMERIC $EXPAND_NUMBERS $PREPEND_MARK
{
    CHAIN="$1"
    LIST_VERBOSE="$2"
    NUMERIC="$3"
    EXPAND_NUMBERS="$4"
    PREPEND_MARK="$5"

    # The ipfwadm code looks like: (* = -e only)
    # *    *               *    *    *    *    *       
    # pcnt bcnt kind proto bkyo TOSA TXOR IFNM IFADD SRC DST SPTs -> DPTs REDIR
    #
    # The ipchains code looks like: (* = -v only)
    # *    *               *    *    *    *    *       
    # pcnt bcnt targ proto !yfl TOSA TXOR IFNM MARK SRC DST SPTs -> DPTs REDIR
    LAST_MARK=xxx

    BIDIR=0
    SYN_NO_PROTO=0
    SRCPORTS=""
    DSTPORTS=""

    [ -z "$NUMERIC" ] || NUMERIC="-n"
    $IPCHAINS -L $CHAIN -v -x $NUMERIC | tail +3 |
    while true
    do
	if ! read PCNT BCNT TARG PROTO FLAGS TOSA TOSX IFNM MARK SRCIP DSTIP SRCPTS IGN1 DSTPTS REDIR
	then
	    # Dump last rule.
	    if [ "$LAST_MARK" != "xxx" ] 
	    then
		[ -z "$PREPEND_MARK" ] || printf "%-10s " "$LAST_MARK"
		dump_rule "$LIST_VERBOSE" "$EXPAND_NUMBERS" $BIDIR $SYN_NO_PROTO "$SRCPORTS" "$DSTPORTS" $LAST_PCNT $LAST_BCNT $LAST_TARG $LAST_PROTO $LAST_FLAGS $LAST_TOSA $LAST_TOSX "$LAST_IFNM" "$NUMERIC" $LAST_SRCIP $LAST_DSTIP "$LAST_REDIR"
	    fi
	    return
	fi
	[ -z "$DEBUG_IPFWADM" ] || echo RULE is "$PCNT $BCNT $TARG $PROTO $FLAGS $TOSA $TOSX "$IFNM" $MARK $SRCIP $DSTIP $SRCPTS $IGN1 $DSTPTS $REDIR" 1>&2

	if [ "$LAST_MARK" = "$MARK" ]
	then
# Fold rules back together.
	
# We combine for any of the following reasons:
# -k or -y used with no protocol: first rule has proto TCP and 'y'.
# -b used: SRC & DST reversed.
# Multiple ports: all the same but for port.

# Worst cases:
# ipfwadm -I -a accept -b -P tcp -S 0/0 1 4 -D 1/1 5 9
# => pcnt bcnt targ proto !yfl TOSA TXOR IFNM MARK SRC DST SPTs -> DPTs REDIR
#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    0/0 1/1 1       5
#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    1/1 0/0 5       1
#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    0/0 1/1 1       9
#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    1/1 0/0 9       1
#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    0/0 1/1 4       5
#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    1/1 0/0 5       4
#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    0/0 1/1 4       9
#    ?    ?    ?    TCP   ?    ?    ?    ?    ?    1/1 0/0 9       4
#
# ipfwadm -I -a accept -b -y -S 0/0 -D 1/1
# => pcnt bcnt targ proto !yfl TOSA TXOR IFNM MARK SRC DST SPTs -> DPTs REDIR
#    ?    ?    ?    TCP   ?y?? ?    ?    ?    ?    0/0 1/1
#    ?    ?    ?    TCP   ?y?? ?    ?    ?    ?    1/1 0/0
#    ?    ?    ?    ANY   ?-?? ?    ?    ?    ?    0/0 1/1
#    ?    ?    ?    ANY   ?-?? ?    ?    ?    ?    1/1 0/0
# 	    if [ -n "$DEBUG_IPFWADM" ]
# 	    then
#		echo LAST_PROTO = \`"$LAST_PROTO"\'
#		echo PROTO = \`"$PROTO"\'
# 		echo LAST_SRCIP = \`"$LAST_SRCIP"\'
# 		echo DSTIP = \`"$DSTIP"\'
# 		echo LAST_DSTIP = \`"$LAST_DSTIP"\'
# 		echo SRCIP = \`"$SRCIP"\'
# 		echo LAST_SRCPTS = \`"$LAST_SRCPTS"\'
# 		echo DSTPTS = \`"$DSTPTS"\'
# 		echo LAST_DSTPTS = \`"$LAST_DSTPTS"\'
# 		echo SRCPTS = \`"$SRCPTS"\'
# 	    fi
	    if [ "$LAST_PROTO" = \!tcp -a "$PROTO" = tcp ]
	    then
		[ -n "$DEBUG_IPFWADM" ] && echo "Found SYN rule."
		SYN_NO_PROTO=1
		PCNT=$(($LAST_PCNT + $PCNT))
		BCNT=$(($LAST_BCNT + $BCNT))
		PROTO="all"
	    elif [ "$LAST_SRCIP" = "$DSTIP" -a "$LAST_DSTIP" = "$SRCIP" -a "$LAST_SRCPTS" = "$DSTPTS" -a "$LAST_DSTPTS" = "$SRCPTS" ]
	    then
		[ -n "$DEBUG_IPFWADM" ] && echo "Found bidir rule."
		BIDIR=1
		LAST_PCNT=$(($LAST_PCNT + $PCNT))
		LAST_BCNT=$(($LAST_BCNT + $BCNT))
		# Don't transfer this rule to LAST_ vars - effectively ignore.
		continue;
	    else
		[ -n "$DEBUG_IPFWADM" ] && echo "Found port rule."
		# For n source ports and m dest ports, there will be
		# n x m rules.  So, we add to SRCPORTS when we see a new
		# SRCPTS, but only add to DSTPORTS for the first SRCPORTS.
		if [ "$SRCPTS" != "$LAST_SRCPTS" ]
		then
		    SRCPORTS="$SRCPORTS $SRCPTS"
		fi
		if [ "$SRCPORTS" = "$SRCPTS" ]
		then
		    DSTPORTS="$DSTPORTS $DSTPTS"
		fi
		PCNT=$(($LAST_PCNT + $PCNT))
		BCNT=$(($LAST_BCNT + $BCNT))
	    fi
	else
	    # Dump last rule.
	    if [ "$LAST_MARK" != "xxx" ]
	    then
		[ -z "$PREPEND_MARK" ] || printf "%-10s " "$LAST_MARK"
		dump_rule "$LIST_VERBOSE" "$EXPAND_NUMBERS" $BIDIR $SYN_NO_PROTO "$SRCPORTS" "$DSTPORTS" $LAST_PCNT $LAST_BCNT $LAST_TARG $LAST_PROTO $LAST_FLAGS $LAST_TOSA $LAST_TOSX "$LAST_IFNM" "$NUMERIC" $LAST_SRCIP $LAST_DSTIP "$LAST_REDIR"
	    fi

	    BIDIR=0
	    SYN_NO_PROTO=0
	    SRCPORTS="$SRCPTS"
	    DSTPORTS="$DSTPTS"
	fi

	# Save for next iteration, in case mark the same.
	LAST_PCNT=$PCNT
	LAST_BCNT=$BCNT
	LAST_PROTO=$PROTO
	LAST_FLAGS=$FLAGS
	LAST_TOSA=$TOSA
	LAST_TOSX=$TOSX
	LAST_IFNM="$IFNM"
	LAST_MARK=$MARK
	LAST_SRCIP=$SRCIP
	LAST_DSTIP=$DSTIP
	LAST_REDIR="$REDIR"
	LAST_SRCPTS="$SRCPTS"
	LAST_DSTPTS="$DSTPTS"
	case "$CHAIN" in
	acctin) LAST_TARG=in ;;
	acctout) LAST_TARG=out ;;
	acctboth) LAST_TART=i/o ;;
	*)
	    case "$TARG" in
	    REDIRECT) LAST_TARG="acc/r" ;;
	    MASQ) LAST_TARG="acc/m" ;;
	    ACCEPT) LAST_TARG="acc" ;;
	    REJECT) LAST_TARG="rej" ;;
	    DENY) LAST_TARG="deny" ;;
	    *) barf Unknown target \`"$TARG"\'. ;;
	    esac
	    ;;
	esac
    done
}

############################################################################

if [ ! -f $PROC_FIREWALL_NAMES ]
then
    if [ -f /proc/net/ip_input -a -x /sbin/ipfwadm.real ]
    then
	# Old kernel.  Let's play nice.
	/sbin/ipfwadm.real "$@"; exit
    fi
    echo "Generic IP Firewall Chains not in this kernel" 1>&2
    exit 1
fi

while [ $# != 0 ]
do
    case "$1" in
    -A)
	case x"$2" in
	x-*) CHAIN=acctboth ;;
	xboth) CHAIN=acctboth; shift ;;
	xin) CHAIN=acctin; shift ;;
	xout) CHAIN=acctout; shift ;;
	x) CHAIN=acctboth ;;
 	*) barf Unknown option \`"$2"\' ;;
	esac
	;;
    -I)
	CHAIN=inp
	;;
    -O)
	CHAIN=out
	;;
    -F)
	CHAIN=fwd
	;;
    -M)
	MASQ_MODE=1
	;;
    -a)
	COMMAND=-A
	case x"$2" in
	x-*) TARGET="" ;;
	x) TARGET="" ;;
	xr*) TARGET=REJECT; shift ;;
	xd*) TARGET=DENY; shift ;;
	xa*) TARGET=ACCEPT; shift ;;
	xm*) TARGET=ACCEPT; MASQ=1; shift ;;
 	*) barf Unknown policy for append: \`"$2"\' ;;
	esac
	;;
    -i)
	COMMAND="-I "
	case x"$2" in
	x-*) TARGET="" ;;
	x) TARGET="" ;;
	xr*) TARGET=REJECT; shift ;;
	xd*) TARGET=DENY; shift ;;
	xa*) TARGET=ACCEPT; shift ;;
 	*) barf Unknown policy for insert: \`"$2"\' ;;
	esac
	;;
    -d)
	COMMAND=-D
	case x"$2" in
	x-*) TARGET="" ;;
	x) TARGET="" ;;
	xr*) TARGET=REJECT; shift ;;
	xd*) TARGET=DENY; shift ;;
	xa*) TARGET=ACCEPT; shift ;;
 	*) barf Unknown policy for delete: \`"$2"\' ;;
	esac
	;;
    -l)
	LIST=1
	;;
    -z)
	COMMAND=-Z
	;;
    -f)
	COMMAND=-F
	;;
    -p)
	COMMAND=-P
	case "$2" in
	r*) TARGET=REJECT; shift ;;
	d*) TARGET=DENY; shift ;;
	a*) TARGET=ACCEPT; shift ;;
 	*) barf Unknown policy for -p: \`"$2"\' ;;
	esac
	;;

    -s)
	COMMAND=-S
	OPTIONS="$2 $3 $4"
	shift 3
	;;
    -c)
	COMMAND=-C
	;;
    -h)
	print_help
	;;
    -P)
	PROTOCOL="-p $2"
	shift
	;;
    -S)
	SRC_OPTIONS="-s $2"
	shift
	while true
	do
	    case x"$2" in
		x) break ;;
		x-*) break ;;
		x?*) SRC_PORTS="$2 $SRC_PORTS" ;;
	    esac
	    shift
	done
	;;
    -D)
	DST_OPTIONS="-d $2"
	shift
	while true
	do
	    case x"$2" in
		x) break ;;
		x-*) break ;;
		x?*) DST_PORTS="$2 $DST_PORTS" ;;
	    esac
	    shift
	done
	;;
    -V)
	VIA_ADDR="$2"
	shift
	;;
    -W)
	INTERFACE="$2"
	OPTIONS="$OPTIONS -i $2"
	shift
	;;
    -b)
	OPTIONS="$OPTIONS -b"
	;;
    -e)
	LIST_VERBOSE=1
	;;
    -k)
	TCPSYN="! -y"
	;;
    -m)
	MASQ=1
	;;
    -n)
	NUMERIC=1
	;;
    -o)
	OPTIONS="$OPTIONS -l"
	;;
    -r)
	case x"$2" in
	    x-*) REDIR=0 ;;
	    x) REDIR=0 ;;
	    x?*) REDIR="$2"; shift ;;
	esac
	;;
    -t)
	TOSAND=$(($2 | 0x01))
	TOSXOR=$(($3 & 0xFE))
	OPTIONS="$OPTIONS -t "`printf "0x%02x 0x%02x" $TOSAND $TOSXOR`
	shift 2
	;;
    -v)
	OPTIONS="$OPTIONS -v"
	;;
    -x)
	EXPAND_NUMBERS=1;
	;;
    -y)
	TCPSYN="-y"
	;;

    --version)
	echo "ipfwadm wrapper version 1.0.2"
	exit 0
	;;

    *) barf Unexpected argument \`"$1"\'.
	;;
    esac
    shift
done

# Variables to worry about:
#  $CHAIN - actual chain to work on.
# X$MASQ_MODE - set if -M given.
# X$COMMAND - set if this is a simple command conversion.
# X$TARGET - set for COMMAND of -A, -I, -D or -P (but see REDIR and MASQ).
# X$LIST - set if they want a list.
# X$NUMERIC - list with -n.
# X$LIST_VERBOSE - list all info.
# X$EXPAND_NUMBERS - list full numbers.
# X$OPTIONS - miscellaneous easy-to-convert options.
# X$SRC_OPTIONS - set if a source address is specified.
# X$SRC_PORTS - space-separated list of specified source ports/ranges.
# X$DST_OPTIONS - set if a dest address is specified.
# X$DST_PORTS - space-separated list of specified dest ports/ranges.
#  $VIA_ADDR - an interface address if one is specified.
#  $INTERFACE - an interface name if one is specified.
# X$TCPSYN - set if `-k' or `-y' is specified.
# X$MASQ - set if `-m' is specified.
# X$REDIR - set to the port if `-r port' is specified

if [ -n "$MASQ_MODE" ]
then
    if [ -n "$LIST" ]
    then
	$IPCHAINS -M -L $OPTIONS
    else
	$IPCHAINS $COMMAND $OPTIONS
    fi
elif [ -n "$LIST" ]
then
    if ! grep -q IpFwAdM! < $PROC_FIREWALL_NAMES
    then
	echo "Chains are empty. (ie. ipfwadm has not been used on them)." 1>&2
	exit 0
    fi
    # Construct a list.
    if [ x$COMMAND = x-Z ]
    then
	# We have to atomically zero and list a chain.  This is
	# currently impossible, so we:
	# 1) stop all packets on the given chain. 
	# 2) list the values.
	# 3) clear the counters.
	# 4) resume on the given chain.
	case "$CHAIN" in
	    acct*)
		$IPCHAINS -I 1 input -j DENY
		$IPCHAINS -I 1 output -j DENY
		;;
	    inp)
		$IPCHAINS -I 1 input -j DENY
		;;
	    out)
		$IPCHAINS -I 1 output -j DENY
		;;
	    fwd)
		$IPCHAINS -I 1 forward -j DENY
		;;
	    *) barf Unknown chain to stop: \`"$CHAIN"\'.
	esac

	list_chain $CHAIN "$LIST_VERBOSE" "$NUMERIC" "$EXPAND_NUMBERS"
	$IPFWADM -Z $CHAIN
	
	case "$CHAIN" in
	    acct*)
		$IPCHAINS -D 1 input
		$IPCHAINS -D 1 output
		;;
	    inp)
		$IPCHAINS -D 1 input
		;;
	    out)
		$IPCHAINS -D 1 output
		;;
	    fwd)
		$IPCHAINS -D 1 forward
		;;
	    *) barf Unknown chain to restart: \`"$CHAIN"\'.
	esac
    else
	list_chain $CHAIN "$LIST_VERBOSE" "$NUMERIC" "$EXPAND_NUMBERS"
    fi
elif [ x"$COMMAND" = x"-F" -o x"$COMMAND" = x"-Z" -o x"$COMMAND" = x"-C" ]
then
    if ! grep -q IpFwAdM! < $PROC_FIREWALL_NAMES
    then
	echo "Chains are empty. (ie. ipfwadm has not been used on them)." 1>&2
	exit 0
    fi
    $IPCHAINS $COMMAND $CHAIN $OPTIONS
else
    grep -q IpFwAdM! < $PROC_FIREWALL_NAMES || setup_chains

    # Figure out what the target should be.
    if [ -n "$REDIR" ]
    then
	TARGET=REDIRECT
    elif [ -n "$MASQ" ]
    then
	TARGET=MASQ
    fi

    if [ x"$COMMAND" = x"-P" ]
    then
	case "$CHAIN" in
	inp) CHAIN=input ;;
	out) CHAIN=output ;;
	fwd) CHAIN=forward ;;
	*) barf "Illegal chain for -P: \`$CHAIN'" ;;
	esac
	$IPCHAINS $COMMAND $CHAIN $TARGET $OPTIONS
    else
	# If they used -V, and not -W, then try to figure out interface
	# name.  ALWAYS warn about difference.
	if [ -n "$VIA_ADDR" ]
	then
	    if [ -n "$INTERFACE" ]
	    then
		echo Warning: \`-V $VIA_ADDR\' option ignored\; using \`-W $INTERFACE\' only.
	    else
		INTERFACE=`ifconfig | awk -v ADDR=$VIA_ADDR '/^[a-z0-9A-Z]/ { IFNAME=$1 } $0 ~ "^[^A-Za-z0-9:]*inet addr:" ADDR { print IFNAME}'`
		if [ -z "$INTERFACE" ]
		then
		    echo Can\'t handle -V option: can\'t find interface name for the address \`$VIA_ADDR\'. 1>&2
		    echo Please replace the -V with the appropriate -W option. 1>&2
		    exit 1
		fi
		echo Replacing \`-V $VIA_ADDR\' with \`-W $INTERFACE\'.
		OPTIONS="$OPTIONS -i $INTERFACE"
	    fi
	fi

	# Insert, append or delete.
	case $COMMAND in
	    # For Insert, get (and decrement) minimal mark #.
	    -I*) MARK=$(set $($IPCHAINS -L $SPECIAL_CHAIN -v | head -3 | tail -1); echo $9)
		$IPCHAINS -R $SPECIAL_CHAIN 1 -m $(($MARK - 1))
		MARK="-m $MARK"
		;;

	    # For Append, get (and increment) maximum mark #.
	    -A) MARK=$(set $($IPCHAINS -L $SPECIAL_CHAIN -v | head -4 | tail -1); echo $9)
		$IPCHAINS -R $SPECIAL_CHAIN 2 -m $(($MARK + 1))
		MARK="-m $MARK"
		;;
	esac

	# Only care about TCP SYN if -p TCP not specified.
	if [ -n "$TCPSYN" ]
	then
	    case "$PROTOCOL" in
		*[Tt][Cc][Pp]) 
		    OPTIONS="$OPTIONS $PROTOCOL $TCPSYN"
		    TCPSYN=""
		;;
	    esac
	else
	    OPTIONS="$OPTIONS $PROTOCOL"
	fi

	# Mangle source port and dest port args.
	if [ -z "$SRC_PORTS" ]; then SRC_PORTS="X"; fi
	if [ -z "$DST_PORTS" ]; then DST_PORTS="X"; fi

	[ -n "$TARGET" ] && TARGET="-j $TARGET"
	for SRC in $SRC_PORTS
	do
	    if [ $SRC = "X" ]; then SRC=""; fi
	    for DST in $DST_PORTS
	    do
		if [ $DST = "X" ]; then DST=""; fi
		if [ -n "$TCPSYN" ]
		then
		    $IPCHAINS $COMMAND $CHAIN $SRC $DST $OPTIONS -p ! tcp $TARGET $MARK || barf "ipchains failed!"
		    $IPCHAINS $COMMAND $CHAIN $SRC $DST $OPTIONS -p tcp $TCPSYN $TARGET $MARK  || barf "ipchains failed!"
		else
		    $IPCHAINS $COMMAND $CHAIN $SRC_OPTIONS $SRC $DST_OPTIONS $DST $OPTIONS $TARGET $MARK
		fi
	    done
	done
    fi
fi
