#!/bin/sh
#
#     The Shoreline Firewall (Shorewall) Packet Filtering Firewall Compiler - V3.3
#
#     This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
#     (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net)
#
#	Complete documentation is available at http://shorewall.net
#
#	This program is free software; you can redistribute it and/or modify
#	it under the terms of Version 2 of the GNU General Public License
#	as published by the Free Software Foundation.
#
#	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
#
#	If an error occurs while starting or restarting the firewall, the
#	firewall is automatically stopped.
#
#	Commands are:
#
#          compile check                        Verify the configuration files.
#	   compile compile <path name>          Compile into <path name>
#
#	Environmental Variables:
#
#	    EXPORT=Yes                          -e option specified to /sbin/shorewall
#	    SHOREWALL_DIR                       A directory name was passed to /sbin/shorewall
#	    VERBOSE                             Standard Shorewall verbosity control.

#
# Fatal error -- stops the compiler after issuing the error message
#
fatal_error() # $* = Error Message
{   
    echo "   ERROR: $@" >&2
    [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
    [ -n "$OUTPUT" ] && rm -f $OUTPUT
    kill $$
    exit 2
}

#
# We include this for compatibility with the 'firewall' script. It distinguishes between
# Fatal Errors (stop or restore required) and Startup Errors (errors detected before the firewall 
# state has been changed. This allows us to use common parsing routines in both programs.
#
startup_error() 
{
    echo "   ERROR: $@" >&2
    [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
    [ -n "$OUTPUT" ] && rm -f $OUTPUT
    kill $$
    exit 2
}

#
# Write the passed args to the compiler output file.
#
save_command()
{
    [ $# -gt 0 ] && echo "${INDENT}${@}" >&3 || echo >&3
}

save_command_unindented()
{
    echo "${@}" >&3
}

#
# Write a progress_message2 command to the output file.
#
save_progress_message()
{
    echo                                     >&3
    echo "${INDENT}progress_message2 \"$@\"" >&3
    echo                                     >&3
}

save_progress_message_short()
{
    echo "${INDENT}progress_message \"$@\"" >&3
}

progress_message_and_save()
{
    progress_message "$@"
    echo "${INDENT}progress_message \"$@\"" >&3
}

#
# Echo the contents of the passed file indented by $INDENT
#
indent() {
    if [ -n "$INDENT" ]; then
	if [ -n "$HAVEAWK" ]; then
	    eval awk \''BEGIN { indent=1; }; /^[[:space:]]*$/ { print ""; indent=1; next; }; { if (indent == 1) print "'"$INDENT"'" $0; else print; }; { indent=1; }; /\\$/ { indent=0; };'\' $1
	else
	    eval sed \'s\/^/"$INDENT"\/\' $1
	fi
    else
	cat $1
    fi
}

#
# Append a file to the compiler's output with indentation.
#
append_file() # $1 = File Name
{
    local user_exit=$(find_file $1)

    if [ -f $user_exit ]; then
	save_progress_message "Processing $user_exit ..."
	indent $user_exit >&3
    fi
}

#
# Generate a command to run iptables
#
do_iptables() {
    save_command \$IPTABLES $@
}

#
# Generate an IPTABLES command. Include hacks to work around iptables limitations
#
run_iptables() {
    if [ -z "$KLUDGEFREE" ]; then
	#
	# Purge the temporary files that we use to prevent duplicate '-m' specifications
	#
	[ -n "$BRIDGING" ]      && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
	[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
    fi

    save_command "$IPTABLES_COMMAND $@"
}

#
# Version of 'run_iptables' that inserts white space after "!" in the arg list
#
run_iptables2() {
    if [ -z "$KLUDGEFREE" ]; then
	#
	# Purge the temporary files that we use to prevent duplicate '-m' specifications
	#
	[ -n "$BRIDGING" ]      && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
	[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
    fi

    save_command run_iptables $(fix_bang $@)
}

#
# Generate command to quietly run iptables
#
qt_iptables() {
    if [ -z "$KLUDGEFREE" ]; then
	#
	# Purge the temporary files that we use to prevent duplicate '-m' specifications
	#
	[ -n "$BRIDGING" ]      && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
	[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
    fi

    save_command qt \$IPTABLES $@
}

#
# Generate a command to run tc
#
run_tc() {
    save_command run_tc $@
}

#
# Add the implicit ACCEPT rules at the end of a rules file section
#
finish_chain_section() # $1 = canonical chain $2 = state list
{
    local policy policychain

    [ -n "$FASTACCEPT" ] || run_iptables -A $1 -m state --state $2 -j ACCEPT

    if list_search RELATED $(separate_list $2) ; then
	if is_policy_chain $1 ; then
	    if eval test -n \"\$${1}_synparams\" ; then
		if [ $SECTION = DONE ]; then
		    eval policy=\$${1}_policy

		    case $policy in
			ACCEPT|CONTINUE|QUEUE)
			    run_iptables -A $1 -p tcp --syn -j @$1
			    ;;
		    esac
		else
		    run_iptables -A $1 -p tcp --syn -j @$1
		fi
	    fi
	else
	    eval policychain=\$${1}_policychain

	    if eval test -n \"\$${policychain}_synparams\" ; then
		run_iptables -A $1 -p tcp --syn -j @$policychain
	    fi
	fi
    fi
}

finish_section() # $1 = Section(s)
{
    local zone zone1 chain

    for zone in $ZONES $FW; do
	for zone1 in $ZONES $FW; do
	    chain=${zone}2${zone1}
	    if havechain $chain; then
		finish_chain_section $chain $1
	    fi
	done
    done
}

#
# Create a filter chain
#
# If the chain isn't one of the common chains then add a rule to the chain
# allowing packets that are part of an established connection. Create a
# variable exists_${1} and set its value to Yes to indicate that the chain now
# exists.
#
createchain() # $1 = chain name, $2 = If "yes", do section-end processing
{
    local c=$(chain_base $1)

    run_iptables -N $1

    if [ $2 = yes ]; then
	case $SECTION in
	    NEW|DONE)
		finish_chain_section $1 ESTABLISHED,RELATED
		;;
	    RELATED)
		finish_chain_section $1 ESTABLISHED
		;;
	esac
    fi

    eval exists_${c}=Yes
}

createchain2() # $1 = chain name, $2 = If "yes", create default rules
{
    local c=$(chain_base $1)

    ensurechain $1

    if [ $2 = yes ]; then
	case $SECTION in
	    NEW|DONE)
		finish_chain_section $1 ESTABLISHED,RELATED
		;;
	    RELATED)
		finish_chain_section $1 ESTABLISHED
		;;
	esac
    fi

    eval exists_${c}=Yes
}

#
# Determine if a chain exists
#
# When we create a chain "x", we create a variable named exists_x and
# set its value to Yes. This function tests for the "exists_" variable
# corresponding to the passed chain having the value of "Yes".
#
havechain() # $1 = name of chain
{
    local c=$(chain_base $1)

    eval test \"\$exists_${c}\" = Yes
}

#
# Ensure that a chain exists (create it if it doesn't)
#
ensurechain() # $1 = chain name
{
    havechain $1 || createchain $1 yes
}

ensurechain1() # $1 = chain name
{
    havechain $1 || createchain $1 no
}

#
# Add a rule to a chain creating the chain if necessary
#
addrule() # $1 = chain name, remainder of arguments specify the rule
{
    ensurechain $1
    run_iptables -A $@
}

addrule2() # $1 = chain name, remainder of arguments specify the rule
{
    ensurechain $1
    run_iptables2 -A $@
}

#
# Query NetFilter about the existence of a filter chain
#
chain_exists() # $1 = chain name
{
    qt $IPTABLES -L $1 -n
}

#
# Create a mangle chain
#
# Create a variable exists_mangle_${1} and set its value to Yes to indicate that
# the chain now exists.
#
createmanglechain() # $1 = chain name
{
    run_iptables -t mangle -N $1

    eval exists_mangle_${1}=Yes
}

#
# Determine if a mangle chain exists
#
# When we create a chain "chain", we create a variable named exists_nat_chain
# and set its value to Yes. This function tests for the "exists_" variable
# corresponding to the passed chain having the value of "Yes".
#
havemanglechain() # $1 = name of chain
{
    eval test \"\$exists_mangle_${1}\" = Yes
}

#
# Ensure that a mangle chain exists (create it if it doesn't)
#
ensuremanglechain() # $1 = chain name
{
    havemanglechain $1 || createmanglechain $1
}

#
# Create a nat chain
#
# Create a variable exists_nat_${1} and set its value to Yes to indicate that
# the chain now exists.
#
createnatchain() # $1 = chain name
{
    run_iptables -t nat -N $1

    eval exists_nat_${1}=Yes
}

#
# Determine if a nat chain exists
#
# When we create a chain "chain", we create a variable named exists_nat_chain
# and set its value to Yes. This function tests for the "exists_" variable
# corresponding to the passed chain having the value of "Yes".
#
havenatchain() # $1 = name of chain
{
    eval test \"\$exists_nat_${1}\" = Yes
}

#
# Ensure that a nat chain exists (create it if it doesn't)
#
ensurenatchain() # $1 = chain name
{
    havenatchain $1 || createnatchain $1
}

#
# Add a rule to a nat chain creating the chain if necessary

#
addnatrule() # $1 = chain name, remainder of arguments specify the rule
{
    ensurenatchain $1
    run_iptables2 -t nat -A $@
}

#
# Create a rule to delete a chain if it exists
#
deletechain() # $1 = name of chain
{
    save_command "qt \$IPTABLES -L $1 -n && qt \$IPTABLES -F $1 && qt \$IPTABLES -X $1"
}

#
# validate the policy file
#
validate_policy()
{
    local clientwild
    local serverwild
    local zone
    local zone1
    local pc
    local chain
    local policy
    local loglevel
    local synparams
    local parents
    local default
    local var

    print_policy() # $1 = source zone, $2 = destination zone
    {
	[ $1 = $2 ]  || \
	[ $1 = all ] || \
	[ $2 = all ] || \
	progress_message "   Policy for $1 to $2 is $policy using chain $chain"
    }

    for var in DROP_DEFAULT REJECT_DEFAULT ACCEPT_DEFAULT QUEUE_DEFAULT; do
	eval default=\$$var

	case $default in
	    none)
		;;
	    *)
		if ! list_search $default $USEDACTIONS; then
		    if ! list_search $default $DEFAULT_MACROS; then
			if [ ! -f $(find_file macro.$default) ]; then
			    fatal_error "Default Action/Macro $var=$default not found"
			fi
			DEFAULT_MACROS="$DEFAULT_MACROS $default"
		    fi
		fi
	esac
    done

    ALL_POLICY_CHAINS=

    for zone in $ZONES $FW; do
	chain=${zone}2${zone}
	eval ${chain}_is_policy=Yes
	eval ${chain}_is_optional=Yes
	eval ${chain}_policy=ACCEPT
	eval ${chain}_policychain=$chain
	ALL_POLICY_CHAINS="$ALL_POLICY_CHAINS $chain"

	if [ -n "$IMPLICIT_CONTINUE" ]; then
	    eval parents=\$${zone}_parents
	    if [ -n "$parents" ]; then
		for zone1 in $ZONES $FW; do
		    chain=${zone}2${zone1}
		    eval ${chain}_is_policy=Yes
		    eval ${chain}_is_optional=Yes
		    eval ${chain}_policy=CONTINUE
		    eval ${chain}_policychain=$chain
		    ALL_POLICY_CHAINS="$ALL_POLICY_CHAINS $chain"
		    chain=${zone1}2${zone}
		    eval ${chain}_is_policy=Yes
		    eval ${chain}_is_optional=Yes
		    eval ${chain}_policy=CONTINUE
		    eval ${chain}_policychain=$chain
		    ALL_POLICY_CHAINS="$ALL_POLICY_CHAINS $chain"
		done
	    fi
	fi
    done

    strip_file policy

    while read client server policy loglevel synparams; do
	expandv client server policy loglevel synparams

	clientwild=
	serverwild=

	case "$client" in
	all|ALL)
	    clientwild=Yes
	    ;;
	*)
	    if ! validate_zone $client; then
		fatal_error "Undefined zone $client"
	    fi
	esac

	case "$server" in
	all|ALL)
	    serverwild=Yes
	    ;;
	*)
	    if ! validate_zone $server; then
		fatal_error "Undefined zone $server"
	    fi
	esac

	default=
	
	case $policy in
	    *:None|*:none)
		default=none
		;;
	    *:*)
		default=${policy#*:}
		if list_search $default $ACTIONS; then
		    if ! list_search $default $USEDACTIONS; then
			USEDACTIONS="$USEDACTIONS $default"
		    fi
		elif ! list_search $default $DEFAULT_MACROS; then
		    [ -f $(find_file macro.${default}) ] || fatal_error "$client $server $policy $loglevel $synparams: Default Macro $default not found"
		    DEFAULT_MACROS="$DEFAULT_MACROS $default"
		fi
		;;
	    *)
		;;
	esac

	case ${policy%:*} in
	    DROP)
		[ -n "${default:=$DROP_DEFAULT}" ]
		;;
	    REJECT)
		[ -n "${default:=$REJECT_DEFAULT}" ]
		;;
	    ACCEPT)
		[ -n "${default:=$ACCEPTT_DEFAULT}" ]
		;;
	    CONTINUE)
		;;
	    QUEUE)
		[ -n "${default:=$QUEUE_DEFAULT}" ]
		;;
	    NONE)
		[ "$client" = "$FW" -o "$server" = "$FW" ] && \
		    fatal_error " $client $server $policy $loglevel $synparams: NONE policy not allowed to/from the $FW zone"
		
		[ -n "$clientwild" -o -n "$serverwild" ] && \
		    fatal_error " $client $server $policy $loglevel $synparams: NONE policy not allowed with \"all\""
		;;
	    *)
		fatal_error "Invalid policy $policy"
		;;
	esac

	chain=${client}2${server}

	if is_policy_chain $chain ; then
	    if eval test -n \"\$${chain}_is_optional\" ; then
		eval ${chain}_is_optional=
	    else
		fatal_error "Duplicate policy: $client $server $policy"
	    fi
	fi

	[ "x$loglevel"   = "x-" ] && loglevel=
	[ "x$synparams"  = "x-" ] && synparams=

	policy=${policy%:*}
	
	[ $policy = NONE ] || ALL_POLICY_CHAINS="$ALL_POLICY_CHAINS $chain"

	eval ${chain}_is_policy=Yes
	eval ${chain}_policy=$policy
	eval ${chain}_loglevel=$loglevel
	eval ${chain}_synparams=$synparams
	eval ${chain}_default=$default

	if [ -n "${clientwild}" ]; then
	    if [ -n "${serverwild}" ]; then
		for zone in $ZONES $FW all; do
		    for zone1 in $ZONES $FW all; do
			eval pc=\$${zone}2${zone1}_policychain

			if [ -z "$pc" ]; then
			    eval ${zone}2${zone1}_policychain=$chain
			    eval ${zone}2${zone1}_policy=$policy
			    print_policy $zone $zone1
			fi
		    done
		done
	    else
		for zone in $ZONES $FW all; do
		    eval pc=\$${zone}2${server}_policychain

		    if [ -z "$pc" ]; then
			eval ${zone}2${server}_policychain=$chain
			eval ${zone}2${server}_policy=$policy
			print_policy $zone $server
		    fi
		done
	    fi
	elif [ -n "$serverwild" ]; then
	    for zone in $ZONES $FW all; do
		eval pc=\$${client}2${zone}_policychain

		    if [ -z "$pc" ]; then
			eval ${client}2${zone}_policychain=$chain
			eval ${client}2${zone}_policy=$policy
			print_policy $client $zone
		    fi
	    done
	else
	    eval ${chain}_policychain=${chain}
	    print_policy $client $server
	fi

    done < $TMP_DIR/policy
}

#
# Find broadcast addresses -- if we are compiling a script and 'detect' is specified for an interface
#                             the function returns nothing for that interface
#
find_broadcasts() {
    for interface in $ALL_INTERFACES; do
	eval bcast=\$$(chain_base $interface)_broadcast
	if [ "x$bcast" != "xdetect" -a "x${bcast}" != "x-" ]; then
	    echo $(separate_list $bcast)
	fi
    done
}

#
# Find interfaces with BROADCAST=detect -- Only returns information if we are compiling a script
#
find_bcastdetect_interfaces() {
    for interface in $ALL_INTERFACES; do
	eval bcast=\$$(chain_base $interface)_broadcast
	[ "x$bcast" = "xdetect" ] && echo $interface
    done
}

#
# Set /proc/sys/net/ipv4/ip_forward based on $IP_FORWARDING
#
setup_forwarding() {

    progress_message2 "Compiling IP Forwarding..."

    case "$IP_FORWARDING" in
    [Oo][Nn])
	save_progress_message "IP Forwarding Enabled"
	save_command "echo 1 > /proc/sys/net/ipv4/ip_forward"
	;;
    [Oo][Ff][Ff])
	save_progress_message "IP Forwarding Disabled!"
	save_command "echo 0 > /proc/sys/net/ipv4/ip_forward"
	;;
    esac
}

