#!/bin/sh
#
#     Shorewall Packet Filtering Firewall Control Program - V2.2
#
#     This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
#     (c) 1999,2000,2001,2002,2003,2004,2005 - Tom Eastep (teastep@shorewall.net)
#
#	This file should be placed in /sbin/shorewall.
#
#	Shorewall documentation is available at http://shorewall.sourceforge.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.
#
#	The firewall uses configuration files in /etc/shorewall/ - skeleton
#	files is included with the firewall.
#
#	Commands are:
#
#          shorewall add <iface>[:<host>] zone     Adds a host or subnet to a zone
#          shorewall delete <iface>[:<host>] zone  Deletes a host or subnet from a zone
#	   shorewall start 			   Starts the firewall
#	   shorewall restart			   Restarts the firewall
#	   shorewall stop			   Stops the firewall
#	   shorewall monitor [ refresh-interval ]  Repeatedly Displays firewall status
#						   plus the last 20 "interesting"
#						   packets
#	   shorewall status			   Displays firewall status
#	   shorewall reset			   Resets iptables packet and
#						   byte counts
#	   shorewall clear			   Open the floodgates by
#						   removing all iptables rules
#						   and setting the three permanent
#						   chain policies to ACCEPT
#	   shorewall refresh			   Rebuild the common chain to
#						   compensate for a change of
#						   broadcast address on any "detect"
#						   interface.
#	   shorewall show <chain> [ <chain> ... ]  Display the rules in each <chain> listed
#	   shorewall show log			   Print the last 20 log messages
#	   shorewall show connections		   Show the kernel's connection
#						   tracking table
#	   shorewall show nat			   Display the rules in the nat table
#	   shorewall show {mangle|tos}		   Display the rules in the mangle table
#	   shorewall show tc			   Display traffic control info
#	   shorewall show classifiers		   Display classifiers
#	   shorewall version			   Display the installed version id
#	   shorewall check			   Verify the more heavily-used
#						   configuration files.
#	   shorewall try <directory> [ <timeout> ] Try a new configuration and if
#						   it doesn't work, revert to the
#						   standard one. If a timeout is supplied
#						   the command reverts back to the
#						   standard configuration after that many
#						   seconds have elapsed after successfully
#						   starting the new configuration.
#	   shorewall logwatch [ refresh-interval ] Monitor the local log for Shorewall
#						   messages.
#	   shorewall drop <address> ...		   Temporarily drop all packets from the
#						   listed address(es)
#	   shorewall reject <address> ...	   Temporarily reject all packets from the
#						   listed address(es)
#	   shorewall allow <address> ...	   Reenable address(es) previously
#						   disabled with "drop" or "reject"
#	   shorewall save [ <file> ]		   Save the list of "rejected" and
#						   "dropped" addresses so that it will
#						   be automatically reinstated the
#						   next time that Shorewall starts.
#                                                  Save the current state so that 'shorewall
#                                                  restore' can be used.
#
#          shorewall forget [ <file> ]             Discard the data saved by 'shorewall save'
#
#          shorewall restore [ <file> ]            Restore the state of the firewall from
#                                                  previously saved information.
#
#          shorewall ipaddr [ <address>/<cidr> | <address> <netmask> ]
#
#                                                  Displays information about the network
#                                                  defined by the argument[s]
#
#          shorewall iprange <address>-<address>   Decomposes a range of IP addresses into
#                                                  a list of network/host addresses.
#
# Fatal Error
#
fatal_error() # $@ = Message
{
    echo "   $@" >&2
    exit 2
}

# Display a chain if it exists
#

showfirstchain() # $1 = name of chain
{
    awk \
    'BEGIN	 {prnt=0; rslt=1; }; \
    /^$/	 { next; };\
    /^Chain/	 {if ( prnt == 1 ) { rslt=0; exit 0; }; };\
    /Chain '$1'/ { prnt=1; }; \
		 { if (prnt == 1)  print; };\
    END		 { exit rslt; }' $TMPFILE
}

showchain() # $1 = name of chain
{
    if [ "$firstchain" = "Yes" ]; then
	if showfirstchain $1; then
	    firstchain=
	fi
    else
	awk \
	'BEGIN	     {prnt=0;};\
	/^$|^ pkts/  { next; };\
	/^Chain/     {if ( prnt == 1 ) exit; };\
	/Chain '$1'/ { prnt=1; };\
		     { if (prnt == 1)  print; }' $TMPFILE
    fi
}

