#!/bin/sh

#-----------------------------------------------------------------------
# Copyright (C) 2000-2001, Jean-Sebastien Morisset <jsmoriss@mvlan.net>
#-----------------------------------------------------------------------
# $Id: rcf,v 1.17 2001/12/15 13:08:36 edwin Exp $
#-----------------------------------------------------------------------
#  $RCSfile: rcf,v $
#   $Author: edwin $
# $Revision: 1.17 $
#     $Date: 2001/12/15 13:08:36 $
#   $Locker:  $ 
#     $Name:  $
#-----------------------------------------------------------------------
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by 
# the Free Software Foundation <http://www.gnu.org/copyleft/gpl.txt>.
# 
# 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 (LICENSE file in archive) for more details.
#
#-----------------------------------------------------------------------
# README
#-----------------------------------------------------------------------
#
# See the accompanying INSTALL file for installation notes. The file is
# also available online at <http://rcf.mvlan.net/dist/INSTALL>. Updates
# are available from <http://rcf.mvlan.net/> or 
# <ftp://ftp.axess.com/mirrors/rcf.mvlan.net/>.
#
# I created this script using several sources, one of which is David A. 
# Ranch's excellent Linux configuration docs available at
# <http://www.ecst.csuchico.edu/~dranch/LINUX/index-linux.html#trinityos>
# Linux Firewalls by Robert L. Ziegler from New Riders Publishing (ISBN: 
# 0-7357-0900-9) is another essential reference for any Linux Firewall 
# administrator.
#
#-----------------------------------------------------------------------
# CONVENTIONS
#-----------------------------------------------------------------------
#
# Function names should have the first letter of every word capitalized.
# Upper case variables are meant to exist throughout the script and 
# associated functions. Lower case variables should be used only within
# a function or block. They are temporary variables and should be unset
# before exiting the function or block.
#
# This code was written in vim with a tabstop of 4, line numbers, and
# a screen width of 132 collumns.
#
#-----------------------------------------------------------------------
# FUNCTIONS
#-----------------------------------------------------------------------

# Determine the path to the which binary. Some linux installations
# use a built-in which by default which doesn't behave as expected.
# If the which binary is missing, the Security_Check.sh function will
# catch it.
#
WHICH_BIN="`which which 2>/dev/null`"
which () { 
	which_result="`$WHICH_BIN $* 2>/dev/null`"
	[ -x "$which_result" ] && echo "$which_result"
	unset which_result
}

To_Upper () { sed -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'; }
To_Lower () { sed -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'; }

To_Upper_Fix () { sed -e 'y/abcdefghijklmnopqrstuvwxyz:-/ABCDEFGHIJKLMNOPQRSTUVWXYZ__/'; }
To_Lower_Fix () { sed -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ_/abcdefghijklmnopqrstuvwxyz-/'; }

Read_Command_Line () {
	# ARG_EXEC variable accumulates unrecognized parameters
	# which can be passed-on to an executable.
	#   
	ARG_ORIG="$*"
	while : 
	do  
		for ARG in $*
		do
			case $ARG in 
				--help|-h)
					Show_Help
					exit 0
					;;
				--conf|--config|-c)
					CONF="$2"
					readonly CONF
					shift
					;;
				--prefix|-p)
					CMDL_PREFIX="$2"
					shift
					;;
				--groups|-g)
					CMDL_GROUP_DIR="$2"
					shift
					;;
				--modules|-m)
					CMDL_MODULE_DIR="$2"
					shift
					;;
				--functions|-f)
					CMDL_FUNCTION_DIR="$2"
					shift
					;;
				--update-conf*|-uc|--configure)
					UPDATE_CONFIG="yes"
					;;
				--compress-conf*|-cc)
					COMPRESS_CONFIG="yes"
					;;
				--show-conf*|-sc)
					SHOW_CONFIG="yes"
					readonly SHOW_CONFIG
					;;
				--mode)
					CMDL_PUBLIC_INTERFACES_SECURITY="$2"
					shift
					;;
				--strict|--relaxed|--paranoid)
					CMDL_PUBLIC_INTERFACES_SECURITY="`echo $1|sed -e 's/^--//'`"
					;;
				--accept-all|--allow-all|-aa)
					ACCEPT_ALL="yes"
					readonly ACCEPT_ALL
					;;
				--test|-t)
					TEST="yes"

					insmod () { echo "	insmod $*" >>/dev/stderr; }
					ipchains () { echo "	ipchains $*" >>/dev/stderr; }
					ipmasqadm () { echo "	ipmasqadm $*" >>/dev/stderr; }
					;;
				--debug|-d)
					while [ "$2" -a "`echo $2|grep -v '^-'`" ]
					do
						[ "$CMDL_DEBUG" ] && CMDL_DEBUG="$CMDL_DEBUG $2" || CMDL_DEBUG="$2"
						shift
					done
					: ${CMDL_DEBUG:="yes"}
					;;
				--nodebug|-nd)
					CMDL_DEBUG="no"	# needs a negetive to over-ride a positive in config. file
					;;
				--nosecurity-file-check|-nsfc)
					SECURITY_FILE_CHECK="no"
					;;
				--refresh-interfaces|-ri)
					while [ "$2" -a "`echo $2|grep -v '^-'`" ]
					do
						[ "$REFRESH_INTERFACES" ] && \
							REFRESH_INTERFACES="$REFRESH_INTERFACES `echo $2|To_Lower`" || \
							REFRESH_INTERFACES="`echo $2|To_Lower`"
						shift
					done
					;;
				--*-interfaces|--*-*-security|--*-*-*-*)
					VALUE=""

					# Change variable name to upper case, etc.
					VAR="`echo $1|sed -e 's/^--//'|To_Upper_Fix`"

					# Keep accumulating the value of this variable until you 
					# reach another parameter or you reach the end.
					while [ "$2" -a "`echo $2|grep -v '^-'`" ]
					do
						[ "$VALUE" ] && VALUE="$VALUE $2" || VALUE="$2"
						shift
					done
					eval CMDL_$VAR=\"$VALUE\"
					;;
				*)	[ "$ARG_EXEC" ] && ARG_EXEC="$ARG_EXEC $1" || ARG_EXEC="$1"
					;;
			esac    
			shift
			continue 2
		done
		break
	done
	unset ARG

	if [ "$ARG_EXEC" ]
	then
		echo ""
		echo "Unknown Command Line Option(s) \"$ARG_EXEC\". Exiting..."
		echo ""
		exit 1
	fi
}

