#!/bin/sh
#
#     The Shoreline Firewall (Shorewall) Packet Filtering Firewall - V3.2
#
#     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)
#
#     tcstart from tc4shorewall       Version 0.5
#     (c) 2005 Arne Bernin <arne@ucbering.de>
#     Modified by Tom Eastep for integration into the Shorewall distribution
#     published under GPL Version 2#
#
#	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:
#
#	   shorewall start			  Starts the firewall
#	   shorewall restart			  Restarts the firewall
#	   shorewall stop			  Stops the firewall
#	   shorewall reset			  Resets iptables packet and
#						  byte counts
#	   shorewall clear			  Remove all Shorewall chains
#						  and rules/policies.
#
# Mutual exclusion -- These functions are jackets for the mutual exclusion
#		      routines in $FUNCTIONS. They invoke
#		      the corresponding function in that file if the user did
#		      not specify "nolock" on the runline.
#
my_mutex_on() {
    [ -n "$NOLOCK" ] || { mutex_on; HAVE_MUTEX=Yes; }
}

my_mutex_off() {
    [ -n "$HAVE_MUTEX" ] && { mutex_off; HAVE_MUTEX=; }
}

#
# Fatal error -- stops the firewall after issuing the error message
#
fatal_error() # $* = Error Message
{
    echo "   ERROR: $@" >&2
    stop_firewall
    exit 2
}

#
# Fatal error during startup -- generate an error message and abend without
#				altering the state of the firewall
#
startup_error() # $* = Error Message
{
    echo "   ERROR: $@" >&2
    my_mutex_off
    [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
    [ -n "$RESTOREBASE" ] && rm -f $RESTOREBASE
    kill $$
    exit 2
}

#
# Send a message to STDOUT and the System Log
#
report () { # $* = message
    progress_message3 "$@"
    logger "$@"
}

#
# Run iptables and if an error occurs, stop the firewall and quit
#
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

   if ! $IPTABLES $@ ; then
	if [ -z "$STOPPING" ]; then
	    error_message "ERROR: Command \"$IPTABLES $@\" Failed"
	    stop_firewall
	    exit 2
	fi
    fi
}

#
# Version of 'run_iptables' that inserts white space after "!" in the arg list
#
run_iptables2() {

    case "$@" in
	*!*)
	    run_iptables $(fix_bang $@)
	    ;;
	*)
	    run_iptables $@
	    ;;
    esac

}

#
# 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

    qt $IPTABLES $@
}

#
# Run ip and if an error occurs, stop the firewall and quit
#
run_ip() {
    if ! ip $@ ; then
	if [ -z "$STOPPING" ]; then
	    error_message "ERROR: Command \"ip $@\" Failed"
	    stop_firewall
	    exit 2
	fi
    fi
}

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

#
# Delete a chain if it exists
#
deletechain() # $1 = name of chain
{
    qt $IPTABLES -L $1 -n && qt $IPTABLES -F $1 && qt $IPTABLES -X $1
}

#
# 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" ]; then
	    ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u
	elif [ "x${bcast}" != "x-" ]; then
	    echo $(separate_list $bcast)
	fi
    done
}

#
# 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#*:}
	$IPTABLES -A INPUT  -i $interface $(source_ip_range $networks) -j ACCEPT
	$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#*:}
	$IPTABLES -D INPUT  -i $interface $(source_ip_range $networks) -j ACCEPT
	$IPTABLES -D OUTPUT -o $interface $(dest_ip_range $networks) -j ACCEPT
    done
}