#
# The 'awk' hack that compensates for a bug in iptables-save (actually in libipt_policy.so) and can be removed when that bug is fixed.
#

iptablesbug()
{
    if qt which awk ; then
	awk 'BEGIN           {sline=""; };\
             /^-j/           { print sline $0; next };\
             /-m policy.*-j/ { print $0; next };\
             /-m policy/     { sline=$0; next };\
                             {print ; sline="" }'
    else
	echo "   Warning: You don't have 'awk' on this system so the output of the save command may be unusable" >&2
	cat
    fi
}

#
# Validate the value of RESTOREFILE
#
validate_restorefile() # $* = label
{
    case $RESTOREFILE in
	*/*)
	    echo "   ERROR: $@ must specify a simple file name: $RESTOREFILE" >&2
	    exit 2
	    ;;
    esac    
}

#
# Set the configuration variables from shorewall.conf
#
get_config() {

    [ -z "$LOGFILE" ] && LOGFILE=/var/log/messages

    if [ ! -f $LOGFILE ]; then
	echo "LOGFILE ($LOGFILE) does not exist!" >&2
	exit 2
    fi
    #
    # See if we have a real version of "tail" -- use separate redirection so
    # that ash (aka /bin/sh on LRP) doesn't crap
    #
    if ( tail -n5 $LOGFILE > /dev/null 2> /dev/null ) ; then
	realtail="Yes"
    else
	realtail=""
    fi

    [ -n "$FW" ] || FW=fw

    [ -n "LOGFORMAT" ] && LOGFORMAT="${LOGFORMAT%%%*}"

    [ -n "$LOGFORMAT" ] || LOGFORMAT="Shorewall:"

    if [ -n "$IPTABLES" ]; then
	if [ ! -e "$IPTABLES" ]; then
	    echo "   ERROR: The program specified in IPTABLES does not exist or is not executable" >&2
	    exit 2
	fi
    else
	IPTABLES=$(which iptables 2> /dev/null)
	if [ -z "$IPTABLES" ] ; then
	    echo "   ERROR: Can't find iptables executable" >&2
	    exit 2
	fi
    fi

    if [ -n "$SHOREWALL_SHELL" ]; then
	if [ ! -e "$SHOREWALL_SHELL" ]; then
	    echo "   ERROR: The program specified in SHOREWALL_SHELL does not exist or is not executable" >&2
	    exit 2
	fi
    fi

    [ -n "$RESTOREFILE" ] || RESTOREFILE=restore

    validate_restorefile RESTOREFILE

    export RESTOREFILE

}

#
# Clear descriptor 1 if it is a terminal
#
clear_term() {
    [ -t 1 ] && clear
}

#
# Display IPTABLES rules -- we used to store them in a variable but ash
# dies when trying to display large sets of rules
#
display_chains()
{
    trap "rm -f /tmp/chains-$$; exit 1" 1 2 3 4 5 6 9

    if [ "$haveawk" = "Yes" ]; then
	#
	# Send the output to a temporary file since ash craps if we try to store
	# the output in a variable.
	#
	TMPFILE=$(mktempfile)
	[ -n "$TMPFILE" ] || { echo "   ERROR:Cannot create temporary file" >&2; exit 1; }

	$IPTABLES -L $IPT_OPTIONS >> $TMPFILE

	clear_term
	echo "$banner $(date)"
	echo
	echo "Standard Chains"
	echo
	firstchain="Yes"
	showchain INPUT
	showchain OUTPUT
	showchain FORWARD

	timed_read

	clear_term
	echo "$banner $(date)"
	echo
	firstchain=Yes
	echo "Input Chains"
	echo

	chains=$(grep '^Chain.*_[in|fwd]' $TMPFILE | cut -d' ' -f 2)

	for chain in $chains; do
	    showchain $chain
	done

	timed_read

	for zone in $zones; do

	    if [ -n "$(grep "^Chain \.*${zone}" $TMPFILE)" ] ; then
		clear_term
		echo "$banner $(date)"
		echo
		firstchain=Yes
		eval display=\$${zone}_display
		echo "$display Chains"
		echo
		for zone1 in $FW $zones; do
		    showchain ${zone}2$zone1
		    showchain @${zone}2$zone1
		    [ "$zone" != "$zone1" ] && \
			showchain ${zone1}2${zone} && \
			showchain @${zone1}2${zone}
		done

		timed_read
	    fi
	done

	clear_term
	echo "$banner $(date)"
	echo
	firstchain=Yes
	echo "Policy Chains"
	echo
	showchain common
	showchain badpkt
	showchain icmpdef
	showchain rfc1918
	showchain blacklst
	showchain reject
	showchain newnotsyn
	for zone in $zones all; do
	    showchain ${zone}2all
	    showchain @${zone}2all
	    [ "$zone" = "all" ] || { showchain all2${zone}; showchain @all2${zone}; }
	done

	timed_read

	clear_term
	echo "$banner $(date)"
	echo
	firstchain=Yes
	echo "Dynamic Chain"
	echo
	showchain dynamic
	timed_read

	qt rm -f $TMPFILE
    else
	$IPTABLES -L -n -v
	timed_read
    fi
    trap - 1 2 3 4 5 6 9

}

#
# Delay $timeout seconds -- if we're running on a recent bash2 then allow
# <enter> to terminate the delay
#
timed_read ()
{
    read -t $timeout foo 2> /dev/null

    test $? -eq 2 && sleep $timeout
}

#
# Display the last $1 packets logged
#
packet_log() # $1 = number of messages
{
    local options

    [ -n "$realtail" ] && options="-n$1"

    grep "${LOGFORMAT}" $LOGFILE | \
	 sed s/" kernel:"// | \
	 sed s/" $host $LOGFORMAT"/" "/ | \
	 sed 's/MAC=.* SRC=/SRC=/' | \
	 tail $options
}

#
# Show traffic control information
#
show_tc() {

    show_one_tc() {
	local device=${1%@*}
	qdisc=$(tc qdisc list dev $device)

	if [ -n "$qdisc" ]; then
	    echo Device $device:
	    tc -s -d qdisc show dev $device
	    tc -s -d class show dev $device
	    echo
	fi
    }

    ip link list | \
    while read inx interface details; do
	case $inx in
	[0-9]*)
	    show_one_tc ${interface%:}
	    ;;
	*)
	    ;;
	esac
    done

}

#
# Show classifier information
#
show_classifiers() {

    show_one_classifier() {
	local device=${1%@*}
	qdisc=$(tc qdisc list dev $device)

	if [ -n "$qdisc" ]; then
	    echo Device $device:
	    tc -s filter ls dev $device
	    echo
	fi
    }

    ip link list | \
    while read inx interface details; do
	case $inx in
	[0-9]*)
	    show_one_classifier ${interface%:}
	    ;;
	*)
	    ;;
	esac
    done

}
#
# Monitor the Firewall
#
monitor_firewall() # $1 = timeout -- if negative, prompt each time that
		   #		     an 'interesting' packet count changes
{

    host=$(echo $HOSTNAME | sed 's/\..*$//')
    oldrejects=$($IPTABLES -L -v -n | grep 'LOG')

    if [ $1 -lt 0 ]; then
	let "timeout=- $1"
	pause="Yes"
    else
	pause="No"
	timeout=$1
    fi


    if qt which awk; then
	TMP_DIR=$(mktempdir)
	[ -n "$TMP_DIR" ] || { echo "   ERROR:Cannot create temporary directory" >&2; exit 1; }
	haveawk=Yes
	determine_zones
	rm -rf $TMP_DIR
    else
	haveawk=
    fi

    while true; do
	display_chains

	clear_term
	echo "$banner $(date)"
	echo

	echo "Dropped/Rejected Packet Log"
	echo

	show_reset

	rejects=$($IPTABLES -L -v -n | grep 'LOG')

	if [ "$rejects" != "$oldrejects" ]; then
	    oldrejects="$rejects"

	    $RING_BELL

	    packet_log 20

	    if [ "$pause" = "Yes" ]; then
		echo
		echo $ECHO_N 'Enter any character to continue: '
		read foo
	    else
		timed_read
	    fi
	else
	    echo
	    packet_log 20
	    timed_read
	fi

	clear_term
	echo "$banner $(date)"
	echo
	echo "NAT Status"
	echo
	$IPTABLES -t nat -L $IPT_OPTIONS
	timed_read

	clear_term
	echo "$banner $(date)"
	echo
	echo
	echo "TOS/MARK Status"
	echo
	$IPTABLES -t mangle -L $IPT_OPTIONS
	timed_read

	clear_term
	echo "$banner $(date)"
	echo
	echo
	echo "Tracked Connections"
	echo
	cat /proc/net/ip_conntrack
	timed_read

	clear_term
	echo "$banner $(date)"
	echo
	echo
	echo "Traffic Shaping/Control"
	echo
	show_tc
	timed_read

	clear_term
	echo "$banner $(date)"
	echo
	echo
	echo "Packet Classifiers"
	echo
	show_classifiers
	timed_read
    done
}

#
# Watch the Firewall Log
#
logwatch() # $1 = timeout -- if negative, prompt each time that
	   #		     an 'interesting' packet count changes
{

    host=$(echo $HOSTNAME | sed 's/\..*$//')
    oldrejects=$($IPTABLES -L -v -n | grep 'LOG')

    if [ $1 -lt 0 ]; then
	timeout=$((- $1))
	pause="Yes"
    else
	pause="No"
	timeout=$1
    fi

    qt which awk && haveawk=Yes || haveawk=

    while true; do
	clear_term
	echo "$banner $(date)"
	echo

	echo "Dropped/Rejected Packet Log"
	echo

	show_reset

	rejects=$($IPTABLES -L -v -n | grep 'LOG')

	if [ "$rejects" != "$oldrejects" ]; then
	    oldrejects="$rejects"

	    $RING_BELL

	    packet_log 40

	    if [ "$pause" = "Yes" ]; then
		echo
		echo $ECHO_N 'Enter any character to continue: '
		read foo
	    else
		timed_read
	    fi
	else
	    echo
	    packet_log 40
	    timed_read
	fi
    done
}

#
# Help information
#
help()
{
    [ -x $HELP ] && { export version; exec $HELP $*; }
    echo "Help subsystem is not installed at $HELP"
}
 
#
# Give Usage Information
#
usage() # $1 = exit status
{
    echo "Usage: $(basename $0) [debug|trace] [nolock] [-c <directory>] [ -x ] [ -q ] [ -f ] <command>"
    echo "where <command> is one of:"
    echo "   add <interface>[:{<bridge-port>[:<host>]|<host>}[,...]] ... <zone>"
    echo "   allow <address> ..."
    echo "   check [ <directory> ]"
    echo "   clear"
    echo "   delete <interface>[:{<bridge-port>[:<host>]|<host>}[,...]] ... <zone>"
    echo "   drop <address> ..."
    echo "   forget [ <file name> ]"
    echo "   help [ <command > | host | address ]"
    echo "   hits"
    echo "   ipcalc [ <address>/<vlsm> | <address> <netmask> ]"
    echo "   iprange <address>-<address>"
    echo "   logwatch [<refresh interval>]"
    echo "   monitor [<refresh interval>]"
    echo "   refresh"
    echo "   reject <address> ..."
    echo "   reset"
    echo "   restart [ <directory> ]"
    echo "   restore [ <file name> ]"
    echo "   save [ <file name> ]"
    echo "   show [<chain> [ <chain> ... ]|classifiers|connections|log|nat|tc|tos|zones]"
    echo "   start [ <directory> ]"
    echo "   stop"
    echo "   status"
    echo "   try <directory> [ <timeout> ]"
    echo "   version"
    echo 
    echo "The -c and -f options may not be specified with a <directory> in the start, restart and check commands"
    exit $1
}

#
# Display the time that the counters were last reset
#
show_reset() {
    [ -f $STATEDIR/restarted ] && \
	echo "Counters reset $(cat $STATEDIR/restarted)" && \
	echo
}
#
# Display's the passed file name followed by "=" and the file's contents.
#
show_proc() # $1 = name of a file  
{
    [ -f $1 ] && echo "   $1 = $(cat $1)"
}

#
# Execution begins here
#
debugging=

if [ $# -gt 0 ] && [ "$1" = "debug" -o "$1" = "trace" ]; then
    debugging=debug
    shift
fi

nolock=

if [ $# -gt 0 ] && [ "$1" = "nolock" ]; then
    nolock=nolock
    shift
fi

SHOREWALL_DIR=
QUIET=
IPT_OPTIONS="-nv"
FAST=

done=0

while [ $done -eq 0 ]; do
    [ $# -eq 0 ] && usage 1
    option=$1
    case $option in
	-*)
	    option=${option#-}

	    [ -z "$option" ] && usage 1
	    
	    while [ -n "$option" ]; do
		case $option in
		    c)
			[ $# -eq 1 ] && usage 1

			if [ ! -d $2 ]; then
			    if [ -e $2 ]; then
				echo "$2 is not a directory" >&2 && exit 2
			    else
				echo "Directory $2 does not exist" >&2 && exit 2
			    fi
			fi
			
			SHOREWALL_DIR=$2
			option=
			shift
			;;
		    x*)
			IPT_OPTIONS="-xnv"
			option=${option#x}
			;;
		    q*)
			QUIET=Yes
			option=${option#q}
			;;
		    f*)
			FAST=Yes
			option=${option#f}
			;;
		    *)
			usage 1
			;;
		esac
	    done
	    shift
	    ;;
	*)
	    done=1
            ;;
    esac
done

if [ $# -eq 0 ]; then
    usage 1
fi

[ -n "$SHOREWALL_DIR" ] && export SHOREWALL_DIR
[ -n "$QUIET" ]         && export QUIET

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin
MUTEX_TIMEOUT=

SHARED_DIR=/usr/share/shorewall
FIREWALL=$SHARED_DIR/firewall
FUNCTIONS=$SHARED_DIR/functions
VERSION_FILE=$SHARED_DIR/version
HELP=$SHARED_DIR/help

if [ -f $FUNCTIONS ]; then
    . $FUNCTIONS
else
    echo "$FUNCTIONS does not exist!" >&2
    exit 2
fi

ensure_config_path

config=$(find_file shorewall.conf)

if [ -f $config ]; then
    if [ -r $config ]; then
	. $config
    else
	echo "Cannot read $config! (Hint: Are you root?)" >&2
	exit 1
    fi
else
    echo "$config does not exist!" >&2
    exit 2
fi

ensure_config_path

export CONFIG_PATH

get_config

[ -z "${STATEDIR}" ] && STATEDIR=/var/state/shorewall

if [ ! -f $FIREWALL ]; then
    echo "ERROR: Shorewall is not properly installed"
    if [ -L $FIREWALL ]; then
	echo "	     $FIREWALL is a symbolic link to a"
	echo "	     non-existant file"
    else
	echo "	     The file $FIREWALL  does not exist"
    fi

    exit 2
fi

if [ -f $VERSION_FILE ]; then
    version=$(cat $VERSION_FILE)
else
    echo "ERROR: Shorewall is not properly installed"
    echo "	 The file $VERSION_FILE does not exist"
    exit 1
fi

banner="Shorewall-$version Status at $HOSTNAME -"

case $(echo -e) in
    -e*)
	RING_BELL="echo \a"
	;;
    *)
	RING_BELL="echo -e \a"
	;;
esac

case $(echo -n "Testing") in
    -n*)
	ECHO_N=
	;;
    *)
	ECHO_N=-n
	;;
esac

case "$1" in
    start)
	case $# in
	    1)
		;;
	    2)
		[ -n "$SHOREWALL_DIR" -o -n "$FAST" ] && usage 2

		if [ ! -d $2 ]; then
		    if [ -e $2 ]; then
			echo "$2 is not a directory" >&2 && exit 2
		    else
			echo "Directory $2 does not exist" >&2 && exit 2
		    fi
		fi
			
		SHOREWALL_DIR=$2
		export SHOREWALL_DIR
		;;
	    *)
		usage 1
		;;
	esac
	
	if [ -n "$FAST" ]; then
	    
	    RESTOREPATH=/var/lib/shorewall/$RESTOREFILE

	    if [ -x $RESTOREPATH ]; then
		echo Restoring Shorewall...
		$RESTOREPATH
		date > $STATEDIR/restarted
		echo Shorewall restored from $RESTOREPATH
	    else
		exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock start
	    fi
        else
	    exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock start
	fi
	;;
    stop|reset|clear|refresh)
	[ $# -ne 1 ] && usage 1
	exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock $1
	;;
    check|restart)
	case $# in
	    1)
		;;
	    2)
		[ -n "$SHOREWALL_DIR" ] && usage 2

		if [ ! -d $2 ]; then
		    if [ -e $2 ]; then
			echo "$2 is not a directory" >&2 && exit 2
		    else
			echo "Directory $2 does not exist" >&2 && exit 2
		    fi
		fi
			
		SHOREWALL_DIR=$2
		export SHOREWALL_DIR
		;;
	    *)
		usage 1
		;;
	esac
	exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock $1
	;;
    add|delete)
	[ $# -lt 3 ] && usage 1
	exec $SHOREWALL_SHELL $FIREWALL $debugging $nolock $@
	;;
    show|list)
    	[ -n "$debugging" ] && set -x
	case "$2" in
	connections)
	    [ $# -gt 2 ] && usage 1
	    echo "Shorewall-$version Connections at $HOSTNAME - $(date)"
	    echo
	    cat /proc/net/ip_conntrack
	    ;;
	nat)
	    [ $# -gt 2 ] && usage 1
	    echo "Shorewall-$version NAT at $HOSTNAME - $(date)"
	    echo
	    show_reset
	    $IPTABLES -t nat -L $IPT_OPTIONS
	    ;;
	tos|mangle)
	    [ $# -gt 2 ] && usage 1
	    echo "Shorewall-$version TOS at $HOSTNAME - $(date)"
	    echo
	    show_reset
	    $IPTABLES -t mangle -L $IPT_OPTIONS
	    ;;
	log)
	    [ $# -gt 2 ] && usage 1
	    echo "Shorewall-$version Log at $HOSTNAME - $(date)"
	    echo
	    show_reset
	    host=$(echo $HOSTNAME | sed 's/\..*$//')
	    packet_log 20
	    ;;
	tc)
	    [ $# -gt 2 ] && usage 1
	    echo "Shorewall-$version Traffic Control at $HOSTNAME - $(date)"
	    echo
	    show_tc
	    ;;
	classifiers)
	    [ $# -gt 2 ] && usage 1
	    echo "Shorewall-$version Clasifiers at $HOSTNAME - $(date)"
	    echo
	    show_classifiers
	    ;;
	zones)
	    [ $# -gt 2 ] && usage 1
	    [ -z "${STATEDIR}" ] && STATEDIR=/var/state/shorewall
	    if [ -f $STATEDIR/zones ]; then
		echo "Shorewall-$version Zones at $HOSTNAME - $(date)"
		echo
		while read zone hosts; do
		    echo $zone
		    for host in $hosts; do
			echo "   $host"
		    done
		done < $STATEDIR/zones
		echo
	    else
		echo "   ERROR: $STATEDIR/zones does not exist" >&2
		exit 1
	    fi
	    ;;
	*)
	    shift
	    
	    echo "Shorewall-$version $([ $# -gt 1 ] && echo Chains || echo Chain) $* at $HOSTNAME - $(date)"
	    echo
	    show_reset
	    if [ $# -gt 0 ]; then
		for chain in $*; do
		    $IPTABLES -L $chain $IPT_OPTIONS
		done
	    else
		$IPTABLES -L $IPT_OPTIONS
	    fi
	    ;;
	esac
	;;
    monitor)
    	[ -n "$debugging" ] && set -x
	if [ $# -eq 2 ]; then
	    monitor_firewall $2
	elif [ $# -eq 1 ]; then
	    monitor_firewall 30
	else
	    usage 1
	fi
	;;
    status)
    	[ -n "$debugging" ] && set -x
	[ $# -eq 1 ] || usage 1
	clear_term
	echo "Shorewall-$version Status at $HOSTNAME - $(date)"
	echo
	show_reset
	host=$(echo $HOSTNAME | sed 's/\..*$//')
	$IPTABLES -L $IPT_OPTIONS
	echo
	packet_log 20
	echo
	echo "NAT Table"
	echo
	$IPTABLES -t nat -L $IPT_OPTIONS
	echo
	echo "Mangle Table"
	echo
	$IPTABLES -t mangle -L $IPT_OPTIONS
	echo
	cat /proc/net/ip_conntrack
	echo
	echo "IP Configuration"
	echo
	ip addr ls
	echo
	echo "IP Stats"
	echo
	ip -stat link ls
	
	if qt which brctl; then
	    echo
	    echo "Bridges"
	    echo
	    brctl show
	fi

	echo
	echo "/proc"
	echo

	show_proc /proc/sys/net/ipv4/ip_forward
	show_proc /proc/sys/net/ipv4/icmp_echo_ignore_all

	for directory in /proc/sys/net/ipv4/conf/*; do
	    for file in proxy_arp arp_filter rp_filter log_martians; do
		show_proc $directory/$file
	    done
	done

	if [ -n "$(ip rule ls)" ]; then
	    echo
	    echo "Routing Rules"
	    echo
	    ip rule ls
	    ip rule ls | while read rule; do
		table=${rule##* }
		echo
		echo "Table $table:"
		echo
		ip route ls table $table
	    done
	else
	    echo
	    echo "Routing Table"
	    echo
	    ip route ls
	fi

	echo
	echo "ARP"
	echo
	arp -na
	
	if qt which lsmod; then
	    echo
	    echo "Modules"
	    echo
	    lsmod | grep -E '^ip_|^ipt_'
	fi
	;;
    hits)
    	[ -n "$debugging" ] && set -x
	[ $# -eq 1 ] || usage 1
	clear_term
	echo "Shorewall-$version Hits at $HOSTNAME - $(date)"
	echo

	timeout=30

	if [ $(grep -c "$LOGFORMAT" $LOGFILE ) -gt 0 ] ; then
	    echo "   HITS IP		  DATE"
	    echo "   ---- --------------- ------"
	    grep "$LOGFORMAT" $LOGFILE | sed 's/\(.\{6\}\)\(.*SRC=\)\(.*\)\( DST=.*\)/\3	\1/' | sort | uniq -c | sort -rn
	    echo ""

	    echo "   HITS IP		  PORT"
	    echo "   ---- --------------- -----"
	    grep "$LOGFORMAT" $LOGFILE | sed 's/\(.*SRC=\)\(.*\)\( DST=.*DPT=\)\([0-9]\{1,5\}\)\(.*\)/\2	\4/
						t
						s/\(.*SRC=\)\(.*\)\( DST=.*\)/\2/' | sort | uniq -c | sort -rn
	    echo ""

	    echo "   HITS DATE"
	    echo "   ---- ------"
	    grep "$LOGFORMAT" $LOGFILE | sed 's/\(.\{6\}\)\(.*\)/\1/' | sort | uniq -c | sort -rn
	    echo ""

	    echo "   HITS  PORT SERVICE(S)"
	    echo "   ---- ----- ----------"
	    grep "$LOGFORMAT.*DPT" $LOGFILE | sed 's/\(.*DPT=\)\([0-9]\{1,5\}\)\(.*\)/\2/' | sort | uniq -c | sort -rn | \
	    while read count port ; do
		# List all services defined for the given port
		srv=$(grep "^[^#].*\\b$port/" /etc/services | cut -f 1 | sort -u)
		srv=$(echo $srv | sed 's/ /,/g')

		if [ -n "$srv" ] ; then
		    printf '%7d %5d %s\n' $count $port $srv
		else
		    printf '%7d %5d\n' $count $port
		fi
	    done
	fi
	;;
    version)
	echo $version
	;;
    try)
	[ -n "$SHOREWALL_DIR" ] && startup_error "Error: -c option may not be used with \"try\""
	[ $# -lt 2 -o $# -gt 3 ] && usage 1
	if ! $0 $debugging -c $2 restart; then
	    if ! $IPTABLES -L shorewall > /dev/null 2> /dev/null; then
		$0 start
	    fi
	elif ! $IPTABLES -L shorewall > /dev/null 2> /dev/null; then
	    $0 start
	elif [ $# -eq 3 ]; then
	    sleep $3
	    $0 restart
	fi
	;;
    logwatch)
    	[ -n "$debugging" ] && set -x
	if [ $# -eq 2 ]; then
	    logwatch $2
	elif [ $# -eq 1 ]; then
	    logwatch 30
	else
	    usage 1
	fi
	;;
    drop)
    	[ -n "$debugging" ] && set -x
	[ $# -eq 1 ] && usage 1
	mutex_on
	while [ $# -gt 1 ]; do
	    shift
	    qt $IPTABLES -D dynamic -s $1 -j reject
	    qt $IPTABLES -D dynamic -s $1 -j DROP
	    $IPTABLES -A dynamic -s $1 -j DROP || break 1
	    echo "$1 Dropped"
	done
	mutex_off
	;;
    reject)
    	[ -n "$debugging" ] && set -x
	[ $# -eq 1 ] && usage 1
	mutex_on
	while [ $# -gt 1 ]; do
	    shift
	    qt $IPTABLES -D dynamic -s $1 -j reject
	    qt $IPTABLES -D dynamic -s $1 -j DROP
	    $IPTABLES -A dynamic -s $1 -j reject || break 1
	    echo "$1 Rejected"
	done
	mutex_off
	;;
    allow)
    	[ -n "$debugging" ] && set -x
	[ $# -eq 1 ] && usage 1
	mutex_on
	while [ $# -gt 1 ]; do
	    shift
	    if	qt $IPTABLES -D dynamic -s $1 -j reject || qt $IPTABLES -D dynamic -s $1 -j DROP; then
		echo "$1 Allowed"
	    else
		echo "$1 Not Dropped or Rejected"
	    fi
	done
	mutex_off
	;;
    save)
    	[ -n "$debugging" ] && set -x

	case $# in
	1)
	    ;;
	2)
	    RESTOREFILE="$2"
	    validate_restorefile '<restore file>'
	    ;;
	*)
	    usage 1
	    ;;
	esac

	RESTOREPATH=/var/lib/shorewall/$RESTOREFILE

	mutex_on

	if qt $IPTABLES -L shorewall -n; then
	    [ -d /var/lib/shorewall ] || mkdir -p /var/lib/shorewall

	    if [ -f $RESTOREPATH -a ! -x $RESTOREPATH ]; then
		echo "   ERROR: $RESTOREPATH exists and is not a saved Shorewall configuration"
	    else
		case $RESTOREFILE in
		    save|restore-base)
			echo "   ERROR: Reserved file name: $RESTOREFILE"
			;;
		    *)
			if $IPTABLES -L dynamic -n > /var/lib/shorewall/save; then
			    echo "   Dynamic Rules Saved"
			    if [ -f /var/lib/shorewall/restore-base ]; then
				cp -f /var/lib/shorewall/restore-base /var/lib/shorewall/restore-$$
				if iptables-save | iptablesbug >> /var/lib/shorewall/restore-$$ ; then
				    echo __EOF__ >> /var/lib/shorewall/restore-$$
				    [ -f /var/lib/shorewall/restore-tail ] && \
					cat /var/lib/shorewall/restore-tail >> /var/lib/shorewall/restore-$$
				    mv -f /var/lib/shorewall/restore-$$ $RESTOREPATH 
				    chmod +x $RESTOREPATH
				    echo "   Currently-running Configuration Saved to $RESTOREPATH"
				else
				    rm -f /var/lib/shorewall/restore-$$
				    echo "   ERROR: Currently-running Configuration Not Saved"
				fi
			    else
				echo "   ERROR: /var/lib/shorewall/restore-base does not exist"
			    fi
			else
			    echo "Error Saving the Dynamic Rules"
			fi
			;;
		esac
	    fi
	else
	    echo "Shorewall isn't started"
	fi
	mutex_off
	;;
    forget)
	case $# in
	1)
	    ;;
	2)
	    RESTOREFILE="$2"
	    validate_restorefile '<restore file>'
	    ;;
	*)
	    usage 1
	    ;;
	esac


	RESTOREPATH=/var/lib/shorewall/$RESTOREFILE

	if [ -x $RESTOREPATH ]; then
	    rm -f $RESTOREPATH
	    echo "    $RESTOREPATH removed"
	elif [ -f $RESTOREPATH ]; then
	    echo "   $RESTOREPATH exists and is not a saved Shorewall configuration"
	fi
	;;
    ipcalc)
    	[ -n "$debugging" ] && set -x
	if [ $# -eq 2 ]; then
	    address=${2%/*}
	    vlsm=${2#*/}
	elif [ $# -eq 3 ]; then
	    address=$2
	    vlsm=$(ip_vlsm $3)
	else
	    usage 1
	fi
	
	[ -z "$vlsm" ] && exit 2
	[ "x$address" = "x$vlsm" ] && usage 2
	[ $vlsm -gt 32 ] && echo "Invalid VLSM: /$vlsm" >&2 && exit 2

	address=$address/$vlsm

	                                   echo "   CIDR=$address"
	temp=$(ip_netmask $address);       echo "   NETMASK=$(encodeaddr $temp)"
	temp=$(ip_network $address);       echo "   NETWORK=$temp"
	temp=$(broadcastaddress $address); echo "   BROADCAST=$temp"
	;;

    iprange)
    	[ -n "$debugging" ] && set -x
	case $2 in
	    *.*.*.*-*.*.*.*)
		ip_range $2
		;;
	    *)
		usage 1
		;;
	esac
	;;
    restore)
	case $# in
	1)
	    ;;
	2)
	    RESTOREFILE="$2"
	    validate_restorefile '<restore file>'
	    ;;
	*)
	    usage 1
	    ;;
	esac

	RESTOREPATH=/var/lib/shorewall/$RESTOREFILE

	if [ -x $RESTOREPATH ]; then
	    echo Restoring Shorewall...
	    $RESTOREPATH && echo "Shorewall restored from /var/lib/shorewall/$RESTOREFILE"
	else
	    echo "File /var/lib/shorewall/$RESTOREFILE: file not found"
            exit 2
        fi
        ;;
    call) 
    	[ -n "$debugging" ] && set -x
	#
	# Undocumented way to call functions in /usr/share/shorewall/functions directly
	#
	shift;
	$@
	;;
    help)
	shift
	[ $# -ne 1 ] && usage 1
	help $@
	;;
    *)
	usage 1
	;;

esac
