#!/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
}

#
# Validate the zone names and options in the interfaces file
#
validate_interfaces_file() {
    local wildcard
    local found_obsolete_option=
    local z interface networks options r iface option

    while read z interface networks options; do
	expandv z interface networks options
	r="$z $interface $networks $options"

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

	if [ -n "$z" ]; then
	    validate_zone $z || startup_error "Invalid zone ($z) in record \"$r\""
	fi

	list_search $interface $ALL_INTERFACES && \
	    startup_error "Duplicate Interface $interface"

	wildcard=

	case $interface in
	    *:*|+)
		startup_error "Invalid Interface Name: $interface"
		;;
	    *+)
		wildcard=Yes
		;;
	esac

	ALL_INTERFACES="$ALL_INTERFACES $interface"
	options=$(separate_list $options)
	iface=$(chain_base $interface)

	eval ${iface}_broadcast="$networks"
	eval ${iface}_zone="$z"
	eval ${iface}_options=\"$options\"

	for option in $options; do
	    case $option in
		-)
		    ;;
		dhcp|tcpflags|arp_filter|routefilter|maclist|logmartians|sourceroute|blacklist|proxyarp|nosmurfs|upnp|-)
		    ;;
		norfc1918)
		    addr=$(ip -f inet addr show $interface 2> /dev/null | grep inet | head -n1)
		    if [ -n "$addr" ]; then
			addr=$(echo $addr | sed 's/inet //;s/\/.*//;s/ peer.*//')
			for network in 10.0.0.0/8 176.16.0.0/12 192.168.0.0/16; do
			    if in_network $addr $network; then
				startup_error "The 'norfc1918' option may not be specified on an interface with an RFC 1918 address. Interface:$interface"
			    fi
			done
		    fi
		    ;;
		arp_ignore=*)
		    eval ${iface}_arp_ignore=${option#*=}
		    ;;
		arp_ignore)
		    eval ${iface}_arp_ignore=1
		    ;;
		detectnets)
		    [ -n "$wildcard" ] && \
			startup_error "The \"detectnets\" option may not be used with a wild-card interface"
		    [ -n "$EXPORT" ] && \
			startup_error "'detectnets' not permitted with the -e run-line option"
		    ;;
		routeback)
		    [ -n "$z" ] || startup_error "The routeback option may not be specified on a multi-zone interface"
		    ;;
		*)
		    error_message "WARNING: Invalid option ($option) in record \"$r\""
		    ;;
	    esac
	done
    done < $TMP_DIR/interfaces

    [ -z "$ALL_INTERFACES" ] && startup_error "No Interfaces Defined"
}

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