#
# For each entry in the CRITICALHOSTS global list, add INPUT and OUTPUT rules to
# enable traffic to/from those hosts.
#
enable_critical_hosts()
{
    for host in $CRITICALHOSTS; do
	interface=${host%:*}
	networks=${host#*:}
	do_iptables -A INPUT  -i $interface $(source_ip_range $networks) -j ACCEPT
	do_iptables -A OUTPUT -o $interface $(dest_ip_range $networks) -j ACCEPT
    done
}

#
# For each entry in the CRITICALHOSTS global list, delete the INPUT and OUTPUT rules that
# enable traffic to/from those hosts.
#
disable_critical_hosts()
{
    for host in $CRITICALHOSTS; do
	interface=${host%:*}
	networks=${host#*:}
	do_iptables -D INPUT  -i $interface $(source_ip_range $networks) -j ACCEPT
	do_iptables -D OUTPUT -o $interface $(dest_ip_range $networks) -j ACCEPT
    done
}

#
# Set up SYN flood protection
#
setup_syn_flood_chain ()
	# $1 = policy chain
	# $2 = synparams
        # $3 = loglevel
{
    local chain=@$1
    local limit=$2
    local limit_burst=

    case $limit in
	*:*)
	    limit_burst="--limit-burst ${limit#*:}"
	    limit=${limit%:*}
	    ;;
    esac

    run_iptables -N $chain
    run_iptables -A $chain -m limit --limit $limit $limit_burst -j RETURN
    [ -n "$3" ] && \
	log_rule_limit $3 $chain $chain DROP "-m limit --limit 5/min --limit-burst 5" "" ""
    run_iptables -A $chain -j DROP
}

setup_syn_flood_chains()
{
    for chain in $ALL_POLICY_CHAINS; do
	eval loglevel=\$${chain}_loglevel
	eval synparams=\$${chain}_synparams

	[ -n "$synparams" ] && setup_syn_flood_chain $chain $synparams $loglevel
    done
}

#
# Delete existing Proxy ARP
#
delete_proxy_arp() {
    indent >&3 << __EOF__
if [ -f \${VARDIR}/proxyarp ]; then
    while read address interface external haveroute; do
        qt arp -i \$external -d \$address pub
        [ -z "\$haveroute" -a -z "\$NOROUTE" ] && qt ip route del \$address dev \$interface
    done < \${VARDIR}/proxyarp

    rm -f \${VARDIR}/proxyarp
fi

for f in /proc/sys/net/ipv4/conf/*; do
    [ -f \$f/proxy_arp ] && echo 0 > \$f/proxy_arp
done

__EOF__

    [ -d $STATEDIR ] && touch $STATEDIR/proxyarp

 }

#
# Delete existing Static NAT
#
delete_nat() {
    run_iptables -t nat -F
    run_iptables -t nat -X

    [ -d $STATEDIR ] && touch $STATEDIR/nat

    indent >&3 << __EOF__

if [ -f \${VARDIR}/nat ]; then
   while read external interface; do
       del_ip_addr \$external \$interface
    done < \${VARDIR}/nat

    rm -f \${VARDIR}/nat
fi

__EOF__
}

#
# Setup ECN disabling rules
#
setup_ecn() # $1 = file name
{
    local interfaces=""
    local hosts=
    local h

    strip_file ecn $1

    progress_message2 "$DOING $1..."

    while read interface host; do
	expandv interface host
	list_search  $interface $ALL_INTERFACES || \
	    fatal_error "Unknown interface $interface"
	list_search $interface $interfaces || \
	    interfaces="$interfaces $interface"
	[ "x$host" = "x-" ] && host=
	for h in $(separate_list ${host:-0.0.0.0/0}); do
	    hosts="$hosts $interface:$h"
	done
    done < $TMP_DIR/ecn

    if [ -n "$interfaces" ]; then
	progress_message "$DOING ECN control on${interfaces}..."

	for interface in $interfaces; do
	    chain=$(ecn_chain $interface)
	    if havemanglechain $chain; then
		flushmangle $chain
	    else
		createmanglechain $chain
		run_iptables -t mangle -A POSTROUTING -p tcp -o $interface -j $chain
		run_iptables -t mangle -A OUTPUT      -p tcp -o $interface -j $chain
	    fi
	done

	for host in $hosts; do
	    interface=${host%:*}
	    h=${host#*:}
	    run_iptables -t mangle -A $(ecn_chain $interface) -p tcp $(dest_ip_range $h) -j ECN --ecn-tcp-remove
	    progress_message_and_save "   ECN Disabled to $h through $interface"
	done
    fi
}

#
# Set up an exclusion chain
#
build_exclusion_chain() # $1 = variable to store chain name into $2 = table, $3 = SOURCE exclusion list, $4 = DESTINATION exclusion list
{
    local c=excl_${EXCLUSION_SEQ} net

    EXCLUSION_SEQ=$(( $EXCLUSION_SEQ + 1 ))

    run_iptables -t $2 -N $c

    for net in $(separate_list $3); do
	run_iptables -t $2 -A $c $(source_ip_range $net) -j RETURN
    done

    for net in $(separate_list $4); do
	run_iptables -t $2 -A $c $(dest_ip_range $net) -j RETURN
    done

    case $2 in
	filter)
	    eval exists_${c}=Yes
	    ;;
	nat)
	    eval exists_nat_${c}=Yes
	    ;;
    esac

    eval $1=$c
}

#
# Setup queuing and classes
#
setup_tc1() {
    local mark_part=
    #
    # Create the TC mangle chains
    #

    createmanglechain tcpre

    if [ -n "$MANGLE_FORWARD" ]; then
	createmanglechain tcfor
	createmanglechain tcpost
    fi

    createmanglechain tcout
    #
    # Process the TC Rules File
    #
    if strip_file_and_lib_load tcrules tcrules; then
	while read mark sources dests proto ports sports user testval length tos; do
	    expandv mark sources dests proto ports sports user testval length tos
	    rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval $length $tos")
	    process_tc_rule
	done < $TMP_DIR/tcrules
    fi
    #
    # Link to the TC mangle chains from the main chains
    #

    #
    # Route marks are restored in PREROUTING/OUTPUT prior to these rules. We only send
    # packets that are not part of a marked connection to the 'tcpre/tcout' chains.
    #
    if [ -n "$ROUTEMARK_INTERFACES" -a -z "$TC_EXPERT" ]; then
	mark_part="-m mark --mark 0/0xFF00"
	#
	# But let marks in tcpre override those assigned by 'track'
	#
	for interface in $ROUTEMARK_INTERFACES; do
	    run_iptables -t mangle -A PREROUTING -i $interface -j tcpre
	done
    fi

    run_iptables -t mangle -A PREROUTING $mark_part -j tcpre
    run_iptables -t mangle -A OUTPUT     $mark_part -j tcout

    if [ -n "$MANGLE_FORWARD" ]; then
	run_iptables -t mangle -A FORWARD -j tcfor
	run_iptables -t mangle -A POSTROUTING -j tcpost
    fi

    if [ -n "$HIGH_ROUTE_MARKS" ]; then
	for chain in INPUT FORWARD; do
	    run_iptables -t mangle -I $chain -j MARK --and-mark 0xFF
	done
    fi

    if [ -n "$TC_SCRIPT" ]; then
        save_progress_message "Setting up Traffic Control..."
	append_file $TC_SCRIPT
    elif [ "$TC_ENABLED" = Internal ]; then
	strip_file_and_lib_load tcdevices tc
	strip_file_and_lib_load tcclasses tc && setup_traffic_shaping
    fi
}

setup_tc() {

    progress_message2 "$DOING Traffic Control Rules..."

    setup_tc1
}

#
# Clear Traffic Shaping
#
delete_tc()
{
    clear_one_tc() {
	save_command "tc qdisc del dev $1 root 2> /dev/null"
	save_command "tc qdisc del dev $1 ingress 2> /dev/null"

    }

    save_progress_message "Clearing Traffic Control/QOS"

    append_file tcclear

    indent >&3 << __EOF__
ip link list | while read inx interface details; do
    case \$inx in
       [0-9]*)
           qt tc qdisc del dev \${interface%:} root
           qt tc qdisc del dev \${interface%:} ingress
           ;;
       *)
           ;;
    esac
done
__EOF__
}

#
# Refresh queuing and classes
#
refresh_tc() {


    if [ -n "$CLEAR_TC" ]; then
	delete_tc
	save_command
    fi

    [ -n "$MARK_IN_FORWARD_CHAIN" ] && chain=tcfor || chain=tcpre

    #
    # Flush the TC mangle chains
    #

    if [ -n "$MANGLE_FORWARD" ]; then
	run_iptables -t mangle -F tcfor
	run_iptables -t mangle -F tcpost
    fi

    run_iptables -t mangle -F tcpre
    run_iptables -t mangle -F tcout
    #
    # Remove all exclusion chains from the mangle table
    #
    indent >&3 << __EOF__

$IPTABLES -t mangle -L -n | grep '^Chain excl_' | while read junk chain rest; do
    run_iptables -t mangle -F \$chain
    run_iptables -t mangle -X \$chain
done

__EOF__
    #
    # Process the TC Rules File
    #
    if [ -s $TMP_DIR/tcrules ]; then
	save_progress_message "Refreshing Traffic Control Rules..."

	while read mark sources dests proto ports sports user testval; do
	    expandv mark sources dests proto ports sports user testval tos
	    rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval $tos")
	    process_tc_rule
	done < $TMP_DIR/tcrules
    fi

    if [ -n "$TC_SCRIPT" ]; then
	save_progress_message "Refreshing Traffic Shaping"
	run_user_exit $TC_SCRIPT
    elif [ "$TC_ENABLED" = Internal ]; then
	[ -n "$LIB_tc_LOADED" ] && setup_traffic_shaping
    fi
}

#
# Compile refresh of the firewall
#
compile_refresh_firewall()
{
    local INDENT=""
    local DOING="Compiling Refresh of"
    local DONE="Compiled"

    save_command "refresh_firewall()"
    save_command "{"
    INDENT="    "

    append_file refresh

    #
    # Blacklist
    #
    refresh_blacklist

    ecn=$(find_file ecn)

    if [ -f $ecn ] && [ -n "$MANGLE_ENABLED" ]; then
	save_progress_message "Refreshing ECN"
	setup_ecn $ecn
    fi
    #
    # Refresh Traffic Control
    #
    [ -n "$MANGLE_ENABLED" ] && refresh_tc

    append_file refreshed

    save_command "cp -f \$(my_pathname) \${VARDIR}/.restore"

    INDENT=""

    save_command "}"

}

#
# Source the extension script for an action, if any
#
process_action_file() # $1 = File Name
{
    local user_exit=$(find_file $1)

    if [ -f $user_exit ]; then
	progress_message "Processing $user_exit ..."

	. $user_exit
    fi
}

#
# Create and record a log action chain -- Log action chains have names
# that are formed from the action name by prepending a "%" and appending
# a 1- or 2-digit sequence number. In the functions that follow,
# the CHAIN, LEVEL and TAG variable serves as arguments to the user's
# exit. We call the exit corresponding to the name of the action but we
# set CHAIN to the name of the iptables chain where rules are to be added.
# Similarly, LEVEL and TAG contain the log level and log tag respectively.
#
# For each <action>, we maintain two variables:
#
#    <action>_actchain - The action chain number.
#    <action>_chains   - List of ( level[:tag] , chainname ) pairs
#
# The maximum length of a chain name is 30 characters -- since the log
# action chain name is 2-3 characters longer than the base chain name,
# this function truncates the original chain name where necessary before
# it adds the leading "%" and trailing sequence number.

createlogactionchain() # $1 = Action Name, $2 = Log Level [: Log Tag ]
{
    local actchain= action=$1 level=$2

    eval actchain=\${${action}_actchain}

    case ${#action} in
	29|30)
	    CHAIN=$(echo $action | truncate 28) # %...n makes 30
	    ;;
	*)
	    CHAIN=${action}
	    ;;
    esac

    while havechain %${CHAIN}${actchain}; do
	actchain=$(($actchain + 1))
	[ $actchain -eq 10 -a ${#CHAIN} -eq 28 ] && CHAIN=$(echo $CHAIN | truncate 27) # %...nn makes 30
    done

    CHAIN=%${CHAIN}${actchain}

    eval ${action}_actchain=$(($actchain + 1))

    createchain $CHAIN No
    LEVEL=${level%:*}
    if [ "$LEVEL" != "$level" ]; then
	TAG=${level#*:}
    else
	TAG=
    fi

    [ none = "${LEVEL%\!}" ] && LEVEL=

    process_action_file $1

    eval ${action}_chains=\"\$${action}_chains $level $CHAIN\"

}

#
# Create an action chain and run it's associated user exit
#

createactionchain() # $1 = Action, including log level and tag if any
{
    case $1 in
	*::*)
	    fatal_error "Invalid ACTION $1"
	    ;;
	*:*:*)
	    set -- $(split $1)
	    createlogactionchain $1 $2:$3
	    ;;
	*:*)
	    set -- $(split $1)
	    createlogactionchain $1 $2
	    ;;
	*)
	    CHAIN=$1
	    LEVEL=
	    TAG=
	    createchain $CHAIN no

	    process_action_file $CHAIN
	    ;;
    esac
}

#
# Find the chain that handles the passed action. If the chain cannot be found,
# a fatal error is generated and the function does not return.
#
find_logactionchain() # $1 = Action, including log level and tag if any
{
    local fullaction=$1 action=${1%%:*} level= chains=

    case $fullaction in
	*:*)
	    level=${fullaction#*:}
	    ;;
	*)
	    havechain $action ||  fatal_error "Fatal error in find_logactionchain"
	    echo $action
	    return
	    ;;
    esac

    eval chains="\$${action}_chains"

    set -- $chains

    while [ $# -gt 0 ]; do
	[ "$1" = "$level" ] && { echo $2 ; return ; }
	shift 2
    done

    fatal_error "Fatal error in find_logactionchain"

}

#
# This function determines the logging for a subordinate action or a rule within a subordinate action
#
merge_levels() # $1=level at which superior action is called, $2=level at which the subordinate rule is called
{
    local superior=$1 subordinate=$2

    set -- $(split $1)

    case $superior in
	*:*:*)
	    case $2 in
		'none!')
		    echo ${subordinate%%:*}:'none!':$3
		    return
		    ;;
		*'!')
                    echo ${subordinate%%:*}:$2:$3
		    return
		    ;;
		*)
		    case $subordinate in
			*:*:*)
			    echo $subordinate
			    return
			    ;;
			*:*)
			    echo $subordinate:$3
			    return
			    ;;
			*)
			    echo ${subordinate%%:*}:$2:$3
			    return
			    ;;
		    esac
		    ;;
	    esac
	    ;;
	*:*)
	    case $2 in
		'none!')
		    echo ${subordinate%%:*}:'none!'
		    return
		    ;;
		*'!')
                    echo ${subordinate%%:*}:$2
		    return
		    ;;
		*)
		    case $subordinate in
			*:*)
			    echo $subordinate
			    return
			    ;;
			*)
			    echo ${subordinate%%:*}:$2
			    return
			    ;;
		    esac
		    ;;
	    esac
	    ;;
	*)
	    echo $subordinate
	    ;;
    esac
}

#
# Define the builtin actions. They are available even when USE_ACTIONS=No
#
define_builtin_actions() {
    ACTIONS="dropBcast allowBcast dropNotSyn rejNotSyn dropInvalid allowInvalid allowinUPnP allowoutUPnP forwardUPnP Limit"

    USEDACTIONS=
}

#
# This function maps old action names into their new macro equivalents
#
map_old_action() # $1 = Potential Old Action
{
    local macro= aktion

    if [ -n "$MAPOLDACTIONS" ]; then
	case $1 in
	    */*)
		echo $1
		return
		;;
	    *)
		if [ -f $(find_file $1) ]; then
		    echo $1
		    return
		fi

		case $1 in
		    Allow*)
			macro=${1#*w}
			aktion=ACCEPT
			;;
		    Drop*)
			macro=${1#*p}
			aktion=DROP
			;;
		    Reject*)
			macro=${1#*t}
			aktion=REJECT
			;;
		    *)
			echo $1
			return
			;;
		esac
	esac

	if [ -f $(find_file macro.$macro) ]; then
	    echo $macro/$aktion
	    return
	fi
    fi

    echo $1
}

# This function substitutes the second argument for the first part of the first argument up to the first colon (":")
#
# Example:
#
#         substitute_action DNAT PARAM:info:FTP
#
#         produces "DNAT:info:FTP"
#
substitute_action() # $1 = parameter, $2 = action
{
    local logpart=${2#*:}

    case $2 in
	*:*)
	    echo $1:${logpart%/}
	    ;;
	*)
	    echo $1
	    ;;
    esac
}

#
# Third phase of action processing. It needs to be here in the compiler because
#                                   it handles builtin actions.
#
process_actions3()
{    
    for xaction in $USEDACTIONS; do
	#
	# Find the chain associated with this action:level:tag
	#
	xchain=$(find_logactionchain $xaction)
	#
	# Split the action:level:tag
	#
	set -- $(split $xaction)

	xaction1=$1
	xlevel=$2
	xtag=$3

	save_progress_message "Creating action chain $xaction1"

	#
	# Handle Builtin actions
	#
	case $xaction1 in
	    dropBcast)
		if [ -n "$USEPKTTYPE" ]; then
		    case $xlevel in
			none'!')
			    ;;
			*)
			    if [ -n "$xlevel" ]; then
				log_rule_limit ${xlevel%\!} $xchain dropBcast DROP "" "$xtag" -A -m pkttype --pkt-type broadcast
				log_rule_limit ${xlevel%\!} $xchain dropBcast DROP "" "$xtag" -A -m pkttype --pkt-type multicast
			    fi
			    ;;
		    esac

		    run_iptables -A dropBcast -m pkttype --pkt-type broadcast -j DROP
		    run_iptables -A dropBcast -m pkttype --pkt-type multicast -j DROP
		else
		    for interface in $(find_bcastdetect_interfaces); do
			indent >&3 << __EOF__

ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u | while read address; do
__EOF__
			    case $xlevel in
				none*)
				    ;;
				*)
				    [ -n "$xlevel" ] && \
					indent >&3 << __EOF__
   log_rule_limit ${xlevel%\!} $xchain dropBcast DROP "" "$xtag" -A -d \$address
__EOF__
				    ;;
			    esac

			    indent >&3 << __EOF__
    run_iptables -A $xchain -d \$address -j DROP
done

__EOF__
		    done

		    for address in $(find_broadcasts) 255.255.255.255 224.0.0.0/4 ; do
			case $xlevel in
			    none*)
				;;
			    *)
				[ -n "$xlevel" ] && \
				    log_rule_limit ${xlevel%\!} $xchain dropBcast DROP "" "$xtag" -A -d $address
				;;
			esac

			run_iptables -A $xchain -d $address -j DROP
		    done
		fi
		;;
	    allowBcast)
		if [ -n "$USEPKTTYPE" ]; then
		    case $xlevel in
			none'!')
			    ;;
			*)
			    if [ -n "$xlevel" ]; then
				log_rule_limit ${xlevel%\!} $xchain allowBcast ACCEPT "" "$xtag" -A -m pkttype --pkt-type broadcast
				log_rule_limit ${xlevel%\!} $xchain allowBcast ACCEPT "" "$xtag" -A -m pkttype --pkt-type multicast
			    fi
			    ;;
		    esac

		    run_iptables -A allowBcast -m pkttype --pkt-type broadcast -j ACCEPT
		    run_iptables -A allowBcast -m pkttype --pkt-type multicast -j ACCEPT
		else
		    for interface in $(find_bcastdetect_interfaces); do
			indent >&3 << __EOF__

ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u | while read address; do
__EOF__
			case $xlevel in
			    none*)
				;;
			    *)
				[ -n "$xlevel" ] && \
				    indent >&3 << __EOF__
   log_rule_limit ${xlevel%\!} $xchain allowBcast ACCEPT "" "$xtag" -A -d \$address
__EOF__
				;;
			esac

			indent >&3 << __EOF__
    run_iptables -A $xchain -d \$address -j ACCEPT