Show_Help () {
	sed -e '' <<EOF

Command Line Parameters:

-h | --help

    This summary of options. Several man pages are also available:
    rcf-groups(5), rcf-modules(5), firewall.conf(5), and rcf(8).

-c {config-file} | --conf {config-file}

    Specify an alternate path for the configuration file instead of
    /etc/firewall.conf.

    Examples:
        rcf -c /etc/firewall/daytime.conf
        rcf -c /etc/firewall/offhours.conf

-p {prefix-dir} | --prefix {prefix-dir}

    Specify an alternate base directory instead of the default
    /etc/firewall/. Using the --functions, --groups, or --modules 
	parameter will over-ride this option for that specific sub-
	directory.

-f {functions-dir} | --functions {functions-dir}

    Specify an alternate base directory instead of the default
    /etc/firewall/functions/

-g {groups-dir} | --groups {groups-dir}

    Specify an alternate base directory instead of the default
    /etc/firewall/groups/

-m {module-dir} | --modules {module-dir}

    Specify an alternate base directory instead of the default
    /etc/firewall/modules/

-uc | --update-conf

    Update the configuration file with all interface-based options. This
    is essential when upgrading to a new version of rcf.

-cc | --compress-conf

    Compress the configuration file by removing all blank/comment lines
    and all unused configuration options (to increase the speed of loading).
    Before configuring new options, use -uc to restore all options.

-sc | --show-conf

    Displays a summary of options and configuration values.

-t | --test

    Show the commands which would be executed by rc.firewall. The
    ipchains rules are not changed.

-d | --debug | --debug [yes|no] | -nd | --nodebug

    Turn debugging ON or OFF, over-riding the configured value. Debug
    mode will log as many input/output/masquerade deny/accept packets
    as possible to the kernel.info syslogd category.

-nsfc | --nosecurity-file-check

    When verifying the system security, don't check file permissions and
    owners.

-ri | --refresh-interfaces

    Refresh the ipchains rules for these interfaces only.

--[public|private|dmz|mz]-interfaces {interface} {...}

    Add network interface(s) to the configured set. The configuration
    file options for these interfaces are not created automatically. If
    you want to implement rules for these interfaces, you'll have to use
    additional command line options.

    Example:
        rcf --public-interfaces eth3 --accept-eth3-telnet-clients any/0

--private-interfaces-security [open|relaxed|strict|paranoid]
--[public|dmz|mz]-interfaces-security [relaxed|strict|paranoid]
--[dmz|mz]-clusters-security [relaxed|strict|paranoid]

    "open" mode allows all standard TCP traffic to pass. Broadcast based
    services (dhcp, etc.) will have to be given explicit access. This mode
    can only be used on private interfaces.

    "relaxed" mode allows outgoing TCP connections and incoming/outgoing
    UDP traffic on high ports. When used for public interfaces, this mode
    will allow port-scanning of remote hosts from your firewall. Access
    must be granted for each service offered (web and ftp servers, etc.).

    "strict" mode allows outgoing TCP connections except to known hacker-
    friendly service ports (sunrpc, rlogin, telnet, etc.). All UDP traffic
    is denied. Access must be given for each UDP based service and local
    TCP services offered to remote users (web and ftp servers, etc.). This
    is usually the prefered mode for public and dmz interfaces/clusters.

    "paranoid" mode denies all incoming/outgoing TCP and UDP traffic.
    Each service (local and remote) must be granted access.

    MAKE SURE YOU EXECUTE RCF WITH THE --update-conf PARAMETER AFTER 
    SWITCHING MODES. This will add/remove several options to/from the
    configuration file.

-aa | --accept-all

    Set the default ipchains policy to accept, flush all firewall rules,
    and remove all chains. This effectively disables the firewall.

--{action}-{interface}-{service}-{type} {option-value} {...}

    Add a temporary entry to a configuration option; Useful when you
    want to open-up a service "on the fly". These settings will be lost 
    the next time the firewall is executed.

    Examples:
        rcf --accept-eth1-telnet-clients somehost.com
        rcf --accept-eth1:1-battlenet-hosts www.battle.net/24 

EOF
}