#
# Process the ipsec information in the zones file
#
setup_ipsec() {
    local zone using_ipsec=

    do_options() # $1 = _in, _out or "" - $2 = option list
    {
	local option opts newoptions= val

	[ x${2} = x- ] && return

	opts=$(separate_list $2)

	for option in $opts; do
	    val=${option#*=}

	    case $option in
		mss=[0-9]*)    ;;
		strict)        newoptions="$newoptions --strict" ;;
		next)          newoptions="$newoptions --next" ;;
		reqid=*)       newoptions="$newoptions --reqid $val" ;;
		spi=*)         newoptions="$newoptions --spi $val" ;;
		proto=*)       newoptions="$newoptions --proto $val" ;;
		mode=*)        newoptions="$newoptions --mode $val" ;;
		tunnel-src=*)  newoptions="$newoptions --tunnel-src $val" ;;
		tunnel-dst=*)  newoptions="$newoptions --tunnel-dst $val" ;;
		reqid!=*)      newoptions="$newoptions ! --reqid $val" ;;
		spi!=*)        newoptions="$newoptions ! --spi $val" ;;
		proto!=*)      newoptions="$newoptions ! --proto $val" ;;
		mode!=*)       newoptions="$newoptions ! --mode $val" ;;
		tunnel-src!=*) newoptions="$newoptions ! --tunnel-src $val" ;;
		tunnel-dst!=*) newoptions="$newoptions ! --tunnel-dst $val" ;;
		*)             fatal_error "Invalid option \"$option\" for zone $zone" ;;
	    esac
	done

	if [ -n "$newoptions" ]; then
	    [ -n "$POLICY_MATCH" ] || fatal_error "Your kernel and/or iptables does not support policy match"
	    eval ${zone}_is_complex=Yes
	    eval ${zone}_ipsec${1}_options=\"${newoptions# }\"
	fi
    }

    case $IPSECFILE in
	zones)
	    f=zones
	    progress_message "Setting up IPSEC..."
	    ;;
	*)
	    f=$IPSECFILE
	    strip_file $f
	    progress_message "Processing $f..."
	    using_ipsec=Yes
	    ;;
    esac

    while read zone type options in_options out_options mss; do
	expandv zone type options in_options out_options mss

	if [ -n "$using_ipsec" ]; then
	    validate_zone1 $zone || fatal_error "Unknown zone: $zone"
	fi

	if [ -n "$type" ]; then
	    if [ -n "$using_ipsec" ]; then
		case $type in
		    No|no)
			;;
		    Yes|yes)
			[ -n "$POLICY_MATCH" ] || fatal_error "Your kernel and/or iptables does not support policy match"
			eval ${zone}_is_ipsec=Yes
			eval ${zone}_is_complex=Yes
			eval ${zone}_type=ipsec4
			;;
		    *)
			fatal_error "Invalid IPSEC column contents"
			;;
		esac
	    fi

	    do_options ""     $options
	    do_options "_in"  $in_options
	    do_options "_out" $out_options
	fi

    done < $TMP_DIR/$f
}

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

#
# Validate the zone names and options in the hosts file
#
validate_hosts_file() {
   local z hosts options r interface host option port

   check_bridge_port()
   {
       list_search ${interface}:${1} $zports || zports="$zports ${interface}:${1}"
       list_search $1 $ALL_PORTS || ALL_PORTS="$ALL_PORTS $1"
   }

   while read z hosts options; do
       expandv z hosts options
       r="$z $hosts $options"
       validate_zone1 $z || startup_error "Invalid zone ($z) in record \"$r\""

       case $hosts in
	   *:*)

	       interface=${hosts%%:*}
	       iface=$(chain_base $interface)

	       list_search  $interface $ALL_INTERFACES || \
		   startup_error "Unknown interface ($interface) in record \"$r\""

	       hosts=${hosts#*:}
	       ;;
	   *)
	       startup_error "Invalid HOST(S) column contents: $hosts"
	       ;;
       esac

       eval zports=\$${z}_ports

       for host in $(separate_list $hosts); do
	   if [ -n "$BRIDGING" ]; then
	       case $host in
		   *:*)
		       known_interface ${host%:*} && \
			   startup_error "Bridged interfaces may not be defined in /etc/shorewall/interfaces: $host"
		       check_bridge_port ${host%%:*}
		       ;;
		   *.*.*)
		       ;;
		   *+|+*)
		       eval ${z}_is_complex=Yes
		       ;;
		   *)
		       known_interface $host && \
			   startup_error "Bridged interfaces may not be defined in /etc/shorewall/interfaces: $host"
		       check_bridge_port $host
		       ;;
	       esac
	   else
	       case $host in
		   *.*.*)
		       ;;
		   +*)
		       eval ${z}_is_complex=Yes
		       ;;
		   *)
		       fatal_error "BRIDGING=Yes is needed for this zone definition: $r"
		       ;;
	       esac
	   fi

	   for option in $(separate_list $options) ; do
	       case $option in
		   maclist|norfc1918|blacklist|tcpflags|nosmurfs|-)
		       ;;
		   ipsec)
			[ -n "$POLICY_MATCH" ] || \
			    startup_error "Your kernel and/or iptables does not support policy match: ipsec"
			eval ${z}_ipsec_hosts=\"\$${z}_ipsec_hosts $interface:$host\"
			eval ${z}_is_complex=Yes
			;;
		   routeback)
			eval ${z}_routeback=\"$interface:$host \$${z}_routeback\"
			;;
		   *)
			error_message "WARNING: Invalid option ($option) in record \"$r\""
			;;
	       esac
	   done
       done

       [ -n "$zports" ]  && eval ${z}_ports=\"$zports\"

   done < $TMP_DIR/hosts

   [ -n "$ALL_PORTS" ] && echo "   Bridge ports are: $ALL_PORTS"
}

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

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

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