done

__EOF__
		    done

		    for address in $(find_broadcasts) 255.255.255.255 224.0.0.0/4 ; do
			case $xlevel in
			    none*)
				;;
			    *)
				[ -n "$xlevel" ] && \
				    log_rule_limit ${xlevel%\!} $xchain allowBcast ACCEPT "" "$xtag" -A -d $address
				;;
			esac

			run_iptables -A $xchain -d $address -j ACCEPT
		    done
		fi
		;;
	    dropNotSyn)
		[ -n "$xlevel" ] && \
		    log_rule_limit ${xlevel%\!} $xchain dropNotSyn DROP "" "$xtag" -A -p tcp ! --syn
		run_iptables -A $xchain -p tcp ! --syn -j DROP
		;;
	    rejNotSyn)
		[ -n "$xlevel" ] && \
		    log_rule_limit ${xlevel%\!} $xchain rejNotSyn REJECT "" "$xtag" -A -p tcp ! --syn
		run_iptables -A $xchain -p tcp ! --syn -j REJECT --reject-with tcp-reset
		;;
	    dropInvalid)
		[ -n "$xlevel" ] && \
		    log_rule_limit ${xlevel%\!} $xchain dropInvalid DROP "" "$xtag" -A -m state --state INVALID
		run_iptables -A $xchain -m state --state INVALID -j DROP
		;;
	    allowInvalid)
		[ -n "$xlevel" ] && \
		    log_rule_limit ${xlevel%\!} $xchain allowInvalid ACCEPT "" "$xtag" -A -m state --state INVALID
		run_iptables -A $xchain -m state --state INVALID -j ACCEPT
		;;
	    forwardUPnP)
		;;
	    allowinUPnP)
		if [ -n "$xlevel" ]; then
		    log_rule_limit ${xlevel%\!} $xchain allowinUPnP ACCEPT "" "$xtag" -A -p udp --dport 1900
		    log_rule_limit ${xlevel%\!} $xchain allowinUPnP ACCEPT "" "$xtag" -A -p tcp --dport 49152
		fi

		run_iptables -A $xchain -p udp --dport 1900  -j ACCEPT
		run_iptables -A $xchain -p tcp --dport 49152 -j ACCEPT
		;;
	    allowoutUPnP)
		[ -n "$xlevel" ] && \
		    log_rule_limit ${xlevel%\!} $xchain allowoutUPnP ACCEPT "" "$xtag" -A -m owner --owner-cmd upnpd
		run_iptables -A $xchain -m owner --cmd-owner upnpd -j ACCEPT
		;;
	    Limit)
		set -- $(separate_list $xtag)

		[ $# -eq 3 ] || fatal_error "Rule must include <set name>,<max connections>,<interval> as the log tag"
		
		run_iptables -A $xchain -m recent --name $1 --set

		if [ -n "$xlevel" ]; then
		    run_iptables -N $xchain%
		    log_rule_limit $xlevel $xchain% $1 DROP "" "" -A
		    run_iptables -A $xchain% -j DROP
		    run_iptables -A $xchain -m recent --name $1 --update --seconds $3 --hitcount $(( $2 + 1 )) -j $xchain%
		else
		    run_iptables -A $xchain -m recent --update --name $1 --seconds $3 --hitcount $(( $2 + 1 )) -j DROP
		fi

		run_iptables -A $xchain -j ACCEPT
		;;
	    *)
		#
		# Not a builtin
		#
		process_action3
		;;
	esac
    done
}

#
# Add a NAT rule - Helper function for the rules file processor
#
# The caller has established the following variables:
#    COMMAND        = The current command
#                     the motions.
#    cli	    = Source IP, interface or MAC Specification
#    serv	    = Destination IP Specification
#    servport	    = Port the server is listening on
#    dest_interface = Destination Interface Specification
#    proto	    = Protocol Specification
#    addr	    = Original Destination Address
#    dports	    = Destination Port Specification. 'dports' may be changed
#		      by this function
#    cport	    = Source Port Specification
#    multiport	    = String to invoke multiport match if appropriate
#    ratelimit      = Optional rate limiting clause
#    userandgroup   = -m owner match to limit the rule to a particular user and/or group
#    logtag         = Log tag
#    excludesource  = Source Exclusion List
#
add_nat_rule() {
    local chain
    local excludedests=

    # Be sure we can NAT

    if [ -z "$NAT_ENABLED" ]; then
	fatal_error "Rule \"$rule\" requires NAT which is disabled"
    fi

    # Parse SNAT address if any

    if [ "$addr" != "${addr%:*}" ]; then
	fatal_error "SNAT may no longer be specified in a DNAT rule; use ${CONFDIR}/masq instead"
    fi

    # Set original destination address

    case $addr in
	all)
	    addr=
	    ;;
	detect)
	    eval interfaces=\$${source}_interfaces

	    if [ -n "$DETECT_DNAT_IPADDRS" -a "$source" != "$FW" ]; then

		save_command
		if [ $(list_count1 $interfaces) -eq 1 ]; then
		    save_command "addr=\$(find_first_interface_address $interfaces)"
		else
		    save_command "addr="
		    for interface in $interfaces; do
			    ident >&3 << __EOF__
addr="\$addr \$(find_first_interface_address $interface)"
__EOF__
		    done
		fi
	    else
		addr=
	    fi
	    ;;
	!*)
	    if [ $(list_count $addr) -gt 1 ]; then
		excludedests="${addr#\!}"
		addr=
	    fi
	    ;;
    esac

    addr=${addr:-0.0.0.0/0}

    # Select target

    if [ "$logtarget" = SAME ]; then
	[ -n "$servport" ] && fatal_error "Port mapping not allowed in SAME rules"
	serv1=
	for srv in $(separate_list $serv); do
	    serv1="$serv1 --to ${srv}"
	done
	target1="SAME $serv1"
    elif [ -n "$serv" ]; then
	servport="${servport:+:$servport}"
	serv1=
	for srv in $(separate_list $serv); do
	    serv1="$serv1 --to-destination ${srv}${servport}"
	done
	target1="DNAT $serv1"
    else
	target1="REDIRECT --to-port $servport"
    fi

    # Generate nat table rules

    if [ "$source" = "$FW" ]; then
	if [ -n "${excludesource}${excludedests}" ]; then
	    build_exclusion_chain chain nat "$excludesource" $excludedests

	    for adr in $(separate_list $addr); do
		run_iptables2 -t nat -A OUTPUT $cli $proto $userandgroup $multiport $sports $dports $(dest_ip_range $adr) -j $chain
	    done

	    if [ -n "$loglevel" ]; then
		log_rule_limit $loglevel $chain OUTPUT $logtarget "$ratelimit" "$logtag" -A -t nat
	    fi

	    addnatrule $chain $ratelimit $proto -j $target1 # Protocol is necessary for port redirection
	else
	    for adr in $(separate_list $addr); do
		if [ -n "$loglevel" ]; then
		    log_rule_limit $loglevel OUTPUT OUTPUT $logtarget "$ratelimit" "$logtag" -A -t nat \
			$(fix_bang $proto $cli $sports $userandgroup $(dest_ip_range $adr) $multiport $dports)
		fi

		run_iptables2 -t nat -A OUTPUT $ratelimit $proto $sports $userandgroup $(dest_ip_range $adr) $multiport $dports -j $target1
	    done
	fi
    else
	if [ -n "${excludesource}${excludedests}" ]; then
	    build_exclusion_chain chain nat "$excludesource" $excludedests

	    if [ $addr = detect ]; then
		ensurenatchain $(dnat_chain $source)

		indent >&3 << __EOF__

for adr in \$addr; do
    run_iptables -t nat -A $(fix_bang $(dnat_chain $source) $cli $proto $multiport $sports $dports) -d \$adr -j $chain
__EOF__
	    else
		for adr in $(separate_list $addr); do
		    addnatrule $(dnat_chain $source) $cli $proto $multiport $sports $dports $(dest_ip_range $adr) -j $chain
		done
	    fi

	    if [ -n "$loglevel" ]; then
		log_rule_limit $loglevel $chain $(dnat_chain $source) $logtarget "$ratelimit" "$logtag" -A -t nat
	    fi

	    addnatrule $chain $ratelimit $proto -j $target1 # Protocol is necessary for port redirection
	else
	    chain=$(dnat_chain $source)

	    if [ $addr = detect ]; then
		ensurenatchain $chain

		indent >&3 << __EOF__

for adr in \$addr; do
__EOF__
		if [ -n "$loglevel" ]; then
		    indent >&3 << __EOF__
    log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A -t nat $(fix_bang $proto $cli $sports $multiport $dports) -d \$adr
__EOF__
		fi

		indent >&3 << __EOF__
    run_iptables -t nat -A $chain $(fix_bang $proto $ratelimit $cli $sports $multiport $dports) -d \$adr -j $target1
__EOF__
	    else
		for adr in $(separate_list $addr); do
		    if [ -n "$loglevel" ]; then
			ensurenatchain $chain
			log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A -t nat \
			    $(fix_bang $proto $cli $sports $(dest_ip_range $adr) $multiport $dports)
		    fi

		    addnatrule $chain $proto $ratelimit $cli $sports \
			-d $adr $multiport $dports -j $target1
		done
	    fi
	fi
    fi

    # Replace destination port by the new destination port

    if [ -n "$servport" ]; then
	if [ -z "$multiport" ]; then
	    dports="--dport ${servport#*:}"
	else
	    dports="--dports ${servport#*:}"
	fi
    fi

    [ "x$addr" = "x0.0.0.0/0" ] && addr=
    ratelimit=
}

#
# Add one Filter Rule
#
# The caller has established the following variables:
#	 COMMAND	= current command.
#	 client		= SOURCE IP or MAC
#	 server		= DESTINATION IP or interface
#	 protocol	= Protocol
#	 address	= Original Destination Address
#	 port		= Destination Port
#	 cport		= Source Port
#	 multioption	= String to invoke multiport match if appropriate
#	 servport	= Port the server listens on
#	 chain		= The canonical chain for this rule
#	 logchain	= The chain that should be mentioned in log messages
#	 ratelimit	= Optional rate limiting clause
#	 userandgroup	= -m owner clause
#	 userspec	= User name
#	 logtag		= Log tag
#	 policy		= Applicable Policy
#
add_a_rule() {
    local natrule=

    do_ports() {
	if [ -n "$port" ]; then
	    dports="--dport"
	    if [ -n "$multioption" -a "$port" != "${port%,*}" ]; then
		multiport="$multioption"
		dports="--dports"
	    fi
	    dports="$dports $port"
	fi

	if [ -n "$cport" ]; then
	    sports="--sport"
	    if [ -n "$multioption" -a "$cport" != "${cport%,*}" ]; then
		multiport="$multioption"
		sports="--sports"
	    fi
	    sports="$sports $cport"
	fi
    }

    interface_error()
    {
	fatal_error "Unknown interface $1 in rule: \"$rule\""
    }
    
    rule_interface_verify()
    {
	verify_interface $1 || interface_error $1
    }
    
    handle_exclusion()
    {
	build_exclusion_chain chain filter "$excludesource" "$excludedest"

	if [ -n "$addr" -a -n "$CONNTRACK_MATCH" ]; then
	    for adr in $(separate_list $addr); do
		run_iptables -A $logchain $state $(fix_bang $proto $sports $multiport $dports) $user -m conntrack --ctorigdst $adr -j $chain
	    done
	    addr=
	else
	    run_iptables -A $logchain $state $(fix_bang $cli $proto $sports $multiport $dports) $user -j $chain
	fi

	cli=
	proto=
	sports=
	multiport=
	dports=
	user=
	state=
    }

    do_ipp2p() {
	[ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. Rule: \"$rule\""
	
	dports="-m ipp2p --${port:-ipp2p}"

	case $proto in
	    ipp2p|IPP2P|ipp2p:tcp|IPP2P:TCP)
		port=
		proto=tcp
		do_ports
		;;
	    ipp2p:udp|IPP2P:UDP)
		port=
		proto=udp
		do_ports
		;;
	    ipp2p:all|IPP2P:ALL)
		port=
		proto=all
		;;
	    *)
		fatal_error "Invalid IPP2P protocol ${proto#*:}. Rule:  \"$rule\""
		;;
	esac
    }

    # Set source variables. The 'cli' variable will hold the client match predicate(s).

    cli=

    case "$client" in
	-)
	    ;;
	*:*)
	    rule_interface_verify ${client%:*}
	    cli="$(match_source_dev ${client%:*}) $(source_ip_range ${client#*:})"
	    ;;
	*.*.*|+*)
	    cli="$(source_ip_range $client)"
	    ;;
	~*|!~*)
	    cli=$(mac_match $client)
	    ;;
	*)
	    if [ -n "$client" ]; then
		rule_interface_verify $client
		cli="$(match_source_dev $client)"
	    fi
	    ;;
    esac

    # Set destination variables - 'serv' and 'dest_interface' hold the server match predicate(s).

    dest_interface=
    serv=
    
    case "$server" in
	-)
	    ;;
	*.*.*|+*)
	    serv=$server
	    ;;
	~*|!~*)
	    fatal_error "Rule \"$rule\" - Destination may not be specified by MAC Address"
	    ;;
	*)
	    if [ -n "$server" ]; then
		[ -n "$nonat" ] && fatal_error "Destination interface not allowed with $logtarget"
		rule_interface_verify $server
		dest_interface="$(match_dest_dev $server)"
	    fi
	    ;;
    esac

    # Setup protocol and port variables

    sports=
    dports=
    proto=$protocol
    addr=$address
    servport=$serverport
    multiport=
    user="$userandgroup"
    
    # Restore $chain to the canonical chain.

    chain=$logchain

    [ x$port  = x- ] && port=
    [ x$cport = x- ] && cport=

    case $proto in
	tcp|TCP|6)
	    do_ports
	    ;;
	tcp:syn)
	    proto="tcp --syn"
	    do_ports
	    ;;
	udp|UDP|17)
	    do_ports
	    ;;
	icmp|ICMP|1)
	    [ -n "$port" ] && dports="--icmp-type $port"
	    ;;
	all|ALL)
	    [ -n "$port" ] && \
		fatal_error "Port number not allowed with protocol \"all\"; rule: \"$rule\""
	    proto=
	    ;;
	ipp2p|IPP2P|ipp2p:*|IPP2P:*)
	    do_ipp2p
	    ;;
	*)
	    [ -n "$port" ] && \
		fatal_error "Port number not allowed with protocol \"$proto\"; rule: \"$rule\""
	    ;;
    esac

    proto="${proto:+-p $proto}"

    # Some misc. setup

    case "$logtarget" in
	ACCEPT|DROP|REJECT|CONTINUE)
	    if [ "$SECTION" != DONE ]; then
		if [ -z "$proto" -a -z "$cli" -a -z "$serv" -a -z "$servport" -a -z "$user" -a -z "$excludesource" -a -z "$excludedest" ] ; then
		    error_message "WARNING -- Rule \"$rule\" is a POLICY"
		    error_message "	   -- and should be moved to the policy file"
		fi
	    fi
	    ;;
	REDIRECT)
	    [ -n "$excludedest" ] && fatal_error "Invalid DEST for this ACTION; rule \"$rule\""
	    
	    [ -n "$serv" ] && \
		fatal_error "REDIRECT rules cannot  specify a server IP; rule: \"$rule\""
	    servport=${servport:=$port}
	    natrule=Yes
	    ;;
	DNAT|SAME)
	    [ -n "$excludedest" ] && fatal_error "Invalid DEST for this ACTION; rule \"$rule\""
	    
	    [ -n "$serv" ] || \
		fatal_error "$logtarget rules require a server address; rule: \"$rule\""
	    natrule=Yes
	    ;;
	LOG)
	    [ -z "$loglevel" ] && \
		fatal_error "LOG requires log level"
	    ;;
    esac

    case $SECTION in
	ESTABLISHED|RELATED)
	    [ -n "$FASTACCEPT" ] && fatal_error "Entries in the $SECTION SECTION of the rules file not permitted with FASTACCEPT=Yes"
	    state="-m state --state $SECTION"
	    ;;
	*)
	    state=
	    ;;
    esac
    
    if [ -n "${serv}${servport}" ]; then

        # A specific server or server port given

	if [ -n "$natrule" ]; then
	    add_nat_rule
	    [ $policy = ACCEPT ] && return
	elif [ -n "$servport" -a "$servport" != "$port" ]; then
	    fatal_error "Only DNAT, SAME and REDIRECT rules may specify destination port mapping; rule \"$rule\""
	fi
	
	if [ -n "${excludesource}${excludedest}" ]; then
	    handle_exclusion
	fi
	
	if [ -z "$dnat_only" ]; then
	    if [ -n "$serv" ]; then
		for serv1 in $(separate_list $serv); do
		    for srv in $(firewall_ip_range $serv1); do
			if [ -n "$addr" -a -n "$CONNTRACK_MATCH" ]; then
			    if [ "$addr" = detect ]; then
				indent >&3 << __EOF__
    run_iptables -A $chain $state $proto $ratelimit $multiport $cli $sports $(dest_ip_range $srv) $dports -m conntrack --ctorigdst \$adr $user -j $target
done