# ======================================================================
# END OF FUNCTIONS
# ======================================================================


# ======================================================================
# MAIN
# ======================================================================

PROJECT_VERSION=5.2.1
PROJECT_MATURITY=s
PROJECT_RELEASE=1
PKG_VERSION="${PROJECT_VERSION}${PROJECT_MATURITY}${PROJECT_RELEASE}"
readonly PROJECT_VERSION PROJECT_MATURITY PROJECT_RELEASE PKG_VERSION

sed -e '' <<EOF

Copyright (C) 2000-2001, Jean-Sebastien Morisset <jsmoriss@mvlan.net>.
rcf (aka rc.firewall) v${PKG_VERSION} and all related scripts come with ABSOLUTELY NO
WARRANTY; for details see the LICENSE file included in the rcf distribution.
--------------------------------------------------------------------------------
 PLEASE SEE <http://rcf.mvlan.net/> FOR PRIVATE AND COMMERCIAL USE. THANK YOU.
--------------------------------------------------------------------------------
EOF

#
# SYSTEM VARIABLES, ETC.
# ----------------------

ORIGINAL_UMASK="`umask`"
umask 077

ANY="any/0"
readonly ANY

LANG=en_EN
export LANG

# Make sure we have all the standard OS paths available. I also prefer
# using the standard OS utilities instead of what we might find under 
# /usr/local/bin.
#
ORIGINAL_PATH="$PATH"
PATH_PREFIX="/sbin:/usr/sbin:/bin:/usr/bin"
PATH="${PATH_PREFIX}:${PATH}"
export PATH

SECURITY_FILE_CHECK="yes"
ERROR_PAUSE="10"	# in seconds.
TOTAL_MODULES="0"	# will be incremented as modules are read

CONF="/etc/firewall.conf"			# Over-ride w/ command line parameter.
PREFIX="/etc/firewall"				# Over-ride w/ command line parameter.

#
# PARSE COMMAND LINE PARAMETERS
# -----------------------------

Read_Command_Line $*

#
# SET FILE LOCATION VARIABLES
# ---------------------------

[ "$CMDL_PREFIX" ] \
	&& { PREFIX="$CMDL_PREFIX"; unset CMDL_PREFIX; }

[ "$CMDL_GROUP_DIR" ] \
	&& { GROUP_DIR="$CMDL_GROUP_DIR"; unset CMDL_GROUP_DIR; } \
	|| { GROUP_DIR="${PREFIX}/groups"; }

[ "$CMDL_MODULE_DIR" ] \
	&& { MODULE_DIR="$CMDL_MODULE_DIR"; unset CMDL_MODULE_DIR; } \
	|| { MODULE_DIR="${PREFIX}/modules"; }

[ "$CMDL_FUNCTION_DIR" ] \
	&& { FUNCTION_DIR="$CMDL_FUNCTION_DIR"; unset CMDL_FUNCTION_DIR; } \
	|| { FUNCTION_DIR="${PREFIX}/functions"; }

#
# READ-IN FUNCTIONS
# -----------------