#
# Stop the Firewall
#
stop_firewall() {
    #
    # Turn off trace unless we were tracing "stop" or "clear"
    #

    [ -n "$RESTOREBASE" ] && rm -f $RESTOREBASE

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

	    [ -n "${RESTOREFILE:=restore}" ]

	    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 Shorewall...

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

		my_mutex_off
		kill $$
		exit 2
	    fi
	    ;;
    esac

    set_state "Stopping"

    STOPPING="Yes"

    TERMINATOR=

    deletechain shorewall

    run_user_exit stop

    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

    delete_proxy_arp
    [ -n "$CLEAR_TC" ] && delete_tc1

    [ -n "$DISABLE_IPV6" ] && disable_ipv6

    process_criticalhosts

    if [ -n "$CRITICALHOSTS" ]; then
	if [ -z "$ADMINISABSENTMINDED" ]; then
	    for chain in INPUT OUTPUT; do
		setpolicy $chain ACCEPT
	    done

	    setpolicy FORWARD DROP

	    deleteallchains

	    enable_critical_hosts

	    for chain in INPUT OUTPUT; do
		setpolicy $chain DROP
	    done
	else
	    for chain in INPUT OUTPUT; do
		setpolicy $chain ACCEPT
	    done

	    setpolicy FORWARD DROP

	    deleteallchains

	    enable_critical_hosts

	    setpolicy INPUT DROP

	    for chain in INPUT FORWARD; do
		setcontinue $chain
	    done
	fi
    elif [ -z "$ADMINISABSENTMINDED" ]; then
	for chain in INPUT OUTPUT FORWARD; do
	    setpolicy $chain DROP
	done

	deleteallchains
    else
	for chain in INPUT FORWARD; do
	    setpolicy $chain DROP
	done

	setpolicy OUTPUT ACCEPT

	deleteallchains

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

    process_routestopped -A

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

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

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

    run_user_exit stopped

    set_state "Stopped"

    logger "Shorewall Stopped"

    rm -rf $TMP_DIR

    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
	#
	my_mutex_off
	kill $$
	;;
    esac
}

#
# Remove all rules and remove all user-defined chains
#
clear_firewall() {
    stop_firewall

    setpolicy INPUT ACCEPT
    setpolicy FORWARD ACCEPT
    setpolicy OUTPUT ACCEPT

    run_iptables -F

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

    if [ -n "$DISABLE_IPV6" ] && 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

    run_user_exit clear

    set_state "Cleared"

    logger "Shorewall Cleared"
}