__EOF__
			    else
				for adr in $(separate_list $addr); do
				    if [ -n "$loglevel" -a -z "$natrule" ]; then
					log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A -m conntrack --ctorigdst $adr \
					    $user $(fix_bang $proto $sports $multiport $cli $(dest_ip_range $srv) $dports) $state
				    fi
				    
				    run_iptables2 -A $chain $state $proto $ratelimit $multiport $cli $sports \
					$(dest_ip_range $srv) $dports -m conntrack --ctorigdst $adr $user -j $target
				done
			    fi
			else
			    if [ -n "$loglevel" -a -z "$natrule" ]; then
				log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A $user \
				    $state $(fix_bang $proto $sports $multiport $cli $(dest_ip_range $srv) $dports)
			    fi
			    
			    if [ -n "$nonat" ]; then
				addnatrule $(dnat_chain $source) $proto $multiport \
				    $cli $sports $(dest_ip_range $srv) $dports $ratelimit $user -j RETURN
			    fi
			    
			    if [ "$logtarget" != NONAT ]; then
				run_iptables2 -A $chain $state $proto $multiport $cli $sports \
				    $(dest_ip_range $srv) $dports $ratelimit $user -j $target
			    fi
			fi
		    done
		done
	    else
		if [ -n "$loglevel" -a -z "$natrule" ]; then
		    log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A $user \
			$state $(fix_bang $proto $sports $multiport $cli $dports)
		fi
		
		[ -n "$nonat" ] && \
		    addnatrule $(dnat_chain $source) $proto $multiport \
		    $cli $sports $dports $ratelimit $user -j RETURN
		    
		    [ "$logtarget" != NONAT ] && \
			run_iptables2 -A $chain $state $proto $multiport $cli $sports \
			$dports $ratelimit $user -j $target
	    fi
	fi
    else

    # Destination is a simple zone

	if [ -n "${excludesource}${excludedest}" ]; then
	    handle_exclusion
	fi

	if [ -n "$addr" ]; then
	    for adr in $(separate_list $addr); do
		if [ -n "$loglevel" ]; then
		    log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A $user \
			$state $(fix_bang $proto $multiport $cli $dest_interface $sports $dports -m conntrack --ctorigdst $adr)
		fi
		
		if [ "$logtarget" != LOG ]; then
		    if [ -n "$nonat" ]; then
			addnatrule $(dnat_chain $source) $proto $multiport \
			    $cli $sports $dports $ratelimit $user  -m conntrack --ctorigdst $adr -j RETURN
		    fi
		    
		    if [ "$logtarget" != NONAT ]; then
			run_iptables2 -A $chain $state $proto $multiport $cli $dest_interface \
			    $sports $dports $ratelimit $user  -m conntrack --ctorigdst $adr -j $target
		    fi
		fi
	    done
	else
	    if [ -n "$loglevel" ]; then
		log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A $user \
		    $state $(fix_bang $proto $multiport $cli $dest_interface $sports $dports)
	    fi
	    
	    if [ "$logtarget" != LOG ]; then
		if [ -n "$nonat" ]; then
		    addnatrule $(dnat_chain $source) $proto $multiport \
			$cli $sports $dports $ratelimit $user -j RETURN
		fi
		
		if [ "$logtarget" != NONAT ]; then
		    run_iptables2 -A $chain $state $proto $multiport $cli $dest_interface \
			$sports $dports $ratelimit $user -j $target
		fi
	    fi
	fi
    fi
}

#
# Process the contents of the USER/GROUP column
#
process_userspec()
{
    [ "x$userspec" = x- ]   && userspec=

    if [ -n "$userspec" ]; then

	userandgroup="-m owner"

	case "$userspec" in
	    !*+*)
		if [ -n "${userspec#*+}" ]; then
		    userandgroup="$userandgroup ! --cmd-owner ${userspec#*+}"
		fi
		userspec=${userspec%+*}
		;;
	    *+*)
		if [ -n "${userspec#*+}" ]; then
		    userandgroup="$userandgroup --cmd-owner ${userspec#*+}"
		fi
		userspec=${userspec%+*}
		;;
	esac

	case "$userspec" in
	    !*:*)
		if [ "$userspec" != "!:" ]; then
		    temp="${userspec#!}"
		    temp="${temp%:*}"
		    [ -n "$temp" ] && userandgroup="$userandgroup ! --uid-owner $temp"
		    temp="${userspec#*:}"
		    [ -n "$temp" ] && userandgroup="$userandgroup ! --gid-owner $temp"
		fi
		;;
	    *:*)
		if [ "$userspec" != ":" ]; then
		    temp="${userspec%:*}"
		    [ -n "$temp" ] && userandgroup="$userandgroup --uid-owner $temp"
		    temp="${userspec#*:}"
		    [ -n "$temp" ] && userandgroup="$userandgroup --gid-owner $temp"
		fi
		;;
	    !*)
		[ "$userspec" != "!" ] && userandgroup="$userandgroup ! --uid-owner ${userspec#!}"
		;;
	    *)
		[ -n "$userspec" ] && userandgroup="$userandgroup --uid-owner $userspec"
		;;
	esac

	[ "$userandgroup" = "-m owner" ] && userandgroup=
    fi
}

#
# Process the RATE/LIMIT column contents
#
process_ratelimit() {
    [ "x$ratelimit" = "x-" ] && ratelimit=

    if [ -n "$ratelimit" ]; then
	case $ratelimit in
	    *:*)
		ratelimit="-m limit --limit ${ratelimit%:*} --limit-burst ${ratelimit#*:}"
		;;
	    *)
		ratelimit="-m limit --limit $ratelimit"
		;;
	esac
    fi
}

#
# Process a record from the rules file
#
process_rule() # $1 = target
               # $2 = clients
               # $3 = servers
               # $4 = protocol
               # $5 = ports
               # $6 = cports
               # $7 = address
               # $8 = ratelimit
               # $9 = userspec
{
    local target="$1"
    local clients="$2"
    local servers="$3"
    local protocol="$4"
    local ports="$5"
    local cports="$6"
    local address="$7"
    local ratelimit="$8"
    local userspec="$9"
    local userandgroup=
    local logtag=
    local nonat=

    # # # # # F u n c t i o n   B o d y # # # # #

    process_ratelimit

    # Isolate log level

    if [ "$target" = "${target%:*}" ]; then
	loglevel=
    else
	loglevel="${target#*:}"
	target="${target%%:*}"
	expandv loglevel
	if [ "$loglevel" != "${loglevel%:*}" ]; then
	    logtag="${loglevel#*:}"
	    loglevel="${loglevel%:*}"
	    expandv logtag
	fi

	case $loglevel in
	    none*)
		loglevel=
		[ $target = LOG ] && return
		;;
	esac

	loglevel=${loglevel%\!}
    fi
    #
    # Save the original target in 'logtarget' for logging rules
    #
    logtarget=${target%-}
    #
    # Targets ending in "-" only apply to the nat table
    #
    [ $target = $logtarget ] && dnat_only= || dnat_only=Yes

    # Tranform the rule:
    #
    # - parse the user specification
    # - set 'target' to the filter table target.
    # - make $FW the destination for REDIRECT
    # - remove '-' suffix from logtargets while setting 'dnat_only'
    # - clear 'address' if it has been set to '-'

    [ "x$address" = "x-" ] && address=

    process_userspec

    case $target in
	ACCEPT+|NONAT)
	    [ $SECTION = NEW ] || fatal_error "$target rules are not allowed in the $SECTION SECTION"
	    nonat=Yes
	    target=ACCEPT
	    ;;
	ACCEPT|LOG)
	    ;;
	DROP)
	    [ -n "$ratelimit" ] && fatal_error "Rate Limiting not available with DROP"
	    ;;
	REJECT)
	    target=reject
	    ;;
	CONTINUE)
	    target=RETURN
	    ;;
	DNAT*|SAME*)
	    [ $SECTION = NEW ] || fatal_error "$target rules are not allowed in the $SECTION SECTION"
	    target=ACCEPT
	    address=${address:=detect}
	    ;;
	REDIRECT*)
	    [ $SECTION = NEW ] || fatal_error "REDIRECT rules are not allowed in the $SECTION SECTION"
	    target=ACCEPT
	    address=${address:=all}
	    if [ "x-" = "x$servers" ]; then
		servers=$FW
	    else
		servers="$FW::$servers"
	    fi
	    ;;
	*-)
	    [ $SECTION = NEW ] || fatal_error "$target rules are not allowed in the $SECTION SECTION"
	    ;;
    esac

    # Parse and validate source

    if [ "$clients" = "${clients%:*}" ]; then
	clientzone="$clients"
	clients=
    else
	clientzone="${clients%%:*}"
	clients="${clients#*:}"
	[ -z "$clientzone" -o -z "$clients" ] && \
	   fatal_error "Empty source zone or qualifier: rule \"$rule\""
    fi

    excludesource=

    case $clients in
	*!*!*)
	    fatal_error "Invalid SOURCE in rule \"$rule\""
	    ;;
	!*)
	    if [ $(list_count $clients) -gt 1 ]; then
		excludesource=${clients#!}
		clients=
	    fi
	    ;;
	*!*)
	    excludesource=${clients#*!}
	    clients=${clients%!*}
	    ;;
    esac

    validate_zone $clientzone || fatal_error "Undefined Client Zone in rule \"$rule\""

    source=$clientzone

    if [ $source != $FW -a -n "$userspec" ]; then
	fatal_error "Invalid use of a user-qualification: rule \"$rule\""
    fi

    # Parse and validate destination

    if [ "$servers" = "${servers%:*}" ] ; then
	serverzone="$servers"
	servers=
	serverport=
    else
	serverzone="${servers%%:*}"
	servers="${servers#*:}"
	if [ "$servers" != "${servers%:*}" ] ; then
	    serverport="${servers#*:}"
	    servers="${servers%:*}"
	    [ -z "$serverzone" -o -z "$serverport" ] && \
	        fatal_error "Empty destination zone or server port: rule \"$rule\""
	    if [ $(list_count $servers) -gt 1 ]; then
		case $servers in
		    !*)
			fatal_error "Exclude lists not supported in the DEST column"
			;;
		esac
	    fi
	else
	    serverport=
	    [ -z "$serverzone" -o -z "$servers" ] && \
	        fatal_error "Empty destination zone or qualifier: rule \"$rule\""
	fi
    fi

    excludedest=

    case $servers in
	*!*!*)
	    fatal_error "Invalid DEST in rule \"$rule\""
	    ;;
	!*)
	    if [ $(list_count $servers) -gt 1 ]; then
		excludedest=${servers#*!}
		servers=
	    fi
	    ;;
	*!*)
	    excludedest=${servers#*!}
	    servers=${servers%!*}
	    ;;
    esac

    if ! validate_zone $serverzone; then
	fatal_error "Undefined Server Zone in rule \"$rule\""
    fi

    dest=$serverzone

    # Ensure that this rule doesn't apply to a NONE policy pair of zones

    chain=${source}2${dest}

    # If we have one or more exclusion lists, we will create a new chain and
    # store it's name in 'chain'. We still want log rules to reflect the
    # canonical chain so we store it's name in $logchain.

    logchain=$chain

    eval policy=\$${chain}_policy

    [ -z "$policy" ] && \
	fatal_error "No policy defined from zone $source to zone $dest"

    [ $policy = NONE ] && \
	fatal_error "Rules may not override a NONE policy: rule \"$rule\""

    [ "x$protocol" = "x-" ] && protocol=all || protocol=${protocol:=all}

    ensurechain $chain

    # Generate Netfilter rule(s)

    case $logtarget in
	DNAT*|SAME)

	    if [ -n "$XMULTIPORT" ] && \
	       ! list_search $protocol "icmp" "ICMP" "1" && \
	       [ $(( $(list_count $ports)  + $(list_count1 $(split $ports ) ) ))  -le 16 -a \
		 $(( $(list_count $cports) + $(list_count1 $(split $cports ) ) )) -le 16 ]
		then
	        #
	        # Extended MULTIPORT is enabled, and less than
	        # 16 ports are listed (port ranges count as two ports) - use multiport match.
	        #
		multioption="-m multiport"
		for client in $(separate_list ${clients:=-}); do
		    #
		    # add_a_rule() modifies these so we must set their values each time
		    #
		    server=${servers:=-}
		    port=${ports:=-}
		    cport=${cports:=-}
		    add_a_rule
		done
	    elif [ -n "$MULTIPORT" ] && \
	       ! list_search $protocol "icmp" "ICMP" "1" && \
	       [ "$ports" = "${ports%:*}" -a \
		"$cports" = "${cports%:*}" -a \
		$(list_count $ports) -le 15 -a \
		$(list_count $cports) -le 15 ]
		then
	        #
	        # MULTIPORT is enabled, there are no port ranges in the rule and less than
	        # 16 ports are listed - use multiport match.
	        #
		multioption="-m multiport"
		for client in $(separate_list ${clients:=-}); do
		    #
		    # add_a_rule() modifies these so we must set their values each time
		    #
		    server=${servers:=-}
		    port=${ports:=-}
		    cport=${cports:=-}
		    add_a_rule
		done
	    else
	        #
	        # MULTIPORT is disabled or the rule isn't compatible with multiport match
	        #
		multioption=
		for client in $(separate_list ${clients:=-}); do
		    for port in $(separate_list ${ports:=-}); do
			for cport in $(separate_list ${cports:=-}); do
			    server=${servers:=-}
			    add_a_rule
			done
		    done
		done
	    fi
	    ;;
	*)

	    if [ -n "$XMULTIPORT" ] && \
	       ! list_search $protocol "icmp" "ICMP" "1" && \
	       [ $(( $(list_count $ports)  + $(list_count1 $(split $ports ) ) ))  -le 16 -a \
		 $(( $(list_count $cports) + $(list_count1 $(split $cports ) ) )) -le 16  ]
		then
	        #
	        # Extended MULTIPORT is enabled, and less than
	        # 16 ports are listed (port ranges count as two ports) - use multiport match.
	        #
		multioption="-m multiport"
		for client in $(separate_list ${clients:=-}); do
		    for server in $(separate_list ${servers:=-}); do
		        #
		        # add_a_rule() modifies these so we must set their values each time
		        #
			port=${ports:=-}
			cport=${cports:=-}
			add_a_rule
		    done
		done
	    elif [ -n "$MULTIPORT" ] && \
	       ! list_search $protocol "icmp" "ICMP" "1" && \
	       [ "$ports" = "${ports%:*}" -a \
		"$cports" = "${cports%:*}" -a \
		$(list_count $ports) -le 15 -a \
		$(list_count $cports) -le 15 ]
		then
	        #
	        # MULTIPORT is enabled, there are no port ranges in the rule and less than
	        # 16 ports are listed - use multiport match.
	        #
		multioption="-m multiport"
		for client in $(separate_list ${clients:=-}); do
		    for server in $(separate_list ${servers:=-}); do
		        #
		        # add_a_rule() modifies these so we must set their values each time
		        #
			port=${ports:=-}
			cport=${cports:=-}
			add_a_rule
		    done
		done
	    else
	        #
	        # MULTIPORT is disabled or the rule isn't compatible with multiport match
	        #
		multioption=
		for client in $(separate_list ${clients:=-}); do
		    for server in $(separate_list ${servers:=-}); do
			for port in $(separate_list ${ports:=-}); do
			    for cport in $(separate_list ${cports:=-}); do
				add_a_rule
			    done
			done
		    done
		done
	    fi
	    ;;
    esac
    #
    # Report Result
    #
    progress_message "   Rule \"$rule\" $DONE."
    save_progress_message_short "   Rule \\\"$rule\\\" added."
}

#
# Process a macro invocation in the rules file
#

process_macro() # $1 = target
               # $2 = param
               # $2 = clients
               # $3 = servers
               # $4 = protocol
               # $5 = ports
               # $6 = cports
               # $7 = address
               # $8 = ratelimit
               # $9 = userspec
{
    local itarget="$1"
    local param="$2"
    local iclients="$3"
    local iservers="$4"
    local iprotocol="$5"
    local iports="$6"
    local icports="$7"
    local iaddress="$8"
    local iratelimit="$9"
    local iuserspec="${10}"

    progress_message "..Expanding Macro $(find_file macro.${itarget%%:*})..."

    while read mtarget mclients mservers mprotocol mports mcports mratelimit muserspec; do
	expandv mtarget mclients mservers mprotocol mports mcports mratelimit muserspec

	mtarget=$(merge_levels $itarget $mtarget)

	case $mtarget in
	    PARAM|PARAM:*)
		[ -n "$param" ] && mtarget=$(substitute_action $param $mtarget) || fatal_error "PARAM requires that a parameter be supplied in macro invocation"
		;;
	esac

	case ${mtarget%%:*} in
	    ACCEPT|ACCEPT+|NONAT|DROP|REJECT|DNAT|DNAT-|REDIRECT|REDIRECT-|LOG|CONTINUE|QUEUE|SAME|SAME-)
		;;
	    *)
		if list_search ${mtarget%%:*} $ACTIONS; then
		    if ! list_search $mtarget $USEDACTIONS; then
			createactionchain $mtarget
			USEDACTIONS="$USEDACTIONS $mtarget"
		    fi

		    mtarget=$(find_logactionchain $mtarget)
		else
		    fatal_error "Invalid Action in rule \"$mtarget ${mclients:--} ${mservers:--} ${mprotocol:--} ${mports:--} ${mcports:--} ${xaddress:--} ${mratelimit:--} ${muserspec:--}\""
		fi
		;;
	esac

	if [ -n "$mclients" ]; then
	    case $mclients in
		-|SOURCE)
		    mclients=${iclients}
		    ;;
		DEST)
		    mclients=${iservers}
		    ;;
		*)
		    mclients=$(merge_macro_source_dest $mclients $iclients)
		    ;;
	    esac
	else
	    mclients=${iclients}
	fi

	if [ -n "$mservers" ]; then
	    case $mservers in
		-|DEST)
		    mservers=${iservers}
		    ;;
		SOURCE)
		    mservers=${iclients}
		    ;;
		*)
		    mservers=$(merge_macro_source_dest $mservers $iservers)
		    ;;
	    esac
	else
	    mservers=${iservers}
	fi

	[ -n "$iprotocol" ]  && [ "x${iprotocol}" != x- ]  && mprotocol=$iprotocol
	[ -n "$iports" ]     && [ "x${iports}" != x- ]     && mports=$iports
	[ -n "$icports" ]    && [ "x${icports}" != x- ]    && mcports=$icports
	[ -n "$iratelimit" ] && [ "x${iratelimit}" != x- ] && mratelimit=$iratelimit
	[ -n "$iuserspec" ]  && [ "x${iuserspec}" != x- ]  && muserspec=$iuserspec

	rule="$mtarget ${mclients=-} ${mservers:=-} ${mprotocol:=-} ${mports:=-} ${mcports:=-} ${xaddress:=-} ${mratelimit:=-} ${muserspec:=-}"
	process_rule $mtarget $mclients $mservers $mprotocol $mports $mcports ${iaddress:=-} $mratelimit $muserspec

    done < $TMP_DIR/macro.${itarget%%:*}

    progress_message "..End Macro"

}