[ -d "$FUNCTION_DIR" ] && \
	for FUNCTION in $FUNCTION_DIR/*.sh
	do
		[ -f "$FUNCTION" ] && . "$FUNCTION"
	done

echo ""
echo "SECURITY CHECK"
echo "================================================================================"
#
# Call the Security_Check function AFTER the CONF variable has been 
# defined. The idea is not to call any binaries before we can check 
# the environment.
#
Security_Check

echo ""
echo "RCF CONFIGURATION"
echo "================================================================================"

#
# READ CONFIGURATION FILE
# -----------------------

Check_Old_Config_Paths
Contrib_Migration
Read_Config

#
# VALIDATE CONFIGURATION VARIABLES
# --------------------------------
# These changes will be saved when the configuration file is written.

Verify_All_Yn_Variables

#
# WRITE CONFIGURATION FILE
# ------------------------

[ "$UPDATE_CONFIG" ] && {
	#
	# DEFINE VARIABLES FOR CLUSTER NAMES ETC.
	# These are used when writing the configuration file,
	# so they must be defined before.
	#
	Set_Cluster_Vars DMZ MZ
	Set_Mode_Num
	Write_Config
	exit 0
}

if [ "$COMPRESS_CONFIG" = "yes" ]
then
	Compress_Config
	exit 0
fi

#
# MANIPULATE CONFIGURATION VARIABLES
# ----------------------------------
# This goes on after the configuration file is written so we don't save
# these changes (removing downed interfaces, add hosts to variables, etc.).

LOOPBACK_INTERFACES="lo"
readonly LOOPBACK_INTERFACES

for VAR in `set|sed -n -e 's/^CMDL_\(.*\)=.*$/\1/p'`
do
	case $VAR in
		DEBUG|ENABLE_*)
			eval Set_Yn_Variable CMDL_${VAR}
			echo -n "Setting $VAR to \""
			eval echo -n "\$CMDL_$VAR"
			echo "\"."
			eval $VAR=\"\$CMDL_$VAR\"
			;;
		*_INTERFACES_SECURITY|*_CLUSTERS_SECURITY)
			echo -n "Setting $VAR to \""
			eval echo -n "\$CMDL_$VAR"
			echo "\"."
			eval $VAR=\"\$CMDL_$VAR\"
			;;
		*)
			echo -n "Adding \""
			eval echo -n "\$CMDL_$VAR"
			echo "\" to $VAR"
			eval $VAR=\"\$$VAR \$CMDL_$VAR\"
			;;
	esac
	eval unset \$CMDL_$VAR
done

#
# VALIDATE INTERFACES
# -------------------
# Remove interfaces which are down (ppp0, etc.) and/or don't have an IP 
# address. The firewall script can then be executed when the server is 
# disconnected.
#
echo "Validating Interfaces..."

ALL_INTERFACES="$LOOPBACK_INTERFACES $PUBLIC_INTERFACES $PRIVATE_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES"

for interface_type in PUBLIC PRIVATE DMZ MZ
do
	eval INTERFACES=\"\$${interface_type}_INTERFACES\"
	for INTERFACE in $INTERFACES
	do
		if [ -z "`Interface_Up $INTERFACE`" ]
		then
			echo -n "Removing $INTERFACE (down) Interface from "
			eval echo \"${interface_type}_INTERFACES\"
			eval VALUE=\"\$${interface_type}_INTERFACES\"
			VALUE="`echo $VALUE|sed -e \"s/^${INTERFACE}$//g;s/^${INTERFACE} //g;s/ ${INTERFACE} / /g;s/ ${INTERFACE}$//g\"`"
			eval ${interface_type}_INTERFACES=\"${VALUE}\"
			ALL_INTERFACES="`echo $ALL_INTERFACES|sed -e \"s/^${INTERFACE}$//g;s/^${INTERFACE} //g;s/ ${INTERFACE} / /g;s/ ${INTERFACE}$//g\"`"
		fi
	done
done
unset interface_type

#
# CHECK REFRESH_INTERFACES OPERATION
# ----------------------------------

if [ "$REFRESH_INTERFACES" ]
then
	no_interface_chains=""
	for interface in $ALL_INTERFACES
	do
		Get_Interface_Vars $interface
		[ "$VIRTUAL" = "no" ] && \
			[ ! "`Match_Interface REFRESH $interface`" ] && \
				[ ! "`/sbin/ipchains -n -L ${interface}i 2>/dev/null | grep -i $interface 2>/dev/null`" ] && \
					no_interface_chains="yes"
	done
	echo ""
	if [ ! "$no_interface_chains" ]
	then
		all_interfaces=""
		for interface in $ALL_INTERFACES
		do
			device="`echo $interface|sed -n -e 's/^\([^\:]*\).*\$/\1/p'`"
			[ "`Match_Interface REFRESH $device`" ] && \
				all_interfaces="${all_interfaces}${interface} "
		done
		ALL_INTERFACES="$all_interfaces"
	else
		REFRESH_INTERFACES=""
	fi
	unset interface no_interface_chains all_interfaces device
fi

#
# READ-IN GROUP FILES
# -------------------
#
echo "Scanning Options for Group Filenames..."

##for var in PUBLIC_INTERFACES PRIVATE_INTERFACES DMZ_INTERFACES MZ_INTERFACES
##do
##	Include_Group $var
##done
##unset var

for interface in $ALL_INTERFACES
do
	interface="`echo ${interface}|To_Upper_Fix`"
	for action in ACCEPT IGNORE DENY FORWARD
	do
		for var in `set|sed -n -e "s/^\(${action}_${interface}_[^\=]*\)=[^\=]*\$/\1/p"`
		do
			Include_Group $var
		done
	done
done
unset interface action var

if [ ! "$REFRESH_INTERFACES" ]
then
	for var in IANA_RESERVED_NETWORKS
	do
		Include_Group $var
	done
	unset var
fi

#
# SET INTERFACE VARIABLES
# -----------------------
#
echo "Setting Up Internal Variables..."

# Define variables for cluster names etc.
#
Set_Cluster_Vars DMZ MZ

# Define variable arrays based on interface names.
#
Set_Interface_Vars LOOPBACK PUBLIC PRIVATE DMZ MZ

#
# OTHER CONFIG DEPENDENT VARIABLES
# --------------------------------

Set_Mode_Num

if [ "$DEBUG" = "yes" ]
then
	LOG="-l"			# used in most ipchains commands
	LOG_MSG="(logged)"		# added to most echo'ed strings
	readonly LOG LOG_MSG
fi

#
# FIXED VARIABLES
# ---------------

BROADCAST_SRC="0.0.0.0"
BROADCAST_DEST="255.255.255.255"
readonly BROADCAST_SRC BROADCAST_DEST

PRIVPORTS="0:1023"
UNPRIVPORTS="1024:65535"
XWINPORTS="6000:6063"
##SSHPORTS="513:1023"
SSHPORTS="513:65535"
readonly PRIVPORTS UNPRIVPORTS XWINPORTS SSHPORTS

CLASS_A_PRIVATE="10.0.0.0/8"
CLASS_B_PRIVATE="172.16.0.0/12"
CLASS_C_PRIVATE="192.168.0.0/16"
CLASS_D_MULTICAST="224.0.0.0/4"
CLASS_E_RESERVED_NET="240.0.0.0/5"
readonly CLASS_A_PRIVATE CLASS_B_PRIVATE CLASS_C_PRIVATE CLASS_D_MULTICAST CLASS_E_RESERVED_NET

if [ "$SHOW_CONFIG" = "yes" ]
then
	Show_Config
	exit 0
fi

#
# START OF FIREWALL RULES
# -----------------------

echo ""
echo "BASIC OPERATING SYSTEM SETUP"
echo "================================================================================"

[ ! "$REFRESH_INTERFACES" ] && Ipv4_Check

for INTERFACE in $PUBLIC_INTERFACES $DMZ_INTERFACES
do
	if [ "`Option_Value accept $INTERFACE mcastigmp servers`" ]
	then
		if [ ! "`netstat -nre|grep \"224.0.0.0  *0.0.0.0  *240.0.0.0 .* $INTERFACE\"`" ]
		then
			echo "$INTERFACE: Adding Multicast (224.0.0.0) Route"

			if [ "`which ip 2>/dev/null`" ]
			then
				route_command="ip route add 224.0.0.0/4 dev $INTERFACE"
			elif [ "`which route 2>/dev/null`" ]
			then
				route_command="route add -net 224.0.0.0 netmask 240.0.0.0 dev $INTERFACE"
			else
				route_command=":"
			fi

			[ "$TEST" ] && echo "	$route_command" >>/dev/stderr || $route_command
		fi
	fi
done

if [ ! "$REFRESH_INTERFACES" ]
then
	echo -n "Loading Masquerading Module(s):"
	[ "$TEST" ] && echo ""
	for MODULE in $MASQ_MODULES
	do
		if [ ! "`lsmod|grep \"^ip_masq_${MODULE} \"`" ]
		then
			[ "$TEST" ] && echo "${MODULE}:" || echo -n " $MODULE"
			insmod ip_masq_$MODULE
		fi
	done
	[ "$TEST" ] || echo ""

	if [ "`echo $MASQ_TIMEOUTS|grep '^[0-9][0-9]*  *[0-9][0-9]*  *[0-9][0-9]*$'`" ]
	then
		echo "Changing IP Masquerading Timeouts"
		ipchains -M -S $MASQ_TIMEOUTS
	else
		echo "WARNING: ipmasq-timeouts option set incorrectly!"
	fi
fi

#--------------------------------------------------------------------
# Default Policies
#--------------------------------------------------------------------

if [ "$ACCEPT_ALL" = "yes" ]
then
	echo "Setting Default Policies (input/output/forward=ACCEPT)"
	ipchains -P input ACCEPT
	ipchains -P output ACCEPT
	ipchains -P forward ACCEPT
else
	echo "Setting Default Policies (input=DENY, output/forward=REJECT)"
	ipchains -P input DENY
	ipchains -P output REJECT
	ipchains -P forward REJECT
fi

# Save IP Accounting data before we flush all the chains.
#
if [ -x ${IPAC_BINDIR}/fetchipac ]
then
	echo "Saving IP Accounting Data"
	if [ "$TEST" ]
	then
		echo "	( umask $ORIGINAL_UMASK; ${IPAC_BINDIR}/fetchipac 2>/dev/null; )" >>/dev/stderr
	else
		( umask $ORIGINAL_UMASK; ${IPAC_BINDIR}/fetchipac 2>/dev/null; )
	fi
fi

echo "Removing Chains and Flushing Rules"

if [ ! "$REFRESH_INTERFACES" ]
then
	# Flush all chains and delete user defined chains
	ipchains -F
	ipchains -X
else
	# Flush input, output, forward
	ipchains -F input
	ipchains -F output
	ipchains -F forward

	# Delete jump chains in other interfaces
	for INTERFACE in $PUBLIC_INTERFACES $PRIVATE_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES
	do
		Get_Interface_Vars $INTERFACE
		if [ "$VIRTUAL" = "no" ]
		then
			for interface in $REFRESH_INTERFACES
			do
				chain_nums="`/sbin/ipchains -n -L $INCHAIN 2>/dev/null | grep -v '^Chain' | grep -v '^target' | sed -n \"/${interface}i/=\" | sort -nr`"
				for chain_num in $chain_nums
				do
					ipchains -D $INCHAIN $chain_num
				done
			done
		fi
	done
	unset chain_num chain_nums interface

	# Remove REFRESH_INTERFACES chains
	for INTERFACE in $REFRESH_INTERFACES
	do
		Get_Interface_Vars $INTERFACE
		if [ "$VIRTUAL" = "no" ]
		then
			eval  INCHAIN="${INTERFACE}i"
			eval OUTCHAIN="${INTERFACE}o"
			ipchains -F $INCHAIN
			ipchains -F $OUTCHAIN
			ipchains -X $INCHAIN
			ipchains -X $OUTCHAIN
		fi
	done
fi

#--------------------------------------------------------------------
# Setup Each Interface Chain
#--------------------------------------------------------------------

for INTERFACE in $LOOPBACK_INTERFACES $PUBLIC_INTERFACES $PRIVATE_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES
do
	Get_Interface_Vars "$INTERFACE"
	if [ "$VIRTUAL" = "no" ]
	then
		echo "Creating and Linking ${INCHAIN}/${OUTCHAIN} Chains"

		if [ "`Match_Interface ALL $INTERFACE`" ]
		then
			ipchains -N $INCHAIN
			ipchains -N $OUTCHAIN
		fi

		ipchains -A input  -i $DEVICE -j $INCHAIN
		ipchains -A output -i $DEVICE -j $OUTCHAIN
	fi
done

echo ""
echo "CREATING STATIC CHAINS"
echo "==============================================================================="

# deny/reject rules which are used by other {in,out}put chains

if [ "$ACCEPT_ALL" != "yes" ]
then
	if [ ! "$REFRESH_INTERFACES" ]
	then
		# deny/reject rules which are used by other {in,out}put chains
		Iana_Networks_Chains
	fi

	# These functions need only be called if one of the ALL interfaces is 
	# PUBLIC or DMZ, or if REFRESH_INTERFACES contains a down interface.

	singledown=""
	
	for INTERFACE in $REFRESH_INTERFACES
	do
		[ ! "`Match_Interface ALL $INTERFACE`" ] && \
			singledown="${singledown}${INTERFACE}"
	done

        public="$singledown"
        dmzs="$singledown"

	for INTERFACE in $ALL_INTERFACES
	do
		dmz=`Match_Interface DMZ $INTERFACE`
                public="${public}${dmz}`Match_Interface PUBLIC $INTERFACE`"
                dmzs="${dmzs}${dmz}"
	done

	[ "$public" ] && { Private_Net_Chains; Private_Net_Spoofing_Chains; }
	[ "$dmzs" ] && DMZ_Networks_Chains
	unset singledown public dmz dmzs
fi

#-----------------------------------------------------------------------
# A NOTE ABOUT ARROWS
#-----------------------------------------------------------------------
# You'll notice that I use arrows when printing messages on the screen.
# Typically they might look like this:
#
#   Accept interface IP# ServiceName <- hostname
#
# You might wonder what that arrow means. :-) The one pointing to the 
# left means the host is typically the one that initiates a connection.
# An arrow pointing to the right means the firewall initiates the 
# connection. It's not perfect, but it gives a sense about how traffic
# flows.
#-----------------------------------------------------------------------

echo ""
echo "FORWARDING AND MASQUERADING"
echo "================================================================================"

# There are TWO methods of turning on this feature. The first method is
# the Red Hat way. Edit the /etc/sysconfig/network file and change the 
# "FORWARD_IPV4" line to say FORWARD_IPV4=true. The second method is 
# shown below and can executed at any time while the system is running.
#
if [ -w /proc/sys/net/ipv4/ip_forward ]
then
	if [ "`Sed_Cat /proc/sys/net/ipv4/ip_forward`" -ne "1" ]
	then
		echo "Enabling IP Forwarding in Linux Kernel"
		[ "$TEST" ] \
			&& echo "	echo \"1\" >/proc/sys/net/ipv4/ip_forward" >>/dev/stderr \
			|| echo "1" >/proc/sys/net/ipv4/ip_forward
	fi
else
	echo "WARNING: Please enable IP Forwarding in your kernel!"
fi

#
# MASQUERADING
# ------------

# Masquerade all private (LAN) traffic going out on a public interface.
# Traffic going out on DMZ interfaces is NOT masqueraded (private IPs
# should be routable on the firewall).
#
for interface in $PUBLIC_INTERFACES $PRIVATE_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES
do
	Get_Interface_Vars $interface
	if [ "$VIRTUAL" = "no" ]
	then
		for masq_network in `Option_Value forward $interface masq networks`
		do
			echo "Forward $interface IP Masquerade $masq_network -> $ANY $LOG_MSG"
			ipchains -A forward -j MASQ -i $interface -s $masq_network -d $ANY $LOG
		done
	fi
done
unset interface masq_network

#
# FORWARDING
# ----------

Forward_Interfaces "$PRIVATE_INTERFACES" "$PRIVATE_INTERFACES"
Forward_Interfaces "$PRIVATE_INTERFACES" "$MZ_INTERFACES"
Forward_Interfaces "$MZ_INTERFACES" "$MZ_INTERFACES"

for FIRST_INTERFACE in $DMZ_INTERFACES
do
	Get_Interface_Vars $FIRST_INTERFACE FIRST
	[ "$FIRST_VIRTUAL" = "no" ] \
		&& for FIRST_NET in `Device_Subnets FIRST`
		do
			Forward_Subnets "${FIRST_NET}<->any/0"
		done
done

#--------------------------------------------------------------------
# PORT FORWARDING
#--------------------------------------------------------------------

# Flush the PORT FORWADING rules before setting them up
if [ -x "`which ipmasqadm 2>/dev/null`" ]
then
	echo "Flushing Port Forwarding Rules"
	ipmasqadm portfw -f
fi

Port_Forwarding_Rules PUBLIC PRIVATE DMZ MZ

if [ ! "$ACCEPT_ALL" = "yes" ]
then
	# Catch all rule, all other forwarding is denied.
	#
	echo "Reject All Other Forwarding (logged)"
	ipchains -A forward -j REJECT -s $ANY -d $ANY -l
fi

#--------------------------------------------------------------------
# LOOPBACK INTERFACE
#--------------------------------------------------------------------

for INTERFACE in $LOOPBACK_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars "$INTERFACE"
		if [ "$VIRTUAL" = "no" ]
		then
			echo ""
			echo "`echo $INTOPT|To_Upper` LOOPBACK INTERFACE SERVICES"
			echo "================================================================================"
			echo "Accept $INTERFACE $ANY <-> $ANY $LOG_MSG"
			ipchains -A $INCHAIN  -j ACCEPT -i $INTERFACE -s $ANY -d $ANY $LOG
			ipchains -A $OUTCHAIN -j ACCEPT -i $INTERFACE -s $ANY -d $ANY $LOG
		fi
	fi
done

if [ "$ACCEPT_ALL" = "yes" ]
then
	for INTERFACE in $PUBLIC_INTERFACES $PRIVATE_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES
	do
		Get_Interface_Vars "$INTERFACE"
		if [ "$VIRTUAL" = "no" ]
		then
			echo "Accept $INTERFACE $ANY <-> $ANY $LOG_MSG"
			ipchains -A $INCHAIN  -j ACCEPT -s $ANY -d $ANY $LOG
			ipchains -A $OUTCHAIN -j ACCEPT -s $ANY -d $ANY $LOG
		fi
	done

	# Enable IP Accounting chains
	Ip_Accounting
	
	echo ""
	echo "IPchains Firewall Rules Disabled"
	echo ""
	exit 0
fi

#--------------------------------------------------------------------
# PUBLIC INTERFACES AND DMZS
#--------------------------------------------------------------------

for INTERFACE in $PUBLIC_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		echo ""
		echo "`echo $INTOPT|To_Upper` PUBLIC INTERFACE SERVICES"
		echo "================================================================================"
		Service_Rules public $PUBLIC_INTERFACES_MODE
		High_Port_Rules public $PUBLIC_INTERFACES_MODE
	fi
done

for INTERFACE in $DMZ_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		echo ""
		echo "`echo $INTOPT|To_Upper` DMZ INTERFACE SERVICES"
		echo "================================================================================"
		Service_Rules public $DMZ_INTERFACES_MODE
		High_Port_Rules public $DMZ_INTERFACES_MODE
	fi
done

for INTERFACE in $DMZ_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		NETADDR="${NETWORK}/${NETMASK}"
		for CLUSTER_NAME in `Option_Value dmz $INTERFACE cluster names`
		do
			INTOPT="${INTERFACE}:${CLUSTER_NAME}"
			echo ""
			echo "`echo $INTOPT|To_Upper` DMZ CLUSTER SERVICES"
			echo "================================================================================"
			for cluster_network in `Option_Value dmz $INTERFACE $CLUSTER_NAME cluster`
			do
				Get_Cluster_Vars $INTERFACE $cluster_network
				Service_Rules public $DMZ_CLUSTERS_MODE
				High_Port_Rules public $DMZ_CLUSTERS_MODE
			done
			unset cluster_network
		done
		unset CLUSTER_NAME
	fi
done

#--------------------------------------------------------------------
# PRIVATE INTERFACES
#--------------------------------------------------------------------

for INTERFACE in $PRIVATE_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		echo ""
		echo "`echo $INTOPT|To_Upper` PRIVATE INTERFACE SERVICES"
		echo "================================================================================"
		Service_Rules private $PRIVATE_INTERFACES_MODE
		High_Port_Rules private $PRIVATE_INTERFACES_MODE
	fi
done

for INTERFACE in $MZ_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		echo ""
		echo "`echo $INTOPT|To_Upper` MZ INTERFACE SERVICES"
		echo "================================================================================"
		Service_Rules private $MZ_INTERFACES_MODE
		High_Port_Rules private $MZ_INTERFACES_MODE
	fi
done

for INTERFACE in $MZ_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		NETADDR="${NETWORK}/${NETMASK}"
		for CLUSTER_NAME in `Option_Value mz $INTERFACE cluster names`
		do
			INTOPT="${INTERFACE}:${CLUSTER_NAME}"
			echo ""
			echo "`echo $INTOPT|To_Upper` MZ CLUSTER SERVICES"
			echo "================================================================================"
			for cluster_network in `Option_Value mz $INTERFACE $CLUSTER_NAME CLUSTER`
			do
				Get_Cluster_Vars $INTERFACE $cluster_network
				Service_Rules private $MZ_CLUSTERS_MODE
				High_Port_Rules private $MZ_CLUSTERS_MODE
			done
			unset cluster_network
		done
		unset CLUSTER_NAME
	fi
done

echo ""
echo "ALL INTERFACES"
echo "================================================================================"

# In theory, you can tell the Internet how to handle your traffic, be 
# it sensitive to delay, throughput, etc.
#
#       -t 0x01 0x10 = Minimum Delay
#       -t 0x01 0x08 = Maximum Throughput
#       -t 0x01 0x04 = Maximum Reliability
#       -t 0x01 0x02 = Minimum Cost
#
# NOTE: To make use of these flags, you must compile your Linux kernel
# with CONFIG_IP_ROUTE_TOS=y.
#
for INTERFACE in $PUBLIC_INTERFACES $PRIVATE_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		if [ "$VIRTUAL" = "no" ]
		then
			echo "Inserting TOS Mask $INTOPT/tcp -> any/0 ftp ssh telnet www ircd"
			ipchains -I $OUTCHAIN -p tcp -d any/0 ftp      -t 0x01 0x10
			ipchains -I $OUTCHAIN -p tcp -d any/0 ftp-data -t 0x01 0x08
			ipchains -I $OUTCHAIN -p tcp -d any/0 ssh      -t 0x01 0x10   
			ipchains -I $OUTCHAIN -p tcp -d any/0 telnet   -t 0x01 0x10   
			ipchains -I $OUTCHAIN -p tcp -d any/0 www      -t 0x01 0x10
			ipchains -I $OUTCHAIN -p tcp -d any/0 6667     -t 0x01 0x10	# ircd
		fi
	fi
done

for INTERFACE in $PUBLIC_INTERFACES $PRIVATE_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		Mark_Hostports local tcp `Option_Value mark $INTOPT tcp rhostlports`
	fi
done

# When packets come in on an interface, but their destination is the IP
# of another interface, send the packet to the destination interface's 
# input chain. This prevents one interface's input rules from over-
# riding the access of another.
#
Cross_Interface_Access

#--------------------------------------------------------------------
# Catch All Rule
#--------------------------------------------------------------------

# Allow traffic from private subnets to go anywhere. The interface has already
# been protected by specific deny/reject rules for the interface IP.
# This is kind of like an "allservers" cluster.
#
for INTERFACE in $PRIVATE_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE

		# This open's up pretty much everything except broadcast-based services like DHCP.
		# This must be executed AFTER all virtual interface rules have been setup.
		#
		if [ "$VIRTUAL" = "no" ]
		then
			for net in `Device_Subnets`
			do
				echo "Accept $DEVICE $ANY <-> $net $LOG_MSG"
				ipchains -A $INCHAIN  -j ACCEPT -s $net -d $ANY $LOG
				ipchains -A $OUTCHAIN -j ACCEPT -s $ANY -d $net $LOG
			done
			unset net
		fi
	fi
done

echo "Deny/Reject All Other Input/Output (logged)"
for INTERFACE in $PRIVATE_INTERFACES $PUBLIC_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES
do
	if [ "`Match_Interface ALL $INTERFACE`" ]
	then
		Get_Interface_Vars $INTERFACE
		if [ "$VIRTUAL" = "no" ]
		then
			ipchains -A $INCHAIN  -j DENY   -s $ANY -d $ANY -l
			ipchains -A $OUTCHAIN -j REJECT -s $ANY -d $ANY -l
		fi
	fi
done

# Enable IP Accounting chains
Ip_Accounting

echo ""
if [ "$TEST" ]
then
	echo "IPchains Firewall Rules Displayed"
	echo "THESE RULES HAVE NOT BEEN IMPLEMENTED!"
else
	TOTAL_RULES="`ipchains -L -n|grep -v '^Chain  *'|grep -v '^target  *'|Sed_Wc`"
	echo "$TOTAL_RULES IPchains Firewall Rules Implemented"
	unset TOTAL_RULES
fi
echo ""