#
# Determine the value for a parameter that defaults to Yes
#
added_param_value_yes() # $1 = Parameter Name, $2 = Parameter value
{
    local val="$2"

    if [ -z "$val" ]; then
	echo "Yes"
    else case $val in
	[Yy][Ee][Ss])
	    echo "Yes"
	    ;;
	[Nn][Oo])
	    echo ""
	    ;;
	*)
	    startup_error "Invalid value ($val) for $1"
	    ;;
	esac
    fi
}

#
# Determine the value for a parameter that defaults to No
#
added_param_value_no() # $1 = Parameter Name, $2 = Parameter value
{
    local val="$2"

    if [ -z "$val" ]; then
	echo ""
    else case $val in
	[Yy][Ee][Ss])
	    echo "Yes"
	    ;;
	[Nn][Oo])
	    echo ""
	    ;;
	*)
	    startup_error "Invalid value ($val) for $1"
	    ;;
	esac
    fi
}

#
# Initialize this program
#
do_initialize() {

    # Run all utility programs using the C locale
    #
    # Thanks to Vincent Planchenault for this tip #

    export LC_ALL=C

    # Make sure umask is sane
    umask 077

    PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
    #
    # Establish termination function
    #
    TERMINATOR=startup_error
    #
    # Clear all configuration variables
    #
    VERSION=
    IPTABLES=
    FW=
    SUBSYSLOCK=
    ALLOWRELATED=Yes
    LOGRATE=
    LOGBURST=
    LOGPARMS=
    LOGLIMIT=
    ADD_IP_ALIASES=
    ADD_SNAT_ALIASES=
    TC_ENABLED=
    BLACKLIST_DISPOSITION=
    BLACKLIST_LOGLEVEL=
    CLAMPMSS=
    ROUTE_FILTER=
    LOG_MARTIANS=
    DETECT_DNAT_IPADDRS=
    MUTEX_TIMEOUT=
    FORWARDPING=
    MACLIST_DISPOSITION=
    MACLIST_LOG_LEVEL=
    TCP_FLAGS_DISPOSITION=
    TCP_FLAGS_LOG_LEVEL=
    RFC1918_LOG_LEVEL=
    MARK_IN_FORWARD_CHAIN=
    FUNCTIONS=
    VERSION_FILE=
    LOGFORMAT=
    LOGRULENUMBERS=
    ADMINISABSENTMINDED=
    BLACKLISTNEWONLY=
    MODULE_SUFFIX=
    ACTIONS=
    USEDACTIONS=
    SMURF_LOG_LEVEL=
    DISABLE_IPV6=
    BRIDGING=
    DYNAMIC_ZONES=
    PKTTYPE=
    USEPKTYPE=
    RETAIN_ALIASES=
    DELAYBLACKLISTLOAD=
    LOGTAGONLY=
    LOGALLNEW=
    RFC1918_STRICT=
    MACLIST_TTL=
    SAVE_IPSETS=
    RESTOREFILE=
    MAPOLDACTIONS=
    IMPLICIT_CONTINUE=
    HIGH_ROUTE_MARKS=
    TC_EXPERT=

    RESTOREBASE=
    TMP_DIR=
    ALL_INTERFACES=
    ROUTEMARK_INTERFACES=
    IPSECMARK=256
    PROVIDERS=
    CRITICALHOSTS=
    IPSECFILE=
    EXCLUSION_SEQ=1
    STOPPING=
    HAVE_MUTEX=
    ALIASES_TO_ADD=
    SECTION=ESTABLISHED
    SECTIONS=
    ALL_PORTS=

    SHAREDIR=/usr/share/shorewall
    VARDIR=/var/lib/shorewall
    CONFDIR=/etc/shorewall
    
    FUNCTIONS=${SHAREDIR}/functions

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

    TMP_DIR=$(mktempdir)

    [ -n "$TMP_DIR" ] && chmod 700 $TMP_DIR || \
       startup_error "Can't create a temporary directory"

    trap "[ -n "$RESTOREBASE" ] && rm -f $RESTOREBASE;rm -rf $TMP_DIR; my_mutex_off; exit 2" 1 2 3 4 5 6 9

    ensure_config_path

    VERSION_FILE=$SHAREDIR/version

    [ -f $VERSION_FILE ] && VERSION=$(cat $VERSION_FILE)

    run_user_exit params

    config=$(find_file shorewall.conf)

    if [ -f $config ]; then
	if [ -r $config ]; then
	    progress_message "Processing $config..."
	    . $config
	else
	    startup_error "Cannot read $config (Hint: Are you root?)"
	fi
    else
	startup_error "$config does not exist!"
    fi
    #
    # Restore CONFIG_PATH if the shorewall.conf file cleared it
    #
    ensure_config_path
    #
    # Determine the capabilities of the installed iptables/netfilter
    # We load the kernel modules here to accurately determine
    # capabilities when module autoloading isn't enabled.
    #
    PKTTYPE=$(added_param_value_no PKTTYPE $PKTTYPE)

    [ -n "${MODULE_SUFFIX:=o gz ko o.gz ko.gz}" ]
    if [ -z "$EXPORT" ]; then

	load_kernel_modules

	if [ -z "$IPTABLES" ]; then
	    IPTABLES=$(mywhich iptables 2> /dev/null)

	    [ -z "$IPTABLES" ] && startup_error "Can't find iptables executable"
	else
	    [ -e "$IPTABLES" ] || startup_error "\$IPTABLES=$IPTABLES does not exist or is not executable"
	fi
	determine_capabilities

	[ -d ${VARDIR} ] || mkdir -p ${VARDIR}

    else
	f=$(find_file capabilities)

	[ -f $f ] && . $f || startup_error "The -e flag requires a capabilities file"
    fi

    ALLOWRELATED="$(added_param_value_yes ALLOWRELATED $ALLOWRELATED)"
    [ -n "$ALLOWRELATED" ] || \
	startup_error "ALLOWRELATED=No is not supported"
    ADD_IP_ALIASES="$(added_param_value_yes ADD_IP_ALIASES $ADD_IP_ALIASES)"

    if [ -n "${LOGRATE}${LOGBURST}" ]; then
	LOGLIMIT="--match limit"
	[ -n "$LOGRATE" ]  && LOGLIMIT="$LOGLIMIT --limit $LOGRATE"
	[ -n "$LOGBURST" ] && LOGLIMIT="$LOGLIMIT --limit-burst $LOGBURST"
    fi

    if [ -n "$IP_FORWARDING" ]; then
	case "$IP_FORWARDING" in
	[Oo][Nn]|[Oo][Ff][Ff]|[Kk][Ee][Ee][Pp])
	    ;;
	*)
	    startup_error "Invalid value ($IP_FORWARDING) for IP_FORWARDING"
	    ;;
	esac
    else
	IP_FORWARDING=On
    fi

   [ -n "${BLACKLIST_DISPOSITION:=DROP}" ]

    case "$CLAMPMSS" in
	[0-9]*)
	    ;;
	*)
	    CLAMPMSS=$(added_param_value_no CLAMPMSS $CLAMPMSS)
	    ;;
    esac

    ADD_SNAT_ALIASES=$(added_param_value_no ADD_SNAT_ALIASES $ADD_SNAT_ALIASES)
    ROUTE_FILTER=$(added_param_value_no ROUTE_FILTER $ROUTE_FILTER)
    LOG_MARTIANS=$(added_param_value_no LOG_MARTIANS $LOG_MARTIANS)
    DETECT_DNAT_IPADDRS=$(added_param_value_no DETECT_DNAT_IPADDRS $DETECT_DNAT_IPADDRS)
    FORWARDPING=$(added_param_value_no FORWARDPING $FORWARDPING)
    [ -n "$FORWARDPING" ] && \
	startup_error "FORWARDPING=Yes is no longer supported"

    maclist_target=reject

    if [ -n "$MACLIST_DISPOSITION" ] ; then
	case $MACLIST_DISPOSITION in
	    REJECT)
		;;
	    DROP)
		maclist_target=DROP
		;;
	    ACCEPT)
		maclist_target=RETURN
		;;
	    *)
		startup_error "Invalid value ($MACLIST_DISPOSITION) for MACLIST_DISPOSITION"
		;;
	esac
    else
	MACLIST_DISPOSITION=REJECT
    fi

    if [ -n "$TCP_FLAGS_DISPOSITION" ] ; then
	case $TCP_FLAGS_DISPOSITION in
	    REJECT|ACCEPT|DROP)
		;;
	    *)
		startup_error "Invalid value ($TCP_FLAGS_DISPOSITION) for TCP_FLAGS_DISPOSITION"
		;;
	esac
    else
	TCP_FLAGS_DISPOSITION=DROP
    fi

    [ -n "${RFC1918_LOG_LEVEL:=info}" ]

    MARK_IN_FORWARD_CHAIN=$(added_param_value_no MARK_IN_FORWARD_CHAIN $MARK_IN_FORWARD_CHAIN)
    [ -n "$MARK_IN_FORWARD_CHAIN" ] && MARKING_CHAIN=tcfor || MARKING_CHAIN=tcpre
    CLEAR_TC=$(added_param_value_yes CLEAR_TC $CLEAR_TC)

    if [ -n "$LOGFORMAT" ]; then
	if [ -n "$(echo $LOGFORMAT | grep '%d')" ]; then
	    LOGRULENUMBERS=Yes
	    temp=$(printf "$LOGFORMAT" fooxx 1 barxx 2> /dev/null)
	    if [ $? -ne 0 ]; then
		startup_error "Invalid LOGFORMAT string: \"$LOGFORMAT\""
	    fi
	else
	    temp=$(printf "$LOGFORMAT" fooxx barxx 2> /dev/null)
	    if [ $? -ne 0 ]; then
		startup_error "Invalid LOGFORMAT string: \"$LOGFORMAT\""
	    fi
	fi

	[ ${#temp} -le 29 ] || startup_error "LOGFORMAT string is longer than 29 characters: \"$LOGFORMAT\""
    else
	LOGFORMAT="Shorewall:%s:%s:"
    fi
    ADMINISABSENTMINDED=$(added_param_value_no ADMINISABSENTMINDED $ADMINISABSENTMINDED)
    BLACKLISTNEWONLY=$(added_param_value_no BLACKLISTNEWONLY $BLACKLISTNEWONLY)
    DISABLE_IPV6=$(added_param_value_no DISABLE_IPV6 $DISABLE_IPV6)
    BRIDGING=$(added_param_value_no BRIDGING $BRIDGING)
    DYNAMIC_ZONES=$(added_param_value_no DYNAMIC_ZONES $DYNAMIC_ZONES)
    STARTUP_ENABLED=$(added_param_value_yes STARTUP_ENABLED $STARTUP_ENABLED)
    RETAIN_ALIASES=$(added_param_value_no RETAIN_ALIASES $RETAIN_ALIASES)
    [ -n "${ADD_IP_ALIASES}${ADD_SNAT_ALIASES}" ] || RETAIN_ALIASES=
    DELAYBLACKLISTLOAD=$(added_param_value_no DELAYBLACKLISTLOAD $DELAYBLACKLISTLOAD)
    LOGTAGONLY=$(added_param_value_no LOGTAGONLY $LOGTAGONLY)
    RFC1918_STRICT=$(added_param_value_no RFC1918_STRICT $RFC1918_STRICT)
    SAVE_IPSETS=$(added_param_value_no SAVE_IPSETS $SAVE_IPSETS)
    MAPOLDACTIONS=$(added_param_value_yes MAPOLDACTIONS $MAPOLDACTIONS)
    FASTACCEPT=$(added_param_value_no FASTACCEPT $FASTACCEPT)
    IMPLICIT_CONTINUE=$(added_param_value_no IMPLICIT_CONTINUE $IMPLICIT_CONTINUE)
    HIGH_ROUTE_MARKS=$(added_param_value_no HIGH_ROUTE_MARKS $HIGH_ROUTE_MARKS)
    TC_EXPERT=$(added_param_value_no TC_EXPERT $TC_EXPERT)
    [ -n "$XCONNMARK_MATCH" ] || XCONNMARK=
    [ -n "$XMARK" ] || XCONNMARK=

    [ -n "$HIGH_ROUTE_MARKS" -a -z "$XCONNMARK" ] && startup_error "HIGH_ROUTE_MARKS=Yes requires extended CONNMARK target, extended CONNMARK match support and extended MARK support"

    case ${IPSECFILE:=ipsec} in
	ipsec|zones)
	    ;;
	*)
	    startup_error "Invalid value ($IPSECFILE) for IPSECFILE option"
	    ;;
    esac

    case ${MACLIST_TABLE:=filter} in
	filter)
	    ;;
	mangle)
	    [ $MACLIST_DISPOSITION = reject ] && startup_error "MACLIST_DISPOSITION=REJECT is not allowed with MACLIST_TABLE=mangle"
	    ;;	*)
	    startup_error "Invalid value ($MACLIST_TABLE) for MACLIST_TABLE option"
	    ;;
    esac

   TC_SCRIPT=

   if [ -n "$TC_ENABLED" ] ; then
	case "$TC_ENABLED" in
	    [Yy][Ee][Ss])
		TC_ENABLED=
		TC_SCRIPT=$(find_file tcstart)
		[ -f $TC_SCRIPT ] || startup_error "Unable to find tcstart file"
		;;
	    [Ii][Nn][Tt][Ee][Rr][Nn][Aa][Ll])
		TC_ENABLED=Yes
		;;
	    [Nn][Oo])
		TC_ENABLED=
		;;
	esac
    else
	TC_ENABLED=Yes
    fi

    if [ -n "$TC_ENABLED" ];then
	[ -n "$MANGLE_ENABLED" ] || startup_error "Traffic Shaping requires mangle support in your kernel and iptables"
    fi

    [ "x${SHOREWALL_DIR}" = "x." ] && SHOREWALL_DIR="$PWD"

    #
    # Strip the files that we use often
    #
    strip_file interfaces
    strip_file hosts
    #
    # Check out the user's shell
    #
    [ -n "${SHOREWALL_SHELL:=/bin/sh}" ]

    temp=$(decodeaddr 192.168.1.1)
    if [ $(encodeaddr $temp) != 192.168.1.1 ]; then
	startup_error "Shell $SHOREWALL_SHELL is broken and may not be used with Shorewall"
    fi

    if [ -z "$KLUDGEFREE" ]; then
	rm -f $TMP_DIR/physdev
	rm -f $TMP_DIR/iprange
    fi

}

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

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