#
# Process the rules file
#
process_rules()
{
    #
    # Process a rule where the source or destination is "all"
    #
    process_wildcard_rule() # $1 = Yes, if this is a macro, $2 = Yes if we want intrazone traffic
    {
	local yclients yservers ysourcezone ydestzone ypolicy

	for yclients in $xclients; do
	    for yservers in $xservers; do
		ysourcezone=${yclients%%:*}
		ydestzone=${yservers%%:*}
		if [ "${ysourcezone}" != "${ydestzone}" -o "$2" = Yes ] ; then
		    eval ypolicy=\$${ysourcezone}2${ydestzone}_policy
		    if [ "$ypolicy" != NONE ] ; then
			if [ "$1" = Yes ]; then
			    process_macro $xtarget "$xparam" $yclients $yservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec
			else
			    rule="$xtarget $yclients $yservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec"
			    process_rule $xtarget $yclients $yservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec
			fi
		    fi
		fi
	    done
	done
    }

    do_it() # $1 = "Yes" if the target is a macro.
    {
	expandv xprotocol xports xcports xaddress xratelimit xuserspec intrazone=

	if [ -z "$SECTIONS" ]; then
	    finish_section ESTABLISHED,RELATED
	    SECTIONS="ESTABLISHED RELATED NEW"
	    SECTION=NEW
	fi

	case $xclients in
	    all+)
		xclients=all
		intrazone=Yes
		;;
	    all+-|all-+)
		xclients=all-
		intrazone=Yes
		;;
	esac

	case $xservers in
	    all+)
		xservers=all
		intrazone=Yes
		;;
	    all+-|all-+)
		xservers=all-
		intrazone=Yes
		;;
	esac

	case $xclients in
	    all|all-)
		[ $xclients = all ] && xclients="$ZONES $FW" || xclients="$ZONES"

		if [ "x$xservers" = xall ]; then
		    xservers="$ZONES $FW"
		elif [ "x$xservers" = xall- ]; then
		    xservers="$ZONES"
		fi

		process_wildcard_rule "$1" $intrazone
		return
		;;
	esac

	case $xservers in
	    all|all-)
		xservers="$ZONES $FW"
		process_wildcard_rule "$1" $intrazone
		return
		;;
	esac

	if [ "$1" = Yes ]; then
	    process_macro $xtarget "$xparam" $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec
	else
	    rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec"
	    process_rule $xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec
	fi
    }

    while read xtarget xclients xservers xprotocol xports xcports xaddress xratelimit xuserspec; do
	expandv xtarget xclients xservers

	if [ "x$xclients" = xnone -o "x$servers" = xnone ]; then
	    rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec"
	    progress_message "   Rule \"$rule\" ignored."
	    continue
	fi

	case "${xtarget%%:*}" in
	    ACCEPT|ACCEPT+|NONAT|DROP|REJECT|DNAT|DNAT-|REDIRECT|REDIRECT-|LOG|CONTINUE|QUEUE|SAME|SAME-)
		do_it No
		;;
	    SECTION)
		list_search $xclients $SECTIONS && fatal_error "Duplicate or out of order SECTION $xclients"

		case $xclients in
		    ESTABLISHED)
			SECTIONS=ESTABLISHED
			;;
		    RELATED)
			finish_section ESTABLISHED
			SECTIONS="ESTABLISHED RELATED"
		        ;;
		    NEW)
			[ $SECTION = RELATED ] && finish_section RELATED || finish_section ESTABLISHED,RELATED
			SECTIONS="ESTABLISHED RELATED NEW"
			;;
		    *)
			fatal_error "Invalid SECTION $xclients"
			;;
		esac

		[ -n "$xservers" ] && fatal_error "Invalid SECTION $xclients $xservers"

		SECTION=$xclients
		;;
	    *)
		if list_search ${xtarget%%:*} $ACTIONS; then
		    if ! list_search $xtarget $USEDACTIONS; then
			createactionchain $xtarget
			USEDACTIONS="$USEDACTIONS $xtarget"
		    fi

		    xtarget=$(find_logactionchain $xtarget)
		    do_it No
		else
		    xtarget1=$(map_old_action ${xtarget%%:*})

		    case $xtarget1 in
			*/*)
			    xparam=${xtarget1#*/}
			    xtarget1=${xtarget1%%/*}
			    xtarget=$(substitute_action $xtarget1 $xtarget)
			    ;;
			*)
			    xparam=
			    ;;
		    esac

		    f=macro.$xtarget1

		    if [ -f $TMP_DIR/$f ]; then
			do_it Yes
		    else
			fn=$(find_file $f)

			if [ -f $fn ]; then
			    strip_file $f $fn
			    do_it Yes
			else
			    rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec"
			    fatal_error "Invalid Action in rule \"$rule\""
			fi
		    fi
		fi
		;;

	esac
    done < $TMP_DIR/rules

    case $SECTION in
	ESTABLISHED)
	    finish_section ESTABLISHED,RELATED
	    ;;
	RELATED)
	    finish_section RELATED
	    ;;
    esac

    SECTION=DONE
}

#
# Process a default macro
#
process_default_macro() # $1 = macro name
{
    local macro=$1
    local address=
    local multioption=
    local servport=
    local chain=$1
    local logchain=$1
    local userandgroup=
    local logtag=
    local excludesource=
    local target client server protocol port cport ratelimit userspec rule

    havechain $macro && fatal_error "Illegal duplicate default macro name: $macro"

    createchain $macro no
    strip_file macro.$macro
    progress_message "..Expanding Default Macro $(find_file macro.${macro}) into chain $macro..."

    while read target client server protocol port cport ratelimit userspec; do
	expandv target client server protocol port cport ratelimit userspec
	rule="$target ${client:--} ${server:--} ${protocol:--} ${port:--} ${cport:--} ${ratelimit:--} ${userspec:--}"

	case $target in
	    PARAM|PARAM:*)
		fatal_error "Invalid target ($target) in default macro $macro"
		;;
	esac

	case ${target} in
	    ACCEPT|DROP|REJECT)
		;;
	    *)
		if list_search $target $ACTIONS; then
		    if ! list_search $target $USEDACTIONS; then
			createactionchain $target
			USEDACTIONS="$USEDACTIONS $target"
		    fi
		else
		    fatal_error "Invalid target ($target) in default macro $macro"
		fi
		;;
	esac

	if [ $(list_count ${port}${cport}) -gt 1 ]; then
	    multioption="-m multiport"
	fi

	if [ -n "$client" ]; then
	    case $client in
		-|SOURCE)
		    client=
		    ;;
	    esac
	fi

	if [ -n "$server" ]; then
	    case $server in
		-|DEST)
		    server=
		    ;;
		*)
		    ;;
	    esac
	fi

	process_userspec

	process_ratelimit

	add_a_rule
	progress_message "Rule \"$target $protocol $port $cport $ratelimit $userspec\" $DONE"
	
    done < $TMP_DIR/macro.$macro

    progress_message "..End Macro"

}
    
#
# Process a record from the tos file
#
# The caller has loaded the column contents from the record into the following
# variables:
#
#    src dst protocol sport dport tos
#
# and has loaded a space-separated list of their values in "rule". The caller
# has also set the variable 'chain' to contain the name of the mangle table
# chain where forward rules are to be in placed in.
#
process_tos_rule() {
    #
    # Parse the contents of the 'src' variable
    #
    if [ "$src" = "${src%:*}" ]; then
	srczone="$src"
	src=
    else
	srczone="${src%:*}"
	src="${src#*:}"
    fi

    source=
    #
    # Validate the source zone
    #
    if validate_zone $srczone; then
	source=$srczone
    elif [ "$srczone" = "all" ]; then
	source="all"
    else
	error_message "WARNING: Undefined Source Zone - rule \"$rule\" ignored"
	return
    fi

    [ -n "$src" ] && case "$src" in
	*.*.*|+*|!+*)
	    #
	    # IP Address or networks
	    #
	    src="$(source_ip_range $src)"
	    ;;
	~*|!~*)
	    src=$(mac_match $src)
	    ;;
	*)
	    #
	    # Assume that this is a device name
	    #
	    if ! verify_interface $src ; then
		error_message "WARNING: Unknown Interface in rule \"$rule\" ignored"
		return
	    fi

	    src="$(match_source_dev $src)"
	    ;;
    esac

    #
    # Parse the contents of the 'dst' variable
    #
    if [ "$dst" = "${dst%:*}" ]; then
	dstzone="$dst"
	dst=
    else
	dstzone="${dst%:*}"
	dst="${dst#*:}"
    fi

    dest=
    #
    # Validate the destination zone
    #
    if validate_zone $dstzone; then
	dest=$dstzone
    elif [ "$dstzone" = "all" ]; then
	dest="all"
    else
	error_message \
	 "WARNING: Undefined Destination Zone - rule \"$rule\" ignored"
	return
    fi

    [ -n "$dst" ] && case "$dst" in
	*.*.*|+*|!+*)
	    #
	    # IP Address or networks
	    #
	    ;;
	*)
	    #
	    # Assume that this is a device name
	    #
	    error_message \
	     "WARNING: Invalid Destination - rule \"$rule\" ignored"
	    return
	    ;;
    esac

    #
    # Setup PROTOCOL and PORT variables
    #
    sports=""
    dports=""

    case $protocol in
	tcp|udp|TCP|UDP|6|17)
	    [ -n "$sport" ] && [ "x${sport}" != "x-" ] && \
		sports="--sport $sport"
	    [ -n "$dport" ] && [ "x${dport}" != "x-" ] && \
		dports="--dport $dport"
	    ;;
	icmp|ICMP|0)
	    [ -n "$dport" ] && [ "x${dport}" != "x-" ] && \
		dports="--icmp-type $dport"
	    ;;
	all|ALL)
	    protocol=
	    ;;
	*)
	    ;;
    esac

    protocol="${protocol:+-p $protocol}"

    tos="-j TOS --set-tos $tos"

    case "$dstzone" in
	all|ALL)
	    dst=0.0.0.0/0
	    ;;
	*)
	    if [ -z "$MANGLE_FORWARD" ]; then
		error_message "WARNING: A zone name in the DEST column requires Mangle FORWARD Chain support in your kernel and iptables: rule \"$rule\" ignored"
		return
	    fi

	    [ -z "$dst" ] && eval dst=\$${dstzone}_hosts
	;;
    esac

    for dest in $dst; do
	dest="$(match_dest $dest)"

	case $srczone in
	$FW)
	    run_iptables2 -t mangle -A outtos \
		$protocol $dest $dports $sports $tos
	    ;;
	all|ALL)
	    run_iptables2 -t mangle -A outtos \
		$protocol $dest $dports $sports $tos
	    run_iptables2 -t mangle -A $chain \
		$protocol $dest $dports $sports $tos
	    ;;
	*)
	    if [ -n "$src" ]; then
		run_iptables2 -t mangle -A $chain $src \
		    $protocol $dest $dports $sports $tos
	    else
		eval hosts=\$${srczone}_hosts

		for host in $hosts; do
		    run_iptables2 -t mangle -A $chain $(match_source $host) \
			$protocol $dest $dports $sports $tos
		done
	    fi
	    ;;
	esac
    done

    progress_message "   Rule \"$rule\" $DONE."
    save_progress_message "Rule \\\"$rule\\\"  Added."
}

#
# Process the tos file
#
process_tos() # $1 = name of tos file
{
    local chain=pretos stdchain=PREROUTING

    if [ -n "$MANGLE_FORWARD" ]; then
	chain=fortos
	stdchain=FORWARD
    fi
    
    strip_file tos $1

    if [ -s $TMP_DIR/tos ] ; then
	
	progress_message2 "$DOING $1..."

	createmanglechain $chain
	createmanglechain outtos

	while read src dst protocol sport dport tos; do
	    expandv src dst protocol sport dport tos
	    rule="$(echo $src $dst $protocol $sport $dport $tos)"
	    process_tos_rule
	done < $TMP_DIR/tos

	run_iptables -t mangle -A $stdchain  -j $chain
	run_iptables -t mangle -A OUTPUT     -j outtos
    fi
}

policy_rules() # $1 = chain to add rules to
	       # $2 = policy
	       # $3 = loglevel
               # $4 = Default Action/Macro
{
    local target="$2"
    local default="$4"

    if [ -n "$default" ]; then
	[ "$default" = none ] || run_iptables -A $1 -j $default
    fi


    if [ $# -ge 3 -a "x${3}" != "x-" ]; then
	log_rule $3 $1 $2
    fi

    if [ -n "$target" ]; then
	case $target in
	    REJECT)
		run_iptables -A $1 -j reject
		;;
	    *)
		run_iptables -A $1 -j $target
		;;
	esac
    fi
}

#
# Generate default policy & log level rules for the passed client & server
# zones
#
# This function is only called when the canonical chain for this client/server
# pair is known to exist. If the default policy for this pair specifies the
# same chain then we add the policy (and logging) rule to the canonical chain;
# otherwise add a rule to the canonical chain to jump to the appropriate
# policy chain.
#
default_policy() # $1 = client $2 = server
{
    local chain="${1}2${2}"
    local policy=
    local loglevel=
    local chain1

    jump_to_policy_chain() {
	#
	# Add a jump to from the canonical chain to the policy chain. On return,
	# $chain is set to the name of the policy chain
	#
	run_iptables -A $chain -j $chain1
	chain=$chain1
    }

    report_syn_flood_protection()
    {
	progress_message "      Enabled SYN flood protection"
    }

    apply_default()
    {
	#
	# Generate policy file column values for the policy chain
	#
	eval policy=\$${chain1}_policy
	eval loglevel=\$${chain1}_loglevel
	eval synparams=\$${chain1}_synparams
	eval default=\$${chain1}_default
	#
	# Add the appropriate rules to the canonical chain ($chain) to enforce
	# the specified policy

	if [ "$chain" = "$chain1" ]; then
	    #
	    # The policy chain is the canonical chain; add policy rule to it
	    # The syn flood jump has already been added if required.
	    #
	    policy_rules $chain $policy $loglevel $default
	else
	    #
	    # The policy chain is different from the canonical chain -- approach
	    # depends on the policy
	    #
	    case $policy in
	    ACCEPT|QUEUE)
		if [ -n "$synparams" ]; then
		    #
		    # To avoid double-counting SYN packets, enforce the policy
		    # in this chain.
		    #
		    report_syn_flood_protection
		    policy_rules $chain $policy $loglevel $default
		else
		    #
		    # No problem with double-counting so just jump to the
		    # policy chain.
		    #
		    jump_to_policy_chain
		fi
		;;
	    CONTINUE)
		#
		# Silly to jump to the policy chain -- add any logging
		# rules and enable SYN flood protection if requested
		#
		[ -n "$synparams" ] && \
		    report_syn_flood_protection
		policy_rules $chain $policy $loglevel $default
		;;
	    *)
		#
		# DROP or REJECT policy -- enforce in the policy chain and
		# enable SYN flood protection if requested.
		#
		[ -n "$synparams" ] && \
		    report_syn_flood_protection
		jump_to_policy_chain
		;;
	    esac
	fi

	progress_message "   Policy $policy for $1 to $2 using chain $chain"
    }

    eval chain1=\$${1}2${2}_policychain

    if [ -n "$chain1" ]; then
	apply_default $1 $2
    else
	fatal_error "No default policy for zone $1 to zone $2"
    fi
}

#
# Complete a standard chain
#
#	- run any supplied user exit
#	- search the policy file for an applicable policy and add rules as
#	  appropriate
#	- If no applicable policy is found, add rules for an assummed
#	  policy of DROP INFO
#
complete_standard_chain() # $1 = chain, $2 = source zone, $3 = destination zone
{
    local policy=
    local loglevel=
    local policychain=
    local default=

    run_user_exit $1

    eval policychain=\$${2}2${3}_policychain

    if [ -n "$policychain" ]; then
	eval policy=\$${policychain}_policy
	eval loglevel=\$${policychain}_loglevel
	eval default=\$${policychain}_default
	eval

	policy_rules $1 $policy $loglevel $default
    else
	policy_rules $1 DROP info $DROP_DEFAULT
    fi
}

#
# Find the appropriate chain to pass packets from a source zone to a
# destination zone
#
# If the canonical chain for this zone pair exists, echo it's name; otherwise
# locate and echo the name of the appropriate policy chain
#
rules_chain() # $1 = source zone, $2 = destination zone
{
    local chain=${1}2${2} local policy

    havechain $chain && { echo $chain; return; }

    [ "$1" = "$2" ] && { echo ACCEPT; return; }

    eval chain=\$${chain}_policychain

    eval policy=\$${chain}_policy

    if [ "$policy" != CONTINUE ] ; then
	[ -n "$chain" ] && { echo $chain; return; }
	fatal_error "No policy defined for zone $1 to zone $2"
    fi
}

#
# Add a record to the blacklst chain
#
#   $source      = address match
#   $proto       = protocol selector
#   $dport       = destination port selector
#
add_blacklist_rule() {
    if [ -n "$BLACKLIST_LOGLEVEL" ]; then
	log_rule $BLACKLIST_LOGLEVEL blacklst $BLACKLIST_DISPOSITION  $(fix_bang $source $proto $dport)
    fi

    run_iptables2 -A blacklst $source $proto $dport -j $disposition
}

#
# Process a record from the blacklist file
#
#   $networks	 = address/networks
#   $protocol    = Protocol Number/Name
#   $port        = Port Number/Name
#
process_blacklist_rec() {
    local source
    local addr
    local proto
    local dport
    local temp
    local setname

    for addr in $(separate_list $networks); do
	case $addr in
	    -)
		source=
		;;
	    ~*|!~*)
		addr=$(echo $addr | sed 's/~//;s/-/:/g')
		source="--match mac --mac-source $addr"
		;;
	    *)
		source="$(source_ip_range $addr)"
		;;
	esac

	if [ -n "$protocol" ]; then
	    proto=" -p $protocol "

	    case $protocol in
		tcp|TCP|6|udp|UDP|17)
		    if [ -n "$ports" ]; then
			if [ -n "$MULTIPORT" -a \
			    "$ports" != "${ports%,*}" -a \
			    "$ports" = "${ports%:*}" -a \
			    $(list_count $ports) -le 15 ]
			then
			    dport="-m multiport --dports $ports"
			    add_blacklist_rule
			else
			    for dport in $(separate_list $ports); do
				dport="--dport $dport"
				add_blacklist_rule
			    done
			fi
		    else
			add_blacklist_rule
		    fi
		    ;;
		icmp|ICMP|0)
		    if [ -n "$ports" ]; then
			for dport in $(separate_list $ports); do
			    dport="--icmp-type $dport"
			    add_blacklist_rule
			done
		    else
			add_blacklist_rule
		    fi
		    ;;
		*)
		    add_blacklist_rule
		    ;;
	    esac
	else
	    add_blacklist_rule
	fi

	if [ -n "$ports" ]; then
	    addr="$addr $protocol $ports"
	elif [ -n "$protocol" ]; then
	    addr="$addr $protocol"
	fi

	progress_message_and_save "   $addr added to Black List"
    done
}

#
# Setup the Black List
#
setup_blacklist() {
    local hosts="$(find_hosts_by_option blacklist)"
    local f=$(find_file blacklist)
    local disposition=$BLACKLIST_DISPOSITION
    local ipsec policy

    if [ -n "$hosts" -a -f $f ]; then
	progress_message2 "$DOING Blacklisting..."

	strip_file blacklist $f

	createchain blacklst no

	[ -n "$BLACKLISTNEWONLY" ] && state="-m state --state NEW,INVALID" || state=

	for host in $hosts; do
	    ipsec=${host%^*}
	    host=${host#*^}
	    [ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
	    interface=${host%%:*}
	    network=${host#*:}

	    for chain in $(first_chains $interface); do
		run_iptables -A $chain $state $(match_source_hosts $network) $policy -j blacklst
	    done

	    [ $network = 0/0.0.0.0 ] && network= || network=":$network"

	    progress_message_and_save "   Blacklisting enabled on ${interface}${network}"
	done

	[ "$disposition" = REJECT ] && disposition=reject

	if [ -z "$DELAYBLACKLISTLOAD" ]; then
	    while read networks protocol ports; do
		expandv networks protocol ports
		process_blacklist_rec
	    done < $TMP_DIR/blacklist
	fi
    fi
}

#
# Refresh the Black List
#
refresh_blacklist() {
    local f=$(find_file blacklist)
    local disposition=$BLACKLIST_DISPOSITION
    local indent="$INDENT"

    progress_message2 "$DOING Black List..."
    save_progress_message "Loading Black List..."

    strip_file blacklist $f

    [ "$disposition" = REJECT ] && disposition=reject

    save_command "if chain_exists blacklst; then"
    INDENT="$INDENT    "

    run_iptables -F blacklst

    while read networks protocol ports; do
	expandv networks protocol ports
	process_blacklist_rec
    done < $TMP_DIR/blacklist
    
    INDENT="$indent"
    save_command "fi"
}

#
# Verify the Black List
#
validate_blacklist() {
    local f=$(find_file blacklist)
    local disposition=$BLACKLIST_DISPOSITION

    progress_message2 "Checking Black List..."

    strip_file blacklist $f

    [ "$disposition" = REJECT ] && disposition=reject

    while read networks protocol ports; do
	expandv networks protocol ports
	process_blacklist_rec
    done < $TMP_DIR/blacklist
}

#
# Construct zone-independent rules
#
add_common_rules() {
    local savelogparms="$LOGPARMS"
    local broadcasts="$(find_broadcasts) 255.255.255.255 224.0.0.0/4"

    drop_broadcasts() {
	for interface in $(find_bcastdetect_interfaces); do
	    indent >&3 << __EOF__

ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u | while read address; do
    run_iptables -A reject -d \$address -j DROP
done

__EOF__
	done

	for address in $broadcasts ; do
	    run_iptables -A reject -d $address -j DROP
	done
    }
    #
    # Populate the smurf chain
    #
    save_progress_message "Setting up SMURF control..."

    for interface in $(find_bcastdetect_interfaces); do
	indent >&3 << __EOF__

ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u | while read address; do
__EOF__
	[ -n "$SMURF_LOG_LEVEL" ] && \
	    indent >&3 << __EOF__
    log_rule $SMURF_LOG_LEVEL smurfs DROP -s \$address
__EOF__
	indent >&3 << __EOF__
    run_iptables -A smurfs -s \$address -j DROP
done

__EOF__
    done

    for address in $broadcasts ; do
	[ -n "$SMURF_LOG_LEVEL" ] && log_rule $SMURF_LOG_LEVEL smurfs DROP -s $address
	run_iptables -A smurfs $(source_ip_range $address) -j DROP
	run_iptables -A reject -s $address -j DROP
    done
    #
    # Reject Rules -- Don't respond to broadcasts with an ICMP
    #
    if [ -n "$USEPKTTYPE" ]; then
	run_iptables -A reject -m pkttype --pkt-type broadcast -j DROP
	run_iptables -A reject -m pkttype --pkt-type multicast -j DROP
    else
	drop_broadcasts
    fi
    #
    # Don't feed the smurfs
    #
    for address in $broadcasts ; do
	run_iptables -A reject -s $address -j DROP
    done

    run_iptables -A reject -p tcp  -j REJECT --reject-with tcp-reset
    run_iptables -A reject -p udp  -j REJECT
    #
    # Not all versions of iptables support these so don't complain if they don't work
    #
    if [ -n "$ENHANCED_REJECT" ]; then
	run_iptables -A reject -p icmp -j REJECT --reject-with icmp-host-unreachable
	run_iptables -A reject -j REJECT --reject-with icmp-host-prohibited
    else
	run_iptables -A reject -j REJECT
    fi

    #
    # Create default action chains
    #
    for action in $USEDACTIONS; do
	createactionchain $action
    done

    append_file initdone

    #
    # Process Black List
    #
    save_progress_message "Setting up Black List..."

    setup_blacklist

    #
    # SMURFS
    #
    hosts=$(find_hosts_by_option nosmurfs)

    if [ -n "$hosts" ]; then

	progress_message2 "Adding Anti-smurf Rules"

	save_progress_message "Adding Anti-smurf Jumps..."

	for host in $hosts; do
	    ipsec=${host%^*}
	    host=${host#*^}
	    [ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
	    interface=${host%%:*}
	    network=${host#*:}

	    for chain in $(first_chains $interface); do
		run_iptables -A $chain  -m state --state NEW,INVALID $(match_source_hosts $network) $policy -j smurfs
	    done
	done
    fi
    #
    # DHCP
    #
    interfaces=$(find_interfaces_by_option dhcp)

    if [ -n "$interfaces" ]; then

	progress_message2 "Adding rules for DHCP"

	save_progress_message "Setting up rules for DHCP..."

	for interface in $interfaces; do
	    if [ -n "$BRIDGING" ]; then
		is_bridge=$( brctl show $interface 2> /dev/null | grep ^$interface[[:space:]] )
		[ -n "$is_bridge" ] && \
		    do_iptables -A $(forward_chain $interface) -p udp -o $interface --dport 67:68 -j ACCEPT
            fi
	    run_iptables -A $(input_chain $interface) -p udp --dport 67:68 -j ACCEPT
	    run_iptables -A OUTPUT -o $interface      -p udp --dport 67:68 -j ACCEPT
	done
    fi
    #
    # RFC 1918
    #
    hosts="$(find_hosts_by_option norfc1918)"

    if [ -n "$hosts" ]; then
	progress_message2 "Enabling RFC1918 Filtering"

	save_progress_message "Setting up RFC1918 Filtering..."

	strip_file rfc1918

	createchain norfc1918 no

	createchain rfc1918 no

	log_rule $RFC1918_LOG_LEVEL rfc1918 DROP

	run_iptables -A rfc1918 -j DROP

	chain=norfc1918

	if [ -n "$RFC1918_STRICT" ]; then
	    #
	    # We'll generate two chains - one for source and one for destination
	    #
	    chain=rfc1918d
	    createchain $chain no
	elif [ -n "$MANGLE_ENABLED" -a -z "$CONNTRACK_MATCH" ]; then
	    #
	    # Mangling is enabled but conntrack match isn't available --
	    # create a chain in the mangle table to filter RFC1918 destination
            # addresses. This must be done in the mangle table before we apply
	    # any DNAT rules in the nat table
	    #
	    # Also add a chain to log and drop any RFC1918 packets that we find
	    #
	    createmanglechain man1918
	    createmanglechain rfc1918
	    log_rule $RFC1918_LOG_LEVEL rfc1918 DROP -t mangle
	    run_iptables -t mangle -A rfc1918 -j DROP
	fi

	while read networks target; do
	    case $target in
		logdrop)
		    target=rfc1918
		    s_target=rfc1918
		    ;;
		DROP)
		    s_target=DROP
		    ;;
		RETURN)
		    [ -n "$RFC1918_STRICT" ] && s_target=rfc1918d || s_target=RETURN
		    ;;
		*)
		    fatal_error "Invalid target ($target) for $networks"
		    ;;
	    esac

	    for network in $(separate_list $networks); do
		run_iptables2 -A norfc1918 $(source_ip_range $network) -j $s_target

		if [ -n "$CONNTRACK_MATCH" ]; then
		    #
		    # We have connection tracking match -- match on the original destination
		    #
		    run_iptables2 -A $chain -m conntrack --ctorigdst $network -j $target
		elif [ -n "$MANGLE_ENABLED" ]; then
		    #
		    # No connection tracking match but we have mangling -- add a rule to
		    # the mangle table
		    #
		    run_iptables2 -t mangle -A man1918 $(dest_ip_range $network) -j $target
		fi
	    done
	done < $TMP_DIR/rfc1918

	[ -n "$RFC1918_STRICT" ] && run_iptables -A norfc1918 -j rfc1918d

	for host in $hosts; do
	    ipsec=${host%^*}
	    host=${host#*^}
	    [ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
	    interface=${host%%:*}
	    networks=${host#*:}

	    for chain in $(first_chains $interface); do
		run_iptables -A $chain -m state --state NEW $(match_source_hosts $networks) $policy -j norfc1918
	    done

	    [ -n "$MANGLE_ENABLED" -a -z "$CONNTRACK_MATCH" ] && \
		run_iptables -t mangle -A PREROUTING -m state --state NEW -i $interface $(match_source_hosts $networks) -j man1918
	done
    fi

    hosts=$(find_hosts_by_option tcpflags)

    if [ -n "$hosts" ]; then
	progress_message2 "$DOING TCP Flags checking..."

	save_progress_message "Setting up TCP Flags checking..."

	createchain tcpflags no

	if [ -n "$TCP_FLAGS_LOG_LEVEL" ]; then
	    createchain logflags no

	    savelogparms="$LOGPARMS"

	    [ "$TCP_FLAGS_LOG_LEVEL" = ULOG ] || LOGPARMS="$LOGPARMS --log-ip-options"

	    log_rule $TCP_FLAGS_LOG_LEVEL logflags $TCP_FLAGS_DISPOSITION

	    LOGPARMS="$savelogparms"

	    case $TCP_FLAGS_DISPOSITION in
		REJECT)
		    run_iptables -A logflags -j REJECT --reject-with tcp-reset
		    ;;
		*)
		    run_iptables -A logflags -j $TCP_FLAGS_DISPOSITION
		    ;;
	    esac

	    disposition="-j logflags"
	else
	    disposition="-j $TCP_FLAGS_DISPOSITION"
	fi

	run_iptables -A tcpflags -p tcp --tcp-flags ALL FIN,URG,PSH $disposition
	run_iptables -A tcpflags -p tcp --tcp-flags ALL NONE        $disposition
	run_iptables -A tcpflags -p tcp --tcp-flags SYN,RST SYN,RST $disposition
	run_iptables -A tcpflags -p tcp --tcp-flags SYN,FIN SYN,FIN $disposition
	#
	# There are a lot of probes to ports 80, 3128 and 8080 that use a source
	# port of 0. This catches them even if they are directed at an IP that
	# hosts a web server.
	#
	run_iptables -A tcpflags -p tcp --syn --sport 0             $disposition

	for host in $hosts; do
	    ipsec=${host%^*}
	    host=${host#*^}
	    [ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
	    interface=${host%%:*}
	    network=${host#*:}

	    for chain in $(first_chains $interface); do
		run_iptables -A $chain -p tcp $(match_source_hosts $network) $policy -j tcpflags
	    done
	done
    fi
    #
    # ARP Filtering
    #
    save_progress_message "Setting up ARP filtering..."

    indent >&3 << __EOF__
for f in /proc/sys/net/ipv4/conf/*; do
    [ -f \$f/arp_filter ] && echo 0 > \$f/arp_filter
    [ -f \$f/arp_ignore ] && echo 0 > \$f/arp_ignore
done

__EOF__

    interfaces=$(find_interfaces_by_option arp_filter)
    interfaces1=$(find_interfaces_by_option1 arp_ignore)

    if [ -n "${interfaces}${interfaces1}" ]; then
	progress_message2 "$DOING ARP Filtering..."

	for interface in $interfaces; do
	    file=/proc/sys/net/ipv4/conf/$interface/arp_filter
	    indent >&3 << __EOF__
if [ -f $file ]; then
    echo 1 > $file
else
    error_message "WARNING: Cannot set ARP filtering on $interface"
fi
__EOF__
	done

	for interface in $interfaces1; do
	    file=/proc/sys/net/ipv4/conf/$interface/arp_ignore
	    eval value="\$$(chain_base $interface)_arp_ignore"
	    indent >&3 << __EOF__
if [ -f $file ]; then
    echo $value > $file
else
    error_message "WARNING: Cannot set ARP filtering on $interface"
fi
__EOF__
	done
    fi
    #
    # Route Filtering
    #
    interfaces="$(find_interfaces_by_option routefilter)"

    if [ -n "$interfaces" -o -n "$ROUTE_FILTER" ]; then
	progress_message2 "$DOING Kernel Route Filtering..."

	save_progress_message "Setting up Route Filtering..."

	indent >&3 << __EOF__

for f in /proc/sys/net/ipv4/conf/*; do
    [ -f \$f/log_martians ] && echo 0 > \$f/rp_filter
done

__EOF__
	for interface in $interfaces; do
	    file=/proc/sys/net/ipv4/conf/$interface/rp_filter

	    indent >&3 << __EOF__
if [ -f $file ]; then
    echo 1 > $file
else
    error_message "WARNING: Cannot set route filtering on $interface"
fi
__EOF__
	done

	save_command "echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter"

	if [ -n "$ROUTE_FILTER" ]; then
	    save_command "echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter"
	    save_command "echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter"
	fi

	save_command "[ -n \"\$NOROUTES\" ] || ip route flush cache"
    fi

    #
    # Martian Logging
    #
    interfaces="$(find_interfaces_by_option logmartians)"

    if [ -n "$interfaces" -o -n "$LOG_MARTIANS" ]; then
	progress_message2 "$DOING Martian Logging..."

	save_progress_message "Setting up Martian Logging..."

	indent >&3 << __EOF__

for f in /proc/sys/net/ipv4/conf/*; do
    [ -f \$f/log_martians ] && echo 0 > \$f/log_martians
done

__EOF__
	for interface in $interfaces; do
	    file=/proc/sys/net/ipv4/conf/$interface/log_martians

	    indent >&3 << __EOF__
if [ -f $file ]; then
    echo 1 > $file
else
    error_message "WARNING: Cannot set Martian logging on $interface"
fi

__EOF__
	    done

	if [ -n "$LOG_MARTIANS" ]; then
	    save_command "echo 1 > /proc/sys/net/ipv4/conf/default/log_martians"
	    save_command "echo 1 > /proc/sys/net/ipv4/conf/all/log_martians"
	fi

    fi

    #
    # Source Routing
    #
    save_progress_message "Setting up Accept Source Routing..."

    indent >&3 << __EOF__
for f in /proc/sys/net/ipv4/conf/*; do
    [ -f \$f/accept_source_route ] && echo 0 > \$f/accept_source_route
done

__EOF__

    interfaces=$(find_interfaces_by_option sourceroute)

    if [ -n "$interfaces" ]; then
	progress_message2 "$DOING Accept Source Routing..."

	save_progress_message "Setting up Source Routing..."

	for interface in $interfaces; do
	    file=/proc/sys/net/ipv4/conf/$interface/accept_source_route

	    indent >&3 << __EOF__
if [ -f $file ]; then
    echo 1 > $file
else
    error_message "WARNING: Cannot set Accept Source Routing on $interface"
fi
__EOF__
	done
    fi

    if [ -n "$DYNAMIC_ZONES" ]; then
	progress_message "$DOING Dynamic Zone Chains..."

	for interface in $ALL_INTERFACES; do
	    for chain in $(dynamic_chains $interface); do
		createchain $chain no
	    done

	    chain=$(dynamic_in $interface)
	    createnatchain $chain

	    run_iptables -A $(input_chain $interface)   -j $chain
	    run_iptables -A $(forward_chain $interface) -j $(dynamic_fwd $interface)
	    run_iptables -A OUTPUT -o $interface        -j $(dynamic_out $interface)
	done
    fi
    #
    # UPnP
    #
    interfaces=$(find_interfaces_by_option upnp)

    if [ -n "$interfaces" ]; then
	progress_message2 "$DOING UPnP..."

	save_progress_message "Setting up UPnP..."

	createnatchain UPnP

	for interface in $interfaces; do
	    run_iptables -t nat -A PREROUTING -i $interface -j UPnP
	done
    fi

    setup_forwarding
}

#
# Scan the policy file defining the necessary chains
# Add the appropriate policy rule(s) to the end of each canonical chain
#
apply_policy_rules() {
    #
    # Create policy chains
    #
    for chain in $ALL_POLICY_CHAINS; do
	eval policy=\$${chain}_policy
	eval loglevel=\$${chain}_loglevel
	eval optional=\$${chain}_is_optional
	eval default=\$${chain}_default

	if [ "$policy" != NONE ]; then
	    if ! havechain $chain && [ -z "$optional" -a "$policy" != CONTINUE ]; then
		#
		# The chain doesn't exist. Create the chain and add policy
		# rules
		#
		createchain $chain yes
		#
		# If either client or server is 'all' then this MUST be
		# a policy chain and we must apply the appropriate policy rules
		#
		# Otherwise, this is a canonical chain which will be handled in
		# the for loop below
		#
		case $chain in
		    all2*|*2all)
			run_user_exit $chain
			policy_rules $chain $policy $loglevel $default
			;;
		esac
	    fi
	fi
    done

    #
    # Add policy rules to canonical chains
    #
    for zone in $FW $ZONES; do
	for zone1 in $FW $ZONES; do
	    chain=${zone}2${zone1}
	    if havechain $chain; then
		run_user_exit $chain
		default_policy $zone $zone1
	    fi
	done
    done
}

#
# Activate the rules
#
activate_rules()
{
    local PREROUTING_rule=1
    local POSTROUTING_rule=1
    #
    # Jump to a NAT chain from one of the builtin nat chains
    #
    addnatjump() # $1 = BUILTIN chain, $2 = user chain, $3 - * other arguments
    {
	local sourcechain=$1 destchain=$2
	shift
	shift

	if havenatchain $destchain ; then
	    run_iptables2 -t nat -A $sourcechain $@ -j $destchain
	elif [ -z "$KLUDGEFREE" ]; then
	    [ -n "$BRIDGING" -a -f $TMP_DIR/physdev ]      && -rm -f $TMP_DIR/physdev
	    [ -n "$IPRANGE_MATCH" -a -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
	fi
    }

    #
    # Jump to a RULES chain from one of the builtin nat chains. These jumps are
    # are inserted before jumps to one-to-one NAT chains.
    #
    addrulejump() # $1 = BUILTIN chain, $2 = user chain, $3 - * other arguments
    {
	local sourcechain=$1 destchain=$2
	shift
	shift

	if havenatchain $destchain; then
	    eval run_iptables2 -t nat -I $sourcechain \
		\$${sourcechain}_rule $@ -j $destchain
	    eval ${sourcechain}_rule=\$\(\(\$${sourcechain}_rule + 1\)\)
	elif [ -z "$KLUDGEFREE" ]; then
	    [ -n "$BRIDGING" -a -f $TMP_DIR/physdev ]      && rm -f $TMP_DIR/physdev
	    [ -n "$IPRANGE_MATCH" -a -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange

	fi
    }

    #
    # Create a dynamic chain for a zone and jump to it from a second chain
    #
    create_zone_dyn_chain() # $1 = zone, $2 = second chain
    {
	createchain ${1}_dyn No
	run_iptables -A $2 -j ${1}_dyn
    }
    #
    # Add jumps to early SNAT chains
    #
    for interface in $ALL_INTERFACES; do
	addnatjump POSTROUTING $(snat_chain $interface) -o $interface
    done
    #
    # Add jumps for dynamic nat chains
    #
    [ -n "$DYNAMIC_ZONES" ] && for interface in $ALL_INTERFACES ; do
	addrulejump PREROUTING $(dynamic_in $interface) -i $interface
    done
    #
    # Add jumps from the builtin chains to the nat chains
    #
    addnatjump PREROUTING  nat_in
    addnatjump POSTROUTING nat_out

    for interface in $ALL_INTERFACES; do
	addnatjump PREROUTING  $(input_chain $interface)  -i $interface
	addnatjump POSTROUTING $(output_chain $interface) -o $interface
    done

    > $STATEDIR/chains
    echo "$FW firewall" > $STATEDIR/zones
    #
    # Create forwarding chains for complex zones and generate jumps for IPSEC source hosts to that chain.
    #
    for zone in $ZONES; do
	if eval test -n \"\$${zone}_is_complex\" ; then
	    frwd_chain=${zone}_frwd
	    createchain $frwd_chain No

	    if [ -n "$POLICY_MATCH" ]; then
		#
		# Because policy match only matches an 'in' or an 'out' policy (but not both), we have to place the
	        # '--pol ipsec --dir in' rules at the front of the interface forwarding chains. Otherwise, decrypted packets
                # can match '--pol none --dir out' rules and send the packets down the wrong rules chain.
		#
	        eval is_ipsec=\$${zone}_is_ipsec

		if [ -n "$is_ipsec" ]; then
		    eval source_hosts=\$${zone}_hosts
		    [ -n "$DYNAMIC_ZONES" ] && create_zone_dyn_chain $zone $frwd_chain
		else
		    eval source_hosts=\$${zone}_ipsec_hosts
		    [ -n "$DYNAMIC_ZONES" -a -n "$source_hosts" ] && create_zone_dyn_chain $zone $frwd_chain
		fi

		for host in $source_hosts; do
		    interface=${host%%:*}
		    networks=${host#*:}

		    run_iptables2 -A $(forward_chain $interface) $(match_source_hosts $networks) $(match_ipsec_in $zone $host) -j $frwd_chain
		done
	    fi
	fi
    done

    for zone in $ZONES; do
	eval source_hosts=\$${zone}_hosts

	chain1=$(rules_chain $FW $zone)
	chain2=$(rules_chain $zone $FW)

	eval complex=\$${zone}_is_complex
	eval type=\$${zone}_type

	[ -n "$complex" ] && frwd_chain=${zone}_frwd

	echo $zone $type $source_hosts >> $STATEDIR/zones

	if [ -n "$DYNAMIC_ZONES" ]; then
	    echo "$FW $zone $chain1" >> $STATEDIR/chains
	    echo "$zone $FW $chain2" >> $STATEDIR/chains
	fi

	need_broadcast=

	for host in $source_hosts; do
	    interface=${host%%:*}
	    networks=${host#*:}

	    [ -n "$chain1" ] && run_iptables2 -A OUTPUT -o $interface $(match_dest_hosts $networks) $(match_ipsec_out $zone $host) -j $chain1

	    #
	    # Add jumps from the builtin chain for DNAT rules
	    #
	    addrulejump PREROUTING  $(dnat_chain $zone) -i $interface $(match_source_hosts $networks) $(match_ipsec_in $zone $host)

	    [ -n "$chain2" ] && run_iptables2 -A $(input_chain $interface) $(match_source_hosts $networks) $(match_ipsec_in $zone $host) -j $chain2

	    if [ -n "$complex" ] && ! is_ipsec_host $zone $host ; then
                run_iptables2 -A $(forward_chain $interface) $(match_source_hosts $networks) $(match_ipsec_in $zone $host) -j $frwd_chain
	    fi

	    case $networks in
		*.*.*.*|+*)
		    if [ "$networks" != 0.0.0.0/0 ]; then
			if ! list_search $interface $need_broadcast ; then
			    interface_has_option $interface detectnets && need_broadcast="$need_broadcast $interface"
			fi
		    fi
		    ;;
	    esac
	done

	if [ -n "$chain1" ]; then
	    for interface in $need_broadcast ; do
		run_iptables -A OUTPUT -o $interface -d 255.255.255.255 -j $chain1
		run_iptables -A OUTPUT -o $interface -d 224.0.0.0/4     -j $chain1
	    done
	fi

	for zone1 in $ZONES; do

	    eval policy=\$${zone}2${zone1}_policy

	    [ "$policy" = NONE ] && continue

	    eval dest_hosts=\$${zone1}_hosts

	    chain="$(rules_chain $zone $zone1)"

	    [ -z "$chain" ] && continue # CONTINUE policy and there is no canonical chain.

	    [ -n "$DYNAMIC_ZONES" ] && echo "$zone $zone1 $chain" >> $STATEDIR/chains

	    if [ $zone = $zone1 ]; then
		#
		# Try not to generate superfluous intra-zone rules
		#
		eval routeback=\"\$${zone}_routeback\"
		eval interfaces=\"\$${zone}_interfaces\"
		eval ports="\$${zone}_ports"

		num_ifaces=$(list_count1 $interfaces)
		#
		# If the zone has a single interface then what matters is how many ports it has
		#
		[ $num_ifaces -eq 1 -a -n "$ports" ] && num_ifaces=$(list_count1 $ports)
		#
		# If we don't need to route back and if we have only one interface or one port to
		# the zone then assume that hosts in the zone can communicate directly.
		#
		if [ $num_ifaces -lt 2 -a -z "$routeback" ] ; then
		    continue
		fi
	    else
		routeback=
		num_ifaces=0
	    fi

	    if [ -n "$complex" ]; then
		for host1 in $dest_hosts; do
		    interface1=${host1%%:*}
		    networks1=${host1#*:}
		    #
		    # Only generate an intrazone rule if the zone has more than one interface (port) or if
		    # routeback was specified for this host group
		    #
		    if [ $zone != $zone1 -o $num_ifaces -gt 1 ] || list_search $host1 $routeback ; then
			run_iptables2 -A $frwd_chain -o $interface1 $(match_dest_hosts $networks1) $(match_ipsec_out $zone1 $host1) -j $chain
		    fi
		done
	    else
		for host in $source_hosts; do
		    interface=${host%%:*}
		    networks=${host#*:}

		    chain3=$(forward_chain $interface)

		    for host1 in $dest_hosts; do
			interface1=${host1%%:*}
			networks1=${host1#*:}

			if [ "$host" != "$host1" ] || list_search $host $routeback; then
			    run_iptables2 -A $chain3 $(match_source_hosts $networks) -o $interface1 $(match_dest_hosts $networks1) $(match_ipsec_out $zone1 $host1) -j $chain
			fi
		    done
		done
	    fi
	done
    done

    for interface in $ALL_INTERFACES ; do
	run_iptables -A FORWARD -i $interface -j $(forward_chain $interface)
	run_iptables -A INPUT   -i $interface -j $(input_chain $interface)
	addnatjump POSTROUTING $(masq_chain $interface) -o $interface
    done

    chain=${FW}2${FW}

    if havechain $chain; then
	#
	# There is a fw->fw chain. Send loopback output through that chain
	#
	run_iptables -A OUTPUT -o lo -j $chain
	#
	# And delete the unconditional ACCEPT rule
	#
	run_iptables -D OUTPUT -o lo -j ACCEPT
    fi

    complete_standard_chain INPUT all $FW
    complete_standard_chain OUTPUT $FW all
    complete_standard_chain FORWARD all all
    #
    # Remove rules added to keep the firewall alive during [re]start"
    #
    disable_critical_hosts

    for chain in INPUT OUTPUT FORWARD; do
	[ -n "$FASTACCEPT" ] || run_iptables -D $chain -m state --state ESTABLISHED,RELATED -j ACCEPT
	run_iptables -D $chain -p udp --dport 53 -j ACCEPT
    done

    process_routestopped -D

    if [ -n "$LOGALLNEW" ]; then
	for table in mangle nat filter; do
	    case $table in
		mangle)
		    chains="PREROUTING INPUT FORWARD POSTROUTING"
		    ;;
		nat)
		    chains="PREROUTING POSTROUTING OUTPUT"
		    ;;
		*)
		    chains="INPUT FORWARD OUTPUT"
		    ;;
	    esac

	    for chain in $chains; do
		log_rule_limit $LOGALLNEW $chain $table $chain "" "" -I -m state --state NEW -t $table
	    done
	done
    fi
}

#
# Compile a script that will stop the firewall
#
# This function is called by compile_firewall() so all of the overloaded functions
# from that script are available here
#
compile_stop_firewall() {
    local IPTABLES_COMMAND="\$IPTABLES"
    local INDENT="    "

    cat >&3 << __EOF__

#
# Stop/restore the firewall after an error or because of a "stop" or "clear" command
#
stop_firewall() {

    deletechain() {
	qt \$IPTABLES -L \$1 -n && qt \$IPTABLES -F \$1 && qt \$IPTABLES -X \$1
    }

    deleteallchains() {
	\$IPTABLES -F
	\$IPTABLES -X
    }

    setcontinue() {
	\$IPTABLES -A \$1 -m state --state ESTABLISHED,RELATED -j ACCEPT
    }

    delete_nat() {
	\$IPTABLES -t nat -F
	\$IPTABLES -t nat -X

	if [ -f \${VARDIR}/nat ]; then
	    while read external interface; do
		del_ip_addr \$external \$interface
	    done < \${VARDIR}/nat

	    rm -f \${VARDIR}/nat
	fi
    }

    case \$COMMAND in
	stop|clear)
	    ;;
	*)
	    set +x

	    RESTOREPATH=\${VARDIR}/\$RESTOREFILE

	    if [ -x \$RESTOREPATH ]; then

		if [ -x \${RESTOREPATH}-ipsets ]; then
		    progress_message2 Restoring Ipsets...
		    #
		    # We must purge iptables to be sure that there are no
		    # references to ipsets
		    #
		    for table in mangle nat filter; do
			\$IPTABLES -t \$table -F
			\$IPTABLES -t \$table -X
		    done

		    \${RESTOREPATH}-ipsets
		fi

		echo Restoring \${PRODUCT:=Shorewall}...

		if \$RESTOREPATH restore; then
		    echo "\$PRODUCT restored from \$RESTOREPATH"
		    set_state "Started"
		else
		    set_state "Unknown"
		fi

		kill \$\$
		exit 2
	    fi
	    ;;
    esac

    set_state "Stopping"

    STOPPING="Yes"

    TERMINATOR=

    deletechain shorewall

    determine_capabilities

__EOF__

    append_file stop

    cat >&3 << __EOF__

    if [ -n "\$MANGLE_ENABLED" ]; then
	run_iptables -t mangle -F
	run_iptables -t mangle -X
	for chain in PREROUTING INPUT FORWARD POSTROUTING; do
	    qt \$IPTABLES -t mangle -P \$chain ACCEPT
	done
    fi

    if [ -n "\$RAW_TABLE" ]; then
	run_iptables -t raw -F
	run_iptables -t raw -X
	for chain in PREROUTING OUTPUT; do
	    qt \$IPTABLES -t raw -P \$chain ACCEPT
	done
    fi

    if [ -n "\$NAT_ENABLED" ]; then
	delete_nat
	for chain in PREROUTING POSTROUTING OUTPUT; do
	    qt \$IPTABLES -t nat -P \$chain ACCEPT
	done
    fi

    if [ -f \${VARDIR}/proxyarp ]; then
	while read address interface external haveroute; do
	    qt arp -i \$external -d \$address pub
	    [ -z "\${haveroute}\${NOROUTES}" ] && qt ip route del \$address dev \$interface
	done < \${VARDIR}/proxyarp

	rm -f \${VARDIR}/proxyarp
    fi

    for f in /proc/sys/net/ipv4/conf/*; do
	    [ -f \$f/proxy_arp ] && echo 0 > \$f/proxy_arp
    done

__EOF__
    [ -n "$CLEAR_TC" ] && save_command "delete_tc1"

    [ -n "$DISABLE_IPV6" ] && save_command "disable_ipv6"

    process_criticalhosts

    if [ -n "$CRITICALHOSTS" ]; then
	if [ -z "$ADMINISABSENTMINDED" ]; then
	    cat >&3 << __EOF__

    for chain in INPUT OUTPUT; do
	setpolicy \$chain ACCEPT
    done

    setpolicy FORWARD DROP

    deleteallchains

    for host in $CRITICALHOSTS; do
	interface=\${host%:*}
	networks=\${host#*:}
	\$IPTABLES -A INPUT  -i \$interface \$(source_ip_range \$networks) -j ACCEPT
	\$IPTABLES -A OUTPUT -o \$interface \$(dest_ip_range \$networks) -j ACCEPT
    done

    for chain in INPUT OUTPUT; do
	setpolicy $\chain DROP
    done

__EOF__
	else
	    cat >&3 << __EOF__

    for chain in INPUT OUTPUT; do
	setpolicy \$chain ACCEPT
    done

    setpolicy FORWARD DROP

    deleteallchains

    for host in $CRITICALHOSTS; do
	interface=\${host%:*}
	networks=\${host#*:}
	\$IPTABLES -A INPUT  -i \$interface \$(source_ip_range \$networks) -j ACCEPT
	\$IPTABLES -A OUTPUT -o \$interface \$(dest_ip_range \$networks) -j ACCEPT
    done

    setpolicy INPUT DROP

    for chain in INPUT FORWARD; do
	setcontinue \$chain
    done

__EOF__
	fi
    elif [ -z "$ADMINISABSENTMINDED" ]; then
	cat >&3 << __EOF__

    for chain in INPUT OUTPUT FORWARD; do
	setpolicy \$chain DROP
    done

    deleteallchains

__EOF__
    else
	cat >&3 << __EOF__

    for chain in INPUT FORWARD; do
	setpolicy \$chain DROP
    done

    setpolicy OUTPUT ACCEPT

    deleteallchains

    for chain in INPUT FORWARD; do
	setcontinue \$chain
    done

__EOF__
    fi

    process_routestopped -A

    save_command "\$IPTABLES -A INPUT  -i lo -j ACCEPT"

    [ -z "$ADMINISABSENTMINDED" ] && \
	save_command "\$IPTABLES -A OUTPUT -o lo -j ACCEPT"

    for interface in $(find_interfaces_by_option dhcp); do
	save_command "\$IPTABLES -A INPUT  -p udp -i $interface --dport 67:68 -j ACCEPT"
	[ -z "$ADMINISABSENTMINDED" ] && \
	    save_command "\$IPTABLES -A OUTPUT -p udp -o $interface --dport 67:68 -j ACCEPT"
	#
	# This might be a bridge
	#
	save_command "\$IPTABLES -A FORWARD -p udp -i $interface -o $interface --dport 67:68 -j ACCEPT"
    done

    save_command

    case "$IP_FORWARDING" in
    [Oo][Nn])
	save_command "echo 1 > /proc/sys/net/ipv4/ip_forward"
	save_command "progress_message2 IP Forwarding Enabled"
	;;
    [Oo][Ff][Ff])
	save_command "echo 0 > /proc/sys/net/ipv4/ip_forward"
	save_command "progress_message2 IP Forwarding Disabled!"
	;;
    esac

    append_file stopped

    cat >&3 << __EOF__

    set_state "Stopped"

    logger "Shorewall Stopped"

    case \$COMMAND in
    stop|clear)
	;;
    *)
	#
	# The firewall is being stopped when we were trying to do something
	# else. Remove the lock file and Kill the shell in case we're in a
	# subshell
	#
	kill \$\$
	;;
    esac
}
__EOF__
}

#
# Conditionally add an option to .conf file (FD 3)
#
conditionally_add_option() { # $1 = option name
    local value

    eval value=\"\$$1\"

    if [ -n "$value" ]; then
	    cat >&3 << __EOF__
[ -n "\${$1:=$value}" ]
__EOF__
    fi
}	

conditionally_add_option1() { # $1 = option name
    local value

    eval value=\"\$$1\"

    if [ -n "$value" ]; then
	    cat >&3 << __EOF__
$1="$value"
__EOF__
    fi
}	

#
# Compile a Firewall Script
#
compile_firewall() # $1 = File Name
{
    local IPTABLES_COMMAND=run_iptables
    local INDENT=""
    local checking= outfile=$1 dir=

    setup_mss()
    {
	case $CLAMPMSS in
	    Yes)
		option="--clamp-mss-to-pmtu"
		;;
	    *)
		option="--set-mss $CLAMPMSS"
		;;
	esac

	run_iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS $option
    }

    #
    # So that mktempdir doesn't have to jump through hoops when there isn't a working 'mktemp',
    # we create the compiler's temporary directory in TMP_DIR
    #
    STATEDIR=$TMP_DIR/compiler_state/

    mkdir $STATEDIR || fatal_error "Cannot create temporary directory in $TMP_DIR"

    if [ $COMMAND = compile ]; then
	dir=$(dirname $1)
	[ -d $dir ] || fatal_error "Directory $dir does not exist"
	[ -h $dir ] && fatal_error "$dir is a Symbolic Link"
	[ -d $outfile ] && fatal_error "$outfile is a Directory"
	[ -h $outfile ] && fatal_error "$outfile is a Symbolic Link"
	[ -f $outfile -a ! -x $outfile ] && fatal_error "$outfile exists and is not a restore file"

	DOING=Compiling
	DONE=compiled

	OUTPUT=$(mktempfile $STATEDIR)

	[ -n "$OUTPUT" ] || fatal_error "Cannot create temporary file in /tmp"

	exec 3>>$OUTPUT
    else
	DOING=Checking
	DONE=checked
	checking=Yes
	COMMAND=compile

	exec 3>/dev/null
    fi

    cat >&3 << __EOF__
#
# Compiled firewall script generated by Shorewall $VERSION - $(date)"
#
__EOF__

    if [ -n "$EXPORT" ]; then
	cat >&3 << __EOF__
SHAREDIR=/usr/share/shorewall-lite
CONFDIR=/etc/shorewall-lite
VARDIR=/var/lib/shorewall-lite

__EOF__

	cat ${SHAREDIR}/lib.base >&3
    else
	cat >&3 << __EOF__
SHAREDIR=/usr/share/shorewall
CONFDIR=/etc/shorewall
VARDIR=/var/lib/shorewall

. \${SHAREDIR}/lib.base
__EOF__
    fi

    compile_stop_firewall

    cat >&3 << __EOF__

#
# Set policy of chain \$1 to \$2
#
setpolicy() {
    \$IPTABLES -P \$1 \$2
}
__EOF__

    compile_stop_firewall

    cat >&3 << __EOF__

#
# Remove all Shorewall-added rules
#
clear_firewall() {
    stop_firewall

    setpolicy INPUT ACCEPT
    setpolicy FORWARD ACCEPT
    setpolicy OUTPUT ACCEPT

    run_iptables -F

    echo 1 > /proc/sys/net/ipv4/ip_forward

__EOF__
    if [ -n "$DISABLE_IPV6" ]; then
	cat >&3 << __EOF__
    if qt mywhich ip6tables; then
	ip6tables -P INPUT   ACCEPT 2> /dev/null
	ip6tables -P OUTPUT  ACCEPT 2> /dev/null
	ip6tables -P FORWARD ACCEPT 2> /dev/null
    fi

__EOF__
    fi

    append_file clear

    cat >&3 << __EOF__

    set_state "Cleared"

    logger "Shorewall Cleared"
}

#
# Issue a message and stop/restore the firewall
#
fatal_error()
{
    echo "   ERROR: \$@" >&2
    stop_firewall
    exit 2
}

#
# Issue a message and stop
#
startup_error() # \$* = Error Message
{
    echo "   ERROR: \$@" >&2
    kill \$\$
    exit 2
}

#
# Run iptables and if an error occurs, stop/restore the firewall
#
run_iptables()
{
    if ! \$IPTABLES \$@; then
	error_message "ERROR: Command \"\$IPTABLES \$@\" Failed"
	stop_firewall
	exit 2
    fi
}

#
# Run iptables and if an error occurs, stop/restore the firewall
#
run_ip()
{
    if ! ip \$@; then
	error_message "ERROR: Command \"ip \$@\" Failed"
	stop_firewall
	exit 2
    fi
}

#
# Run tc and if an error occurs, stop/restore the firewall
#
run_tc() {
    if ! tc \$@ ; then
	error_message "ERROR: Command \"tc \$@\" Failed"
	stop_firewall
	exit 2
    fi
}

#
# Functions to appease unconverted extension scripts
#
save_command()
{
    return 0
}

run_and_save_command() {
    eval \$@
}

ensure_and_save_command() {
    eval \$@ || fatal_error "Command \"\$@\" failed"
}

#
# Initialize environment
#
initialize() {
__EOF__
    INDENT="    "

    if [ -n "$EXPORT" ]; then
	cat >&3 << __EOF__
    #
    # These variables are required by the library functions called in this script
    #
    CONFIG_PATH="/etc/shorewall-lite:/usr/share/shorewall-lite"
__EOF__
    else
	cat >&3 << __EOF__
    if [ ! -f \${SHAREDIR}/version ]; then
	fatal_error "This script requires Shorewall which do not appear to be installed on this system (did you forget "-e" when you compiled?)"
    fi

    local version=\$(cat \${SHAREDIR}/version)

    if [ \${SHOREWALL_LIBVERSION:-0} -lt 30200 ]; then
	fatal_error "This script requires Shorewall version 3.3.3 or later; current version is \$version"
    fi
    #
    # These variables are required by the library functions called in this script
    #
    CONFIG_PATH="$CONFIG_PATH"
__EOF__
    fi

    cat >&3 << __EOF__
    [ -n "\${COMMAND:=restart}" ]
    [ -n "\${VERBOSE:=0}" ]
    [ -n "\${RESTOREFILE:=$RESTOREFILE}" ]
    MODULESDIR="$MODULESDIR"
    MODULE_SUFFIX="$MODULE_SUFFIX"
    LOGLIMIT="$LOGLIMIT"
    LOGTAGONLY="$LOGTAGONLY"
    LOGRULENUMBERS="$LOGRULENUMBERS"
__EOF__

    if [ -n "$LOGFORMAT" ]; then
	cat >&3 << __EOF__
    LOGFORMAT="$LOGFORMAT"
__EOF__
    else
	cat >&3 << __EOF__
    [ -n "\$LOGFORMAT\" ] || LOGFORMAT="Shorewall:%s:%s:"
__EOF__
    fi

    cat >&3 << __EOF__
    VERSION="$VERSION"
    SUBSYSLOCK="$SUBSYSLOCK"
    PATH="$PATH"
    TERMINATOR=fatal_error

__EOF__
    if [ -n "$IPTABLES" ]; then
	cat >&3 << __EOF__
    IPTABLES="$IPTABLES"

    [ -e "$IPTABLES" ] || startup_error "IPTABLES=$IPTABLES does not exist or is not executable"
__EOF__
   else
	cat >&3 << __EOF__
    [ -z "\$IPTABLES" ] && IPTABLES=\$(mywhich iptables 2> /dev/null)

    [ -n "\$IPTABLES" -a -e "\$IPTABLES" ] || startup_error "Can't find iptables executable"
__EOF__
    fi

    append_file params

    cat >&3 << __EOF__

    STOPPING=
    #
    # The library requires that ${VARDIR} exist
    #
    [ -d \${VARDIR} ] || mkdir -p \${VARDIR}

}

#
# Start/Restart/Reload the firewall
#
define_firewall() {
    local restore_file=\$1
__EOF__

    INDENT="    "

    cat >&3 << __EOF__

    load_kernel_modules

__EOF__

    progress_message2 "Initializing..."
    save_progress_message "Initializing..."

    report_capabilities

    if [ -n "$BRIDGING" ]; then
	[ -n "$PHYSDEV_MATCH" ] || fatal_error "BRIDGING=Yes requires Physdev Match support in your Kernel and iptables"
    fi

    [ "$MACLIST_TTL" = "0" ] && MACLIST_TTL=

    if [ -n "$MACLIST_TTL" -a -z "$RECENT_MATCH" ]; then
	fatal_error "MACLIST_TTL requires the Recent Match capability which is not present in your Kernel and/or iptables"
    fi

    [ -n "$RFC1918_STRICT" -a -z "$CONNTRACK_MATCH" ] && \
	fatal_error "RFC1918_STRICT=Yes requires Connection Tracking match"

    progress_message2 "Determining Zones..."

    determine_zones

    if [ $VERBOSE -ge 1 ]; then
	display_list "IPv4 Zones:" $IPV4_ZONES
	[ -n "$IPSEC_ZONES" ] && \
	    display_list "IPSEC Zones:" $IPSEC_ZONES
	display_list "Firewall Zone:" $FW
    fi

    progress_message2 "Validating interfaces file..."

    validate_interfaces_file

    progress_message2 "Validating hosts file..."

    validate_hosts_file

    define_builtin_actions

    if [ -n "$USE_ACTIONS" ]; then
	progress_message2 "Pre-processing Actions..."
	process_actions1
    fi

    progress_message2 "Validating Policy file..."

    validate_policy

    progress_message2 "Determining Hosts in Zones..."

    determine_interfaces
    determine_hosts

    append_file init

    strip_file rules
    strip_file maclist

    TERMINATOR=fatal_error

    deletechain shorewall

    if [ -n "$NAT_ENABLED" ]; then
	delete_nat
	for chain in PREROUTING POSTROUTING OUTPUT; do
	    qt_iptables -t nat -P $chain ACCEPT
	done
    fi

    delete_proxy_arp

    if [ -n "$MANGLE_ENABLED" ]; then
	run_iptables -t mangle -F
	run_iptables -t mangle -X
	for chain in PREROUTING INPUT FORWARD POSTROUTING; do
	    qt_iptables -t mangle -P $chain ACCEPT
	done
    fi

    if [ -n "$RAW_TABLE" ]; then
	run_iptables -t raw -F
	run_iptables -t raw -X
	for chain in PREROUTING OUTPUT; do
	    qt_iptables -t raw -P $chain ACCEPT
	done
    fi

    [ -n "$CLEAR_TC" ] && delete_tc

    progress_message2 "Deleting user chains..."

    save_progress_message "Deleting user chains..."

    exists_INPUT=Yes
    exists_OUTPUT=Yes
    exists_FORWARD=Yes

    process_criticalhosts

    if [ -n "$CRITICALHOSTS" ]; then

	setpolicy INPUT ACCEPT
	setpolicy OUTPUT ACCEPT
	setpolicy FORWARD DROP

	deleteallchains

	enable_critical_hosts

	setpolicy INPUT DROP
	setpolicy OUTPUT DROP

	[ -n "$CLAMPMSS" ] && setup_mss

	setcontinue FORWARD
	setcontinue INPUT
	setcontinue OUTPUT
    else

	setpolicy INPUT DROP
	setpolicy OUTPUT DROP
	setpolicy FORWARD DROP

	deleteallchains

	[ -n "$CLAMPMSS" ] && setup_mss

	setcontinue FORWARD
	setcontinue INPUT
	setcontinue OUTPUT
    fi

    f=$(find_file ipsets)

    if [ -f $f ]; then
	progress_message2 "Processing $f ..."
	save_progress_message "Restoring IPSETS..."
	save_command "ipset -U :all: :all:"
	save_command "ipset -F"
	save_command "ipset -X"
	save_command "ipset -R < $f"
    fi

    append_file continue

    f=$(find_file routestopped)

    progress_message2 "$DOING $f ..."

    strip_file routestopped $f

    process_routestopped -A

    if [ -n "$DISABLE_IPV6" ]; then
	save_command disable_ipv6
    fi

    save_progress_message "Enabling Loopback and DNS Lookups"

    #
    # Enable the Loopback interface for now
    #
    run_iptables -A INPUT  -i lo -j ACCEPT
    run_iptables -A OUTPUT -o lo -j ACCEPT

    #
    # Allow DNS lookups during startup for FQDNs
    #

    for chain in INPUT OUTPUT FORWARD; do
	run_iptables -A $chain -p udp --dport 53 -j ACCEPT
    done

    strip_file_and_lib_load accounting accounting && setup_accounting $(find_file accounting)
    
    createchain reject	no
    createchain dynamic no
    createchain logdrop no
    createchain logreject no
    createchain smurfs no

    log_rule ${BLACKLIST_LOGLEVEL:-info} logdrop DROP
    log_rule ${BLACKLIST_LOGLEVEL:-info} logreject REJECT

    run_iptables -A logdrop   -j DROP
    run_iptables -A logreject -j reject

    indent >&3 << __EOF__

if [ -f \${VARDIR}/save ]; then
    progress_message2 "Setting up dynamic rules..."
    while read target ignore1 ignore2 address rest; do
        case \$target in
            DROP|reject|logdrop|logreject)
                run_iptables -A dynamic -s \$address -j \$target
                ;;
        esac
    done < \${VARDIR}/save
fi
__EOF__

    [ -n "$BLACKLISTNEWONLY" ] && state="-m state --state NEW,INVALID" || state=

    progress_message2 "Creating Interface Chains..."

    save_progress_message "Creating Interface Chains..."

    for interface in $ALL_INTERFACES; do
	for chain in $(input_chain $interface) $(forward_chain $interface); do
	    createchain $chain no
	    run_iptables -A $chain $state -j dynamic
	done
    done

    if strip_file_and_lib_load proxyarp proxyarp; then
	progress_message2 "$DOING Proxy ARP"
	setup_proxy_arp
    else
	> $STATEDIR/proxyarp
    fi

    #
    # [re]-Establish routing
    #
    if strip_file_and_lib_load providers providers; then
	setup_providers $(find_file providers)
	[ -n "$ROUTEMARK_INTERFACES" ] && setup_routes
    fi

    if strip_file_and_lib_load nat nat; then
	progress_message2 "$DOING NAT..."
	setup_nat
    else
	> $STATEDIR/nat
    fi

    if strip_file_and_lib_load netmap nat; then
	progress_message2 "$DOING NETMAP..."
	setup_netmap
    fi

    progress_message2 "$DOING Common Rules";            add_common_rules

    save_progress_message "Setting up SYN Flood Protection..."

    setup_syn_flood_chains

    save_progress_message "Setting up IPSEC management..."

    setup_ipsec

    maclist_hosts=$(find_hosts_by_option maclist)

    if [ -n "$maclist_hosts" ]; then
	save_progress_message "Setting up MAC Filtration..."
	setup_mac_lists
    fi

    progress_message2 "$DOING $(find_file rules)..."
    save_progress_message "Setting up Rules..."
    process_rules

    if strip_file_and_lib_load tunnels tunnels; then
	tunnels=$(find_file tunnels)
	progress_message2 "$DOING $tunnels..."
	save_progress_message "Setting up Tunnels..."
	setup_tunnels $tunnels
    fi

    if [ -n "$DEFAULT_MACROS" ]; then
	progress_message2 "$DOING default macros..."
	save_progress_message "Creating default macro chains..."
	for macro in $DEFAULT_MACROS; do
	    process_default_macro $macro
	done
    fi

    if [ -n "$USEDACTIONS" ]; then
	save_progress_message "Setting up Actions..."

	progress_message2 "$DOING Actions...";
	[ -n "$USE_ACTIONS" ] && process_actions2
	process_actions3
    fi

    save_progress_message "Applying Policies..."

    progress_message2 "$DOING $(find_file policy)...";   apply_policy_rules

    if strip_file_and_lib_load masq nat; then
	setup_masq $(find_file masq)
    fi

    if [ -n "$MANGLE_ENABLED" ]; then
	tos=$(find_file tos)
	if [ -f $tos ]; then
	    save_progress_message "Setting up TOS..."
	    process_tos $tos
	fi

	ecn=$(find_file ecn)
	if [ -f $ecn ]; then
	    save_progress_message "Setting up ECN..."
	    setup_ecn $ecn
	fi

	save_progress_message "Setting up TC Rules..."
	setup_tc
    fi

    progress_message2 "$DOING Rule Activation..."
    save_progress_message "Activating Rules..."
    activate_rules

    if [ -n "$ALIASES_TO_ADD" ]; then
	save_command add_ip_aliases $ALIASES_TO_ADD
    fi

    for file in chains nat proxyarp zones; do
	save_command "cat > \${VARDIR}/$file $LEFTSHIFT __EOF__"
	cat $STATEDIR/$file >&3
	save_command_unindented __EOF__
    done

    cat >&3 << __EOF__

    if [ \$COMMAND = restore ]; then
	iptables-restore < \$restore_file
    fi

__EOF__
    save_command "date > \${VARDIR}/restarted"

    append_file start

    [ -n "$DELAYBLACKLISTLOAD" ] && refresh_blacklist

    createchain shorewall no

    save_command set_state "Started"

    append_file started

    cat >&3 << __EOF__

    cp -f \$(my_pathname) \${VARDIR}/.restore

    case \$COMMAND in
	start)
	    logger "Shorewall started"
	    ;;
	restart)
	    logger "Shorewall restarted"
	    ;;
	restore)
	    logger "Shorewall restored"
	    ;;
    esac

}

#
# Silently define Firewall and ignore errors
#
restore_firewall()
{
    iptables_save_file=\${VARDIR}/\$(basename \$0)-iptables

    fatal_error()
    {
	echo "   ERROR: \$@" >&2
    }

    startup_error() # \$@ = Error Message
    {
	echo "   ERROR: \$@" >&2
    }

    run_iptables() { return 0; }

    VERBOSE=-1 # The progress messages don't make sense without iptables

    IPTABLES=run_iptables

    if [ -f \$iptables_save_file ]; then
	{
	    define_firewall \$iptables_save_file
	}
    else
	fatal_error "\$iptables_save_file does not exist"
	exit 2
    fi
}

__EOF__

    compile_refresh_firewall

    exec 3>&-

    if [ -n "$checking" ]; then
	progress_message3 "Shorewall configuration verified"
    else
	INDENT=
	cat $(find_file prog.header) $OUTPUT $(find_file prog.footer) > $outfile
	chmod 700 $outfile
	if [ -n "$EXPORT" ]; then
	    exec 3>${outfile}.conf
	    cat >&3 << __EOF__
#
# Shorewall auxiliary configuration file created by Shorewall version $VERSION - $(date)
#
__EOF__
	    for option in VERBOSITY LOGFILE LOGFORMAT IPTABLES PATH SHOREWALL_SHELL SUBSYSLOCK RESTOREFILE; do
		conditionally_add_option $option
	    done
	    
	    conditionally_add_option1 TC_ENABLED

	    exec 3>&-	    
	fi

	progress_message3 "Shorewall configuration compiled to $(resolve_file $outfile)"
	rm -f $OUTPUT
    fi

    rm -rf $TMP_DIR

}

#
# Give Usage Information
#
usage() {
    echo "Usage: $0 [debug] check|compile <filename>}"
    exit 1
}

#
# E X E C U T I O N    B E G I N S   H E R E
#
#
# Start trace if first arg is "debug"
#
[ $# -gt 1 ] && [ "$1" = "debug" ] && { set -x ; shift ; }

NOLOCK=

[ $# -gt 1 ] && [ "$1" = "nolock" ] && { NOLOCK=Yes; shift ; }

trap "exit 2" 1 2 3 4 5 6 9

SHAREDIR=/usr/share/shorewall
VARDIR=/var/lib/shorewall
[ -z "$EXPORT" ] && CONFDIR=/etc/shorewall || CONFDIR=${SHAREDIR}/configfiles

[ -n "${VERBOSE:=2}" ]

for library in lib.base lib.config; do
    FUNCTIONS=${SHAREDIR}/${library}

    if [ -f $FUNCTIONS ]; then
	[ $VERBOSE -ge 2 ] && echo "Loading $FUNCTIONS..."
	. $FUNCTIONS
    else
	fatal_error "$FUNCTIONS does not exist!"
    fi
done

PROGRAM=compiler

COMMAND="$1"

case "$COMMAND" in

    check)
	[ $# -ne 1 ] && usage
	do_initialize
	compile_firewall
	;;

    compile)
	[ $# -ne 2 ] && usage
	do_initialize
	compile_firewall $2
	;;

    call)
	#
	# Undocumented way to call functions in ${SHAREDIR}/compiler directly
	#
	shift
	do_initialize
	EMPTY=
	$@
	;;

    *)
	usage
	;;

esac