#
# Delete existing Proxy ARP
#
delete_proxy_arp() {
    if [ -f ${VARDIR}/proxyarp ]; then
	while read address interface external haveroute; do
	    case $COMMAND in
		stop|clear)
		    qt arp -i $external -d $address pub
		    [ -z "${haveroute}${NOROUTES}" ] && qt ip route del $address dev $interface
		    ;;
		*)
		    if [ -n "$STOPPING" ]; then
			qt arp -i $external -d $address pub
			qt arp -i $external -d $address pub
			[ -z "${haveroute}${NOROUTES}" ] && qt ip route del $address dev $interface
		    else
			qt arp -i $external -d $address pub
			if [ -z "$haveroute" ];then
			    [ -n "$NOROUTE" ] || qt ip route del $address dev $interface
			fi
		    fi
		    ;;
	    esac
	done < ${VARDIR}/proxyarp

	rm -f ${VARDIR}/proxyarp
    fi

    [ -d ${VARDIR} ] && touch ${VARDIR}/proxyarp

    case $COMMAND in
	stop|clear)
	    for f in /proc/sys/net/ipv4/conf/*; do
		[ -f $f/proxy_arp ] && echo 0 > $f/proxy_arp
	    done
	    ;;
	*)
	    if [ -n "$STOPPING" ]; then
		for f in /proc/sys/net/ipv4/conf/*; do
		    [ -f $f/proxy_arp ] && echo 0 > $f/proxy_arp
		done
	    else
		for f in /proc/sys/net/ipv4/conf/*; do
		    [ -f $f/proxy_arp ] && echo 0 > $f/proxy_arp
		done
	    fi
	    ;;
    esac
}

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

    if [ -f ${VARDIR}/nat ]; then
	while read external interface; do
	    qt ip addr del $external dev $interface
	done < ${VARDIR}/nat

	rm -f ${VARDIR}/nat
    fi

    [ -d ${VARDIR} ] && touch ${VARDIR}/nat
}

#
# Display elements of a list with leading white space
#
display_list() # $1 = List Title, rest of $* = list to display
{
    [ $# -gt 1 ] && echo "   $*"
}

#
# Verify that kernel has netfilter support
#
verify_os_version() {

    osversion=$(uname -r)

    case $osversion in
    2.4.*|2.5.*|2.6.*)
	;;
    *)
	startup_error "Shorewall version $VERSION does not work with kernel version $osversion"
	;;
    esac

    [ $COMMAND = start -a -n "$(lsmod 2> /dev/null | grep '^ipchains')" ] && \
	startup_error "Shorewall can't start with the ipchains kernel module loaded - see FAQ #8"
}

#
# Check for disabled startup
#
check_disabled_startup() {
    if [ -z "$STARTUP_ENABLED" ]; then
	echo "   Shorewall Startup is disabled -- to enable startup"
	echo "   after you have completed Shorewall configuration,"
	echo "   change the setting of STARTUP_ENABLED to Yes in"
        echo "   ${CONFDIR}/shorewall.conf"

	[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
	my_mutex_off
	exit 2
    fi
}

#
# Add a host or networks to a zone
#
add_to_zone() # $1...${n-1} = <interface>[:<hosts>] $n = zone
{
    local interface host zone z h z1 z2 chain
    local dhcp_interfaces blacklist_interfaces maclist_interfaces
    local tcpflags_interfaces newhostlist=
    local rulenum source_chain dest_hosts iface hosts hostlist=

    nat_chain_exists() # $1 = chain name
    {
	qt $IPTABLES -t nat -L $1 -n
    }

    do_iptables() # $@ = command
    {
	[ -n "$BRIDGING" ]      && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
	[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange

	if ! $IPTABLES $@ ; then
	    error_message "ERROR: Can't add $newhost to zone $zone"
	fi
    }

    DOING=Processing
    DONE=Processed
    #
    # Load $zones
    #
    determine_zones
    #
    # Validate Interfaces File
    #
    validate_interfaces_file
    #
    # Validate Hosts File
    #
    validate_hosts_file
    #
    # Validate IPSec File
    #
    f=$(find_file $IPSECFILE)

    [ -f $f ] && setup_ipsec $f
    #
    # Normalize host list
    #
    while [ $# -gt 1 ]; do
	interface=${1%%:*}
	host=${1#*:}
        #
        # Be sure that the interface was dynamic at last [re]start
        #
	if ! chain_exists $(input_chain $interface) ; then
	    startup_error "Unknown interface $interface"
	fi

	if ! chain_exists $(dynamic_in $interface) ; then
	    startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf"
	fi

	if [ -z "$host" ]; then
	    hostlist="$hostlist $interface:0.0.0.0/0"
	else
	    for h in $(separate_list $host); do
		hostlist="$hostlist $interface:$h"
	    done
	fi

	shift
    done
    #
    # Validate Zone
    #
    zone=$1

    validate_zone $zone || startup_error "Unknown zone: $zone"

    [ "$zone" = $FW ] && startup_error "Can't add $1 to firewall zone"

    #
    # Be sure that Shorewall has been restarted using a DZ-aware version of the code
    #
    [ -f ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found"
    [ -f ${VARDIR}/zones ]  || startup_error "${VARDIR}/zones -- file not found"
    #
    # Check for duplicates and create a new zone state file
    #
    > ${VARDIR}/zones_$$

    while read z type hosts; do
	if [ "$z" = "$zone" ]; then
	    for h in $hostlist; do
		list_search $h $hosts
		if [ "$?" -gt 0 ]; then
		    newhostlist="$newhostlist $h"
		else
		    error_message "$h already in zone $zone"
		fi
	    done

	    [ -z "$hosts" ] && hosts=$newhostlist || hosts="$hosts $newhostlist"
	fi

	eval ${z}_hosts=\"$hosts\"

	echo "$z $type $hosts" >> ${VARDIR}/zones_$$
    done < ${VARDIR}/zones

    mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones

    TERMINATOR=fatal_error
    #
    # Create a new Zone state file
    #
    for newhost in $newhostlist; do
        #
        # Isolate interface and host parts
        #
	interface=${newhost%%:*}
	host=${newhost#*:}
        #
        # If the zone passed in the command has a dnat chain then insert a rule in
        # the nat table PREROUTING chain to jump to that chain when the source
        # matches the new host(s)#
        #
	chain=${zone}_dnat

	if nat_chain_exists $chain; then
	    do_iptables -t nat -A $(dynamic_in $interface) $(source_ip_range $host) $(match_ipsec_in $zone $newhost) -j $chain
	fi
        #
        # Insert new rules into the filter table for the passed interface
        #
	while read z1 z2 chain; do
	    [ "$z1" = "$z2" ] && op="-I" || op="-A"
	    if [ "$z1" = "$zone" ]; then
		if [ "$z2" = "$FW" ]; then
		    do_iptables $op $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j $chain
		else
		    source_chain=$(dynamic_fwd $interface)
		    if is_ipsec_host $z1 $newhost ; then
			do_iptables $op $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd
		    else
			eval dest_hosts=\"\$${z2}_hosts\"

			for h in $dest_hosts; do
			    iface=${h%%:*}
			    hosts=${h#*:}

			    if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then
				do_iptables $op $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain
			    fi
			done
		    fi
		fi
	    elif [ "$z2" = "$zone" ]; then
		if [ "$z1" = "$FW" ]; then
		    #
		    # Add a rule to the dynamic out chain for the interface
		    #
		    do_iptables $op $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain
		else
		    eval source_hosts=\"\$${z1}_hosts\"

		    for h in $source_hosts; do
			iface=${h%%:*}
			hosts=${h#*:}

			if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then
			    if is_ipsec_host $z1 $h; then
				do_iptables $op ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain
			    else
				do_iptables $op $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain
			    fi
			fi
		    done
		fi
	    fi
	done < ${VARDIR}/chains

	progress_message "$newhost added to zone $zone"

    done

    rm -rf $TMP_DIR
}

#
# Delete a host or networks from a zone
#
delete_from_zone() # $1 = <interface>[:<hosts>] $2 = zone
{
    local interface host zone z h z1 z2 chain delhost
    local dhcp_interfaces blacklist_interfaces maclist_interfaces tcpflags_interfaces
    local rulenum source_chain dest_hosts iface hosts hostlist=

    DOING=Processing
    DONE=Processed
    #
    # Load $zones
    #
    determine_zones
    #
    # Validate Interfaces File
    #
    validate_interfaces_file
    #
    # Validate Hosts File
    #
    validate_hosts_file
    #
    # Validate IPSec File
    #
    f=$(find_file ipsec)

    [ -f $f ] && setup_ipsec $f

    #
    # Normalize host list
    #
    while [ $# -gt 1 ]; do
	interface=${1%%:*}
	host=${1#*:}
        #
        # Be sure that the interface was dynamic at last [re]start
        #
	if ! chain_exists $(input_chain $interface) ; then
	    startup_error "Unknown interface $interface"
	fi

	if ! chain_exists $(dynamic_in $interface) ; then
	    startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf"
	fi

	if [ -z "$host" ]; then
	    hostlist="$hostlist $interface:0.0.0.0/0"
	else
	    for h in $(separate_list $host); do
		hostlist="$hostlist $interface:$h"
	    done
	fi

	shift
    done
    #
    # Validate Zone
    #
    zone=$1

    validate_zone $zone || startup_error "Unknown zone: $zone"

    [ "$zone" = $FW ] && startup_error "Can't delete from the firewall zone"

    #
    # Be sure that Shorewall has been restarted using a DZ-aware version of the code
    #
    [ -f ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found"
    [ -f ${VARDIR}/zones ]  || startup_error "${VARDIR}/zones -- file not found"
    #
    # Delete the passed hosts from the zone state file
    #
    > ${VARDIR}/zones_$$

    while read z hosts; do
	if [ "$z" = "$zone" ]; then
	    temp=$hosts
	    hosts=

	    for host in $hostlist; do
		found=
		for h in $temp; do
		    if [ "$h" = "$host" ]; then
			found=Yes
			break
		    fi
		done

		[ -n "$found" ] || error_message "WARNING: $host does not appear to be in zone $zone"
	    done

	    for h in $temp; do
		found=
		for host in $hostlist; do
		    if [ "$h" = "$host" ]; then
			found=Yes
			break
		    fi
		done

		[ -n "$found" ] || hosts="$hosts $h"
	    done
	fi

	eval ${z}_hosts=\"$hosts\"

	echo "$z $hosts" >> ${VARDIR}/zones_$$
    done < ${VARDIR}/zones

    mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones

    TERMINATOR=fatal_error

    for delhost in $hostlist; do
	interface=${delhost%%:*}
	host=${delhost#*:}
        #
        # Delete any nat table entries for the host(s)
        #
	qt_iptables -t nat -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $zone $delhost) -j ${zone}_dnat
        #
        # Delete rules rules the input chains for the passed interface
        #
	while read z1 z2 chain; do
	    if [ "$z1" = "$zone" ]; then
		if [ "$z2" = "$FW" ]; then
		    qt_iptables -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $delhost) -j $chain
		else
		    source_chain=$(dynamic_fwd $interface)
		    if is_ipsec_host $z1 $delhost ; then
			qt_iptables -D $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd
		    else
			eval dest_hosts=\"\$${z2}_hosts\"

			[ "$z2" = "$zone" ] && dest_hosts="$dest_hosts $hostlist"

			for h in $dest_hosts; do
			    iface=${h%%:*}
			    hosts=${h#*:}

			    if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then
				qt_iptables -D $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain
			    fi
			done
		    fi
		fi
	    elif [ "$z2" = "$zone" ]; then
		if [ "$z1" = "$FW" ]; then
		    qt_iptables -D $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain
		else
		    eval source_hosts=\"\$${z1}_hosts\"

		    for h in $source_hosts; do
			iface=${h%%:*}
			hosts=${h#*:}

			if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then
				if is_ipsec_host $z1 $h; then
				    qt_iptables -D ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain
				else
				    qt_iptables -D $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host)  $(match_ipsec_out $z2 $delhost) -j $chain
				fi
			fi
		    done
		fi
	    fi
	done < ${VARDIR}/chains

	progress_message "$delhost removed from zone $zone"

    done

    rm -rf $TMP_DIR
}

#
# Give Usage Information
#
usage() {
    echo "Usage: $0 [debug] {start|stop|reset|restart|clear}"
    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 "my_mutex_off; exit 2" 1 2 3 4 5 6 9

SHAREDIR=/usr/share/shorewall
VARDIR=/var/lib/shorewall
CONFDIR=/etc/shorewall

FUNCTIONS=${SHAREDIR}/functions

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

PROGRAM=firewall

COMMAND="$1"

case "$COMMAND" in
    stop)
	[ $# -ne 1 ] && usage
	do_initialize
	my_mutex_on
	#
	# Don't want to do a 'stop' when startup is disabled
	#
	check_disabled_startup
	progress_message3 "Stopping Shorewall..."
	stop_firewall
	[ -n "$SUBSYSLOCK" ] && rm -f $SUBSYSLOCK
	progress_message3 "done."
	my_mutex_off
	;;

    reset)
	[ $# -ne 1 ] && usage
	do_initialize
	my_mutex_on
	if ! shorewall_is_started ; then
	    echo "Shorewall Not Started"
	    [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
	    my_mutex_off
	    exit 2;
	fi
	$IPTABLES -Z
	$IPTABLES -t nat -Z
	$IPTABLES -t mangle -Z
	report "Shorewall Counters Reset"
	date > ${VARDIR}/restarted
	my_mutex_off
	;;

    clear)
	[ $# -ne 1 ] && usage
	do_initialize
	my_mutex_on
	progress_message3 "Clearing Shorewall..."
	clear_firewall
	[ -n "$SUBSYSLOCK" ] && rm -f $SUBSYSLOCK
	progress_message3 "done."
	my_mutex_off
	;;

     add)
	[ $# -lt 3 ] && usage
	do_initialize
	my_mutex_on
	if ! shorewall_is_started ; then
	    echo "Shorewall Not Started"
	    [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
	    my_mutex_off
	    exit 2;
	fi
	shift
	add_to_zone $@
	my_mutex_off
	;;

    delete)
	[ $# -lt 3 ] && usage
	do_initialize
	my_mutex_on
	if ! shorewall_is_started ; then
	    echo "Shorewall Not Started"
	    [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
	    my_mutex_off
	    exit 2;
	fi
	shift
	delete_from_zone $@
	my_mutex_off
	;;

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

    *)
	usage
	;;

esac
