#!/bin/sh

#-----------------------------------------------------------------------
# README
#-----------------------------------------------------------------------
#
# Copyright (C) 2000-2001, Jean-Sebastien Morisset <jsmoriss@mvlan.net>.
#
# 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.
#
# 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/jsmoriss.mvlan.net/linux/rcf/>.
#
# 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 funtion 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.
#
#-----------------------------------------------------------------------
# CHANGES 
#-----------------------------------------------------------------------
#
# 2001-04-23  Jean-Sebastien Morisset <jsmoriss@mvlan.net>
#             Added support for the --configure command line parameter
#             which is an alias for --update-conf.
# 2001-04-16  Jean-Sebastien Morisset <jsmoriss@mvlan.net>
#             Used Interface_Up function to replace ifconfig commands
#             to validate interfaces. Added test for execute bit on
#             result from which command. Just in case the which binary
#             sends it's error output to stdout (bug in red hat 7.1).
# 2001-03-28  Jean-Sebastien Morisset <jsmoriss@mvlan.net>
#             Moved ToS rules to the top of output chains. Replaced all
#             "tr" command by To_Upper function.
# 2001-03-25  Jean-Sebastien Morisset <jsmoriss@mvlan.net>
#             Added aliases for command line arguments. i.e.
#             --update-config is also -sc, etc. Updated help file for
#             these new parameters. Following Dougal Holmes' suggestion,
#             I added private<->private and mz<->mz forwarding and
#             removed private<->dmz forwarding. This was redundant since
#             forwarding rules for dmz<->any/0 already existed.
# 2001-03-17  Jean-Sebastien Morisset <jsmoriss@mvlan.net>
#             Replaced or removed all "wc" commands. Replaced all "cat"
#             command by "sed -e ''". Added check for "which" binary and
#             function to call the binary directly.
#
#-----------------------------------------------------------------------
# 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"
					;;
				--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
					;;
				--*-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\"
					;;
				--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"
					;;
				*)	ARG_EXEC="$ARG_EXEC $1"
					;;
			esac    
			shift
			continue 2
		done
		break
	done
	unset ARG
}

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.

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

--[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-hostports \\
            www.battle.net/24 4000 6112:6119 

EOF
}

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


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

VERSION=5.2
readonly VERSION

sed -e '' <<EOF

Copyright (C) 2000-2001, Jean-Sebastien Morisset <jsmoriss@mvlan.net>.
rcf (aka rc.firewall) v$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/#commercial> FOR COMMERCIAL USE. THANK YOU.
--------------------------------------------------------------------------------
EOF

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

ORIGINAL_UMASK="`umask`"
ORIGINAL_PATH="$PATH"

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.
#
PATH="/sbin:/usr/sbin:/bin:/usr/bin:$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
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
}

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

#
# 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 $PUBLIC_INTERFACES $PRIVATE_INTERFACES $DMZ_INTERFACES $MZ_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

for var in IANA_RESERVED_NETWORKS
do
	Include_Group $var
done
unset var

#
# 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..."
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}\"
		fi
	done
done
unset interface_type

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

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

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

#--------------------------------------------------------------------
# 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"
ipchains -F
ipchains -X

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

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

		ipchains -N $INCHAIN
		ipchains -N $OUTCHAIN

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

if [ "$ACCEPT_ALL" != "yes" ]
then
	# deny/reject rules which are used by other {in,out}put chains
	Iana_Networks_Chains
	Private_Networks_Chains
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.
#-----------------------------------------------------------------------

#--------------------------------------------------------------------
# LOOPBACK INTERFACE
#--------------------------------------------------------------------
#
for INTERFACE in $LOOPBACK_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

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
fi

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
	# Enable IP Accounting chains
	Ip_Accounting
	
	echo ""
	echo "IPchains Firewall Rules Disabled"
	echo ""
	exit 0
fi

# Catch all rule, all other forwarding is denied.
#
echo "Reject All Other Forwarding (logged)"
ipchains -A forward -j REJECT -s $ANY -d $ANY -l

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

for INTERFACE in $PUBLIC_INTERFACES
do
	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
done

for INTERFACE in $DMZ_INTERFACES
do
	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
done

for INTERFACE in $DMZ_INTERFACES
do
	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
done

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

for INTERFACE in $PRIVATE_INTERFACES
do
	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
done

for INTERFACE in $MZ_INTERFACES
do
	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
done

for INTERFACE in $MZ_INTERFACES
do
	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
done

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

echo "Inserting Type of Service (ToS) Rules for: ftp ssh telnet www ircd"
#
# 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
	Get_Interface_Vars $INTERFACE
	if [ "$VIRTUAL" = "no" ]
	then
		ipchains -I $OUTCHAIN -p tcp -d 0/0 ftp      -t 0x01 0x10
		ipchains -I $OUTCHAIN -p tcp -d 0/0 ftp-data -t 0x01 0x08
		ipchains -I $OUTCHAIN -p tcp -d 0/0 ssh      -t 0x01 0x10   
		ipchains -I $OUTCHAIN -p tcp -d 0/0 telnet   -t 0x01 0x10   
		ipchains -I $OUTCHAIN -p tcp -d 0/0 www      -t 0x01 0x10
		ipchains -I $OUTCHAIN -p tcp -d 0/0 6667     -t 0x01 0x10	# ircd
	fi
done

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

# 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

# Allow traffic from 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
	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
done

echo "Deny/Reject All Other Input/Output (logged)"
for INTERFACE in $PRIVATE_INTERFACES $PUBLIC_INTERFACES $DMZ_INTERFACES $MZ_INTERFACES
do
	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
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 ""

