shorewall_code/Shorewall/firewall
2002-09-14 23:40:46 +00:00

3683 lines
101 KiB
Bash
Executable File

#!/bin/sh
RCDLINKS="2,S41 3,S41 6,K41"
#
# The Shoreline Firewall (Shorewall) Packet Filtering Firewall - V1.3 6/14/2002
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 1999,2000,2001,2002 - Tom Eastep (teastep@shorewall.net)
#
# On most distributions, this file should be called:
# /etc/rc.d/init.d/shorewall or /etc/init.d/shorewall
#
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# If an error occurs while starting or restarting the firewall, the
# firewall is automatically stopped.
#
# Commands are:
#
# shorewall start Starts the firewall
# shorewall restart Restarts the firewall
# shorewall stop Stops the firewall
# shorewall status Displays firewall status
# shorewall reset Resets iptabless packet and
# byte counts
# shorewall clear Remove all Shorewall chains
# and rules/policies.
# shorewall refresh . Rebuild the common chain
# shorewall check Verify the more heavily-used
# configuration files.
#### BEGIN INIT INFO
# Provides: shorewall
# Required-Start: $network
# Required-Stop:
# Default-Start: 2 3 5
# Default-Stop: 0 1 6
# Description: starts and stops the shorewall firewall
### END INIT INFO
# chkconfig: 2345 25 90
# description: Packet filtering firewall
#
###############################################################################
# Search a list looking for a match -- returns zero if a match found #
# 1 otherwise #
###############################################################################
list_search() # $1 = element to search for , $2-$n = list
{
local e=$1
while [ $# -gt 1 ]; do
shift
[ "x$e" = "x$1" ] && return 0
done
return 1
}
###############################################################################
# Function to count list elements #
###############################################################################
list_count() {
local temp=`separate_list $1`
echo $temp | wc -w
}
###############################################################################
# Mutual exclusion -- These functions are jackets for the mutual exclusion #
# routines in /var/lib/shorewall/functions. They invoke #
# the corresponding function in that file if the user did #
# not specify "nolock" on the runline. #
###############################################################################
my_mutex_on() {
[ -n "$nolock" ] || { mutex_on; have_mutex=Yes; }
}
my_mutex_off() {
[ -n "$have_mutex" ] && { mutex_off; have_mutex=; }
}
###############################################################################
# Message to stderr #
###############################################################################
error_message() # $* = Error Message
{
echo " $@" >&2
}
###############################################################################
# Fatal error -- stops the firewall after issuing the error message #
###############################################################################
fatal_error() # $* = Error Message
{
echo " $@" >&2
stop_firewall
exit 2
}
###############################################################################
# Fatal error during startup -- generate an error message and abend with #
# altering the state of the firewall #
###############################################################################
startup_error() # $* = Error Message
{
echo " $@" >&2
my_mutex_off
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
kill $$
exit 2
}
###############################################################################
# Send a message to STDOUT and the System Log #
###############################################################################
report () { # $* = message
echo "$@"
logger "$@"
}
###############################################################################
# Perform variable substitution on the passed argument and echo the result #
###############################################################################
expand() # $1 = contents of variable which may be the name of another variable
{
eval echo \"$1\"
}
###############################################################################
# Perform variable substitition on the values of the passed list of variables #
###############################################################################
expandv() # $* = list of variable names
{
local varval
while [ $# -gt 0 ]; do
eval varval=\$${1}
eval $1=\"$varval\"
shift
done
}
################################################################################
# Run iptables and if an error occurs, stop the firewall and quit #
################################################################################
run_iptables() {
if ! iptables `echo $@ | sed 's/!/! /g'`; then
[ -z "$stopping" ] && { stop_firewall; exit 2; }
fi
}
################################################################################
# Run ip and if an error occurs, stop the firewall and quit #
################################################################################
run_ip() {
if ! ip $@ ; then
[ -z "$stopping" ] && { stop_firewall; exit 2; }
fi
}
################################################################################
# Run arp and if an error occurs, stop the firewall and quit #
################################################################################
run_arp() {
if ! arp $@ ; then
[ -z "$stopping" ] && { stop_firewall; exit 2; }
fi
}
################################################################################
# Run tc and if an error occurs, stop the firewall and quit #
################################################################################
run_tc() {
if ! tc $@ ; then
[ -z "$stopping" ] && { stop_firewall; exit 2; }
fi
}
################################################################################
# Create a filter chain #
# #
# If the chain isn't one of the common chains then add a rule to the chain #
# allowing packets that are part of an established connection. Create a #
# variable ${1}_exists and set its value to Yes to indicate that the chain now #
# exists. #
################################################################################
createchain() # $1 = chain name, $2 = If non-null, don't create default rules
{
local target
run_iptables -N $1
if [ $# -eq 1 ]; then
state="ESTABLISHED"
[ -n "$ALLOWRELATED" ] && state="$state,RELATED"
run_iptables -A $1 -m state --state $state -j ACCEPT
[ -z "$NEWNOTSYN" ] && \
run_iptables -A $1 -m state --state NEW -p tcp !--syn -j newnotsyn
fi
eval ${1}_exists=Yes
}
################################################################################
# Determine if a chain exists #
# #
# When we create a chain "chain", we create a variable named chain_exists and #
# set its value to Yes. This function tests for the "_exists" variable #
# corresponding to the passed chain having the value of "Yes". #
################################################################################
havechain() # $1 = name of chain
{
eval test \"\$${1}_exists\" = Yes
}
################################################################################
# Ensure that a chain exists (create it if it doesn't) #
################################################################################
ensurechain() # $1 = chain name
{
havechain $1 || createchain $1
}
################################################################################
# Add a rule to a chain creating the chain if necessary #
################################################################################
addrule() # $1 = chain name, remainder of arguments specify the rule
{
ensurechain $1
run_iptables -A $@
}
################################################################################
# Create a nat chain #
# #
# Create a variable ${1}_nat_exists and set its value to Yes to indicate that #
# the chain now exists. #
################################################################################
createnatchain() # $1 = chain name
{
run_iptables -t nat -N $1
eval ${1}_nat_exists=Yes
}
################################################################################
# Determine if a nat chain exists #
# #
# When we create a chain "chain", we create a variable named chain_nat_exists #
# and set its value to Yes. This function tests for the "_exists" variable #
# corresponding to the passed chain having the value of "Yes". #
################################################################################
havenatchain() # $1 = name of chain
{
eval test \"\$${1}_nat_exists\" = Yes
}
################################################################################
# Ensure that a chain exists (create it if it doesn't) #
################################################################################
ensurenatchain() # $1 = chain name
{
havenatchain $1 || createnatchain $1
}
################################################################################
# Add a rule to a nat chain creating the chain if necessary #
################################################################################
addnatrule() # $1 = chain name, remainder of arguments specify the rule
{
ensurenatchain $1
run_iptables -t nat -A $@
}
################################################################################
# Delete a chain if it exists #
################################################################################
deletechain() # $1 = name of chain
{
qt iptables -L $1 -n && qt iptables -F $1 && qt iptables -X $1
}
################################################################################
# Set a standard chain's policy #
################################################################################
setpolicy() # $1 = name of chain, $2 = policy
{
run_iptables -P $1 $2
}
################################################################################
# Set a standard chain to enable established connections #
################################################################################
setcontinue() # $1 = name of chain
{
run_iptables -A $1 -m state --state ESTABLISHED -j ACCEPT
}
################################################################################
# Flush one of the NAT table chains #
################################################################################
flushnat() # $1 = name of chain
{
run_iptables -t nat -F $1
}
################################################################################
# Find interfaces to a given zone #
# #
# Read the interfaces file and for each record matching the passed ZONE, #
# echo the expanded contents of the "INTERFACE" column #
################################################################################
find_interfaces() # $1 = interface zone
{
local zne=$1
while read z interface subnet options; do
[ "x`expand $z`" = "x$zne" ] && echo `expand $interface`
done < $TMP_DIR/interfaces
}
################################################################################
# Chain name base for an interface #
################################################################################
chain_base() #$1 = interface
{
local c=${1%%+*}
echo ${c:=common}
}
################################################################################
# Forward Chain for an interface #
################################################################################
forward_chain() # $1 = interface
{
echo `chain_base $1`_fwd
}
################################################################################
# Input Chain for an interface #
################################################################################
input_chain() # $1 = interface
{
echo `chain_base $1`_in
}
################################################################################
# Output Chain for an interface #
################################################################################
output_chain() # $1 = interface
{
echo `chain_base $1`_out
}
################################################################################
# Masquerade Chain for an interface #
################################################################################
masq_chain() # $1 = interface
{
echo `chain_base $1`_masq
}
################################################################################
# DNAT Chain from a zone #
################################################################################
dnat_chain() # $1 = zone
{
echo ${1}_dnat
}
################################################################################
# SNAT Chain to a zone #
################################################################################
snat_chain() # $1 = zone
{
echo ${1}_snat
}
################################################################################
# First chains for an interface #
################################################################################
first_chains() #$1 = interface
{
local c=`chain_base $1`
echo ${c}_fwd ${c}_in
}
################################################################################
# Find hosts in a given zone #
# #
# Read hosts file and for each record matching the passed ZONE, #
# echo the expanded contents of the "HOST(S)" column #
################################################################################
find_hosts() # $1 = host zone
{
local hosts
while read z hosts options; do
[ "x`expand $z`" = "x$1" ] && expandv hosts && echo `separate_list $hosts`
done < $TMP_DIR/hosts
}
################################################################################
# Determine the interfaces on the firewall #
# #
# For each zone, create a variable called ${zone}_interfaces. This #
# variable contains a space-separated list of interfaces to the zone #
################################################################################
determine_interfaces() {
for zone in $zones; do
interfaces=`find_interfaces $zone`
interfaces=`echo $interfaces` # Remove extra trash
eval ${zone}_interfaces="\$interfaces"
done
}
################################################################################
# Determine the defined hosts in each zone and generate report #
################################################################################
determine_hosts() {
do_a_zone()
{
eval interfaces=\$${zone}_interfaces
for interface in $interfaces; do
if [ -z "$hosts" ]; then
hosts=$interface:0.0.0.0/0
else
hosts="$hosts $interface:0.0.0.0/0"
fi
done
}
recalculate_interfaces()
{
interfaces=
for host in $hosts; do
interface=${host%:*}
if ! list_search $interface $interfaces; then
if [ -z "$interfaces" ]; then
interfaces=$interface
else
interfaces="$interfaces $interface"
fi
fi
done
eval ${zone}_interfaces="\$interfaces"
}
for zone in $zones; do
hosts=`find_hosts $zone`
hosts=`echo $hosts` # Remove extra trash
if [ -n "MERGE_HOSTS" ]; then
####################################################################
# Zone will be the union of its host and interface definitions
#
do_a_zone
recalculate_interfaces
elif [ -n "$hosts" ]; then
####################################################################
# Zone is defined in terms of hosts -- derive the interface list
# from the host list
#
recalculate_interfacess
else
####################################################################
# If no hosts are defined for a zone then the zone consists of any
# host that can send us messages via the interfaces to the zone
#
do_a_zone
fi
eval ${zone}_hosts="\$hosts"
if [ -n "$hosts" ]; then
eval display=\$${zone}_display
display_list "$display Zone:" $hosts
else
error_message "Warning: Zone $zone is empty"
fi
done
}
################################################################################
# Ensure that the passed zone is defined in the zones file or is the firewall #
################################################################################
validate_zone() # $1 = zone
{
list_search $1 $zones $FW
}
################################################################################
# Validate the zone names and options in the interfaces file #
################################################################################
validate_interfaces_file() {
while read z interface subnet options; do
expandv z interface subnet options
r="$z $interface $subnet $options"
[ "x$z" = "x-" ] || validate_zone $z || startup_error "Invalid zone ($z) in record \"$r\""
list_search $interface $all_interfaces && \
startup_error "Error: Duplicate Interface $interface"
all_interfaces="$all_interfaces $interface"
for option in `separate_list $options`; do
case $option in
dhcp|noping|filterping|routestopped|norfc1918|multi)
;;
routefilter|dropunclean|logunclean|blacklist|proxyarp|-)
;;
*)
error_message "Warning: Invalid option ($option) in record \"$r\""
;;
esac
done
[ -z "$all_interfaces" ] && startup_error "Error: No Interfaces Defined"
done < $TMP_DIR/interfaces
}
################################################################################
# Validate the zone names and options in the hosts file #
################################################################################
validate_hosts_file() {
while read z hosts options; do
expandv z hosts options
r="$z $hosts $options"
validate_zone $z || startup_error "Invalid zone ($z) in record \"$r\""
for host in `separate_list $hosts`; do
interface=${host%:*}
list_search $interface $all_interfaces || \
startup_error "Unknown interface ($interface) in record \"$r\""
for option in `separate_list $options`; do
case $option in
routestopped|-)
;;
*)
error_message "Warning: Invalid option ($option) in record \"$r\""
;;
esac
done
done
done < $TMP_DIR/hosts
}
################################################################################
# Format a match by the passed MAC address #
# The passed address begins with "~" and uses "-" as a separator between bytes #
# Example: ~01-02-03-04-05-06 #
################################################################################
mac_match() # $1 = MAC address formated as described above
{
echo "--match mac --mac-source `echo $1 | sed 's/~//;s/-/:/g'`"
}
################################################################################
# validate a record from the rules file #
# #
# The caller has loaded the column contents from the record into the following #
# variables: #
# #
# target clients servers protocol ports cports address #
# #
# and has loaded a space-separated list of their values in "rule". #
################################################################################
validate_rule() {
############################################################################
# Ensure that the passed comma-separated list has 15 or fewer elements
#
validate_list() {
local temp=`separate_list $1`
[ `echo $temp | wc -w` -le 15 ]
}
############################################################################
# validate one rule
#
validate_a_rule() {
########################################################################
# Determine the format of the client
#
cli=
[ -n "$client" ] && case "$client" in
-)
;;
~*)
cli=`mac_match $client`
;;
[0-9]*|![0-9]*)
#
# IP Address, address or subnet
#
cli="-s $client"
;;
*)
#
# Assume that this is a device name
#
cli="-i $client"
;;
esac
dest_interface=
[ -n "$server" ] && case "$server" in
-)
serv=
;;
[0-9]*|![0-9]*)
serv=$server
;;
~*)
fatal_error "Error: Rule \"$rule\" - Server may not be specified by MAC Address"
;;
*)
dest_interface="-o $server"
serv=
;;
esac
################################################################
# Setup PROTOCOL, PORT and STATE variables
#
sports=""
dports=""
state="-m state --state NEW"
proto=$protocol
addr=$address
servport=$serverport
case $proto in
tcp|udp|TCP|UDP|6|17)
[ -n "$port" ] && [ "x${port}" != "x-" ] && \
dports="--dport $port"
[ -n "$cport" ] && [ "x${cport}" != "x-" ] && \
sports="--sport $cport"
;;
icmp|ICMP|0)
[ -n "$port" ] && dports="--icmp-type $port"
state=""
;;
related|RELATED)
proto=
state="-m state --state RELATED"
;;
*)
[ -n "$port" ] && [ "x${port}" != "x-" ] && \
startup_error "Port number not allowed with protocol " \
"\"$proto\"; rule: \"$rule\""
;;
esac
proto="${proto:+-p $proto}"
case "$logtarget" in
REJECT)
target=reject
[ -n "$servport" ] && \
startup_error "Error: server port may not be specified in a REJECT rule;"\
"rule: \"$rule\""
;;
ACCEPT)
[ -n "$servport" ] && \
startup_error "Error: server port may not be specified in an ACCEPT rule;"\
"rule: \"$rule\""
;;
REDIRECT)
[ -n "$serv" ] && startup_error "Error: REDIRECT rules cannot"\
" specify a server IP; rule: \"$rule\""
servport=${servport:=$port}
;;
DNAT)
[ -n "$serv" ] || startup_error "Error: DNAT rules require a" \
" server address; rule: \"$rule\""
;;
esac
if [ -z "$proto" -a -z "$cli" -a -z "$serv" -a -z "$servport" ]; then
error_message "Warning -- Rule \"$rule\" is a POLICY"
error_message " -- and should be moved to the policy file"
fi
if [ -n "${serv}${servport}" ]; then
##################################################################
# Destination is a Specific Server or we're redirecting a port
#
if [ -n "$addr" -a "$addr" != "$serv" ]; then
##############################################################
# Must use Prerouting DNAT
#
if [ -z "$NAT_ENABLED" ]; then
startup_error \
"Error - Rule \"$rule\" requires NAT which is disabled"
fi
if [ "$target" != "ACCEPT" ]; then
startup_error "Error - Only ACCEPT rules may specify " \
"port mapping; rule \"$rule\""
fi
fi
else
[ -n "$addr" ] && startup_error \
"Error: An ADDRESS ($addr) is only allowed in" \
" a DNAT or REDIRECT rule: \"$rule\""
fi
}
############################################################################
# V a l i d a t e _ R u l e S t a r t s H e r e
############################################################################
# Parse the Target and Clients columns
#
if [ "$target" = "${target%:*}" ]; then
loglevel=
else
loglevel="${target#*:}"
target="${target%:*}"
expandv loglevel
fi
logtarget="$target"
#
# DNAT and REDIRECT targets were implemented in version 1.3 to replace
# an older syntax. We simply map the new syntax into the old and proceed;
# that way, people who have files with the old syntax don't need to
# convert right away.
#
case $target in
DNAT)
target=ACCEPT
address=${address:=detect}
;;
REDIRECT)
target=ACCEPT
address=${address:=all}
if [ "x-" = "x$servers" ]; then
servers=$FW
else
servers="fw::$servers"
fi
;;
ACCEPT|DROP|REJECT)
;;
*)
startup_error "Error: Invalid target;" \
" rule: \"$rule\""
esac
if [ "$clients" = "${clients%:*}" ]; then
clientzone="$clients"
clients=
else
clientzone="${clients%:*}"
clients="${clients#*:}"
[ -z "$clientzone" -o -z "$clients" ] && \
startup_error "Error: Empty source zone or qualifier: rule \"$rule\""
fi
if [ "$clientzone" = "${clientzone%\!*}" ]; then
excludezones=
else
excludezones="${clientzone#*\!}"
clientzone="${clientzone%\!*}"
[ "$logtarget" = DNAT ] || [ "$logtarget" = REDIRECT ] ||\
startup_error "Error: Exclude list only allowed with DNAT or REDIRECT"
fi
############################################################################
# Validate the Source Zone
if ! validate_zone $clientzone; then
startup_error "Error: Undefined Client Zone in rule \"$rule\""
fi
source=$clientzone
[ $source = $FW ] && source_hosts= || eval source_hosts=\"\$${source}_hosts\"
############################################################################
# Parse the servers column
#
if [ "$servers" = "${servers%:*}" ] ; then
serverzone="$servers"
servers=
serverport=
else
serverzone="${servers%%:*}"
servers="${servers#*:}"
if [ "$servers" != "${servers%:*}" ] ; then
serverport="${servers#*:}"
servers="${servers%:*}"
[ -z "$serverzone" -o -z "$serverport" ] && \
startup_error "Error: Empty destination zone or server port: rule \"$rule\""
else
serverport=
[ -z "$serverzone" -o -z "$servers" ] && \
startup_error "Error: Empty destination zone or qualifier: rule \"$rule\""
fi
fi
############################################################################
# Validate the destination zone
#
if ! validate_zone $serverzone; then
startup_error "Error: Undefined Server Zone in rule \"$rule\""
fi
dest=$serverzone
############################################################################
# Check length of port lists if MULTIPORT set
#
if [ -n "$MULTIPORT" ]; then
validate_list $ports ||
error_message "Warning: Too many destination ports: Rule \"$rule\""
validate_list $cports ||
error_message "Warning: Too many source ports: Rule \"$rule\""
fi
############################################################################
# Iterate through the various lists validating individual rules
#
for client in `separate_list ${clients:=-}`; do
for server in `separate_list ${servers:=-}`; do
for port in `separate_list ${ports:=-}`; do
for cport in `separate_list ${cports:=-}`; do
validate_a_rule
done
done
done
done
echo " Rule \"$rule\" validated."
}
################################################################################
# validate the rules file #
################################################################################
validate_rules() # $1 = name of rules file
{
strip_file rules
while read target clients servers protocol ports cports address; do
expandv clients servers protocol ports cports address
case "$target" in
ACCEPT*|DROP*|REJECT*|DNAT*|REDIRECT*)
rule="`echo $target $clients $servers $protocol $ports $cports $address`"
validate_rule
;;
*)
rule="`echo $target $clients $servers $protocol $ports $cports $address`"
startup_error "Error: Invalid Target - rule \"$rule\" ignored"
;;
esac
done < $TMP_DIR/rules
}
################################################################################
# validate the policy file #
################################################################################
validate_policy()
{
strip_file policy $policy
while read client server policy loglevel synparams; do
expandv client server policy loglevel synparams
case "$client" in
all|ALL)
;;
*)
if ! validate_zone $client; then
startup_error "Error: Undefined zone $client"
fi
esac
case "$server" in
all|ALL)
;;
*)
if ! validate_zone $server; then
startup_error "Error: Undefined zone $server"
fi
esac
case $policy in
ACCEPT|REJECT|DROP|CONTINUE)
;;
*)
startup_error "Error: Invalid policy $policy"
;;
esac
done < $TMP_DIR/policy
}
################################################################################
# Find broadcast addresses #
################################################################################
find_broadcasts() {
while read z interface bcast options; do
expandv interface bcast
if [ "x$bcast" = "xdetect" ]; then
addr="`ip addr show $interface 2> /dev/null`"
if [ -n "`echo "$addr" | grep 'inet.*brd '`" ]; then
addr="`echo "$addr" | \
grep "inet " | sed 's/^.* inet.*brd //;s/scope.*//'`"
echo $addr | cut -d' ' -f 1
fi
elif [ "x${bcast}" != "x-" ]; then
echo `separate_list $bcast`
fi
done < $TMP_DIR/interfaces
}
################################################################################
# Find interface address--returns the first IP address assigned to the passed #
# device #
################################################################################
find_interface_address() # $1 = interface
{
#
# get the line of output containing the first IP address
#
addr=`ip addr show $1 2> /dev/null | grep inet | head -n1`
#
# If there wasn't one, bail out now
#
[ -n "$addr" ] || fatal_error "Can't determine the IP address of $1"
#
# Strip off the trailing VLSM mask (or the peer IP in case of a P-t-P link)
# along with everything else on the line
#
echo $addr | sed 's/inet //;s/\/.*//;s/ peer.*//'
}
################################################################################
# Find interfaces that have the passed option specified #
################################################################################
find_interfaces_by_option() # $1 = option
{
while read ignore interface subnet options; do
expandv options
list_search $1 `separate_list $options` && \
echo `expand $interface`
done < $TMP_DIR/interfaces
}
################################################################################
# Find hosts with the passed option #
################################################################################
find_hosts_by_option() # $1 = option
{
while read ignore hosts options; do
expandv options
list_search $1 `separate_list $options` && \
echo `expand $hosts`
done < $TMP_DIR/hosts
while read ignore interface ignore1 options; do
expandv options
list_search $1 `separate_list $options` && \
echo `expand $interface`:0.0.0.0/0
done < $TMP_DIR/interfaces
}
################################################################################
# Determine if there are interfaces of the given zone and option #
# #
# Returns zero if any such interfaces are found and returns one otherwise. #
################################################################################
have_interfaces_in_zone_with_option() # $1 = zone, $2 = option
{
local zne=$1
while read z interface broadcast options; do
[ "x`expand $z`" = "x$zne" ] && expandv options && \
list_search $1 `separate_list $options` && \
return 0
done < $TMP_DIR/interfaces
return 1
}
################################################################################
# Flush and delete all user-defined chains in the filter table #
################################################################################
deleteallchains() {
run_iptables -F
run_iptables -X
}
################################################################################
# Source a user exit file if it exists #
################################################################################
run_user_exit() # $1 = file name
{
local user_exit=`find_file $1`
if [ -f $user_exit ]; then
echo "Processing $user_exit ..."
. $user_exit
fi
}
################################################################################
# Stop the Firewall - #
################################################################################
stop_firewall() {
stopping="Yes"
deletechain shorewall
run_user_exit stop
[ -n "$MANGLE_ENABLED" ] && \
run_iptables -t mangle -F && \
run_iptables -t mangle -X
[ -n "$NAT_ENABLED" ] && delete_nat
delete_proxy_arp
[ -n "$TC_ENABLED" ] && delete_tc
setpolicy INPUT DROP
setpolicy OUTPUT DROP
setpolicy FORWARD DROP
deleteallchains
hosts="`find_hosts_by_option routestopped`"
strip_file routestopped
while read interface host; do
expandv interface host
[ "x$host" = "x-" ] && host=
hosts="$hosts $interface:${host:-0.0.0.0/0}"
done < $TMP_DIR/routestopped
for host in $hosts; do
interface=${host%:*}
subnet=${host#*:}
iptables -A INPUT -i $interface -s $subnet -j ACCEPT
iptables -A OUTPUT -o $interface -d $subnet -j ACCEPT
for host1 in $hosts; do
[ "$host" != "$host1" ] && \
iptables -A FORWARD -i $interface -s $subnet \
-o ${host1%:*} -d ${host1#*:} -j ACCEPT
done
done
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
for interface in `find_interfaces_by_option dhcp`; do
iptables -A INPUT -p udp -i $interface --dport 67:68 -j ACCEPT
iptables -A OUTPUT -p udp -o $interface --dport 67:68 -j ACCEPT
done
case "$IP_FORWARDING" in
[Oo][Nn])
echo 1 > /proc/sys/net/ipv4/ip_forward
;;
[Oo][Ff][Ff])
echo 0 > /proc/sys/net/ipv4/ip_forward
;;
esac
run_user_exit stopped
logger "Shorewall Stopped"
rm -rf $TMP_DIR
case $command in
stop|clear)
;;
*)
#
# The firewall is being stopped when we were trying to do something
# else. Remove the lock file and Kill the shell in case we're in a
# subshell
#
my_mutex_off
kill $$
;;
esac
}
################################################################################
# Remove all rules and remove all user-defined chains #
################################################################################
clear_firewall() {
stop_firewall
run_iptables -F
echo 1 > /proc/sys/net/ipv4/ip_forward
setpolicy INPUT ACCEPT
setpolicy FORWARD ACCEPT
setpolicy OUTPUT ACCEPT
run_user_exit clear
logger "Shorewall Cleared"
}
################################################################################
# Set up ipsec tunnels #
################################################################################
setup_tunnels() # $1 = name of tunnels file
{
local inchain
local outchain
setup_one_ipsec() # $1 = gateway $2 = gateway zone
{
options="-m state --state NEW -j ACCEPT"
addrule $inchain -p 50 -s $1 $options
addrule $outchain -p 50 -d $1 $options
run_iptables -A $inchain -p 51 -s $1 $options
run_iptables -A $outchain -p 51 -d $1 $options
run_iptables -A $inchain -p udp -s $1 --sport 500 --dport 500 $options
run_iptables -A $outchain -p udp -d $1 --dport 500 --sport 500 $options
if [ -n "$2" ]; then
if validate_zone $2; then
addrule ${FW}2${2} -p udp --sport 500 --dport 500 $options
else
error_message "Warning: Invalid gateway zone ($2)" \
" -- Tunnel \"$tunnel\" may encounter keying problems"
fi
fi
echo " IPSEC tunnel to $gateway defined."
}
setup_one_other() # $1 = TYPE, $2 = gateway, $3 = protocol
{
options="-m state --state NEW -j ACCEPT"
addrule $inchain -p $3 -s $2 $options
addrule $outchain -p $3 -d $2 $options
echo " $1 tunnel to $gateway defined."
}
strip_file tunnels $1
while read kind z gateway z1; do
expandv kind z gateway z1
tunnel="`echo $kind $z $gateway $z1`"
if validate_zone $z; then
inchain=${z}2${FW}
outchain=${FW}2${z}
case $kind in
ipsec|IPSEC)
setup_one_ipsec $gateway $z1
;;
ipip|IPIP)
setup_one_other IPIP $gateway 4
;;
gre|GRE)
setup_one_other GRE $gateway 47
;;
*)
error_message "Tunnels of type $kind are not supported:" \
"Tunnel \"$tunnel\" Ignored"
;;
esac
else
error_message "Invalid gateway zone ($z)" \
" -- Tunnel \"$tunnel\" Ignored"
fi
done < $TMP_DIR/tunnels
}
################################################################################
# Setup Proxy ARP #
################################################################################
setup_proxy_arp() {
print_error() {
error_message "Invalid value for HAVEROUTE - ($haveroute)"
error_message "Entry \"$address $interface $external $haveroute\" ignored"
}
setup_one_proxy_arp() {
case $haveroute in
[Nn][Oo])
haveroute=
;;
[Yy][Ee][Ss])
;;
*)
if [ -n "$haveroute" ]; then
print_error
return
fi
;;
esac
[ -z "$haveroute" ] && run_ip route add $address dev $interface
run_arp -Ds $address $external pub
echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp
echo 0 > /proc/sys/net/ipv4/conf/$external/proxy_arp
echo $address $interface $external $haveroute >> ${STATEDIR}/proxyarp
echo " Host $address connected to $interface added to ARP on $external"
}
> ${STATEDIR}/proxyarp
strip_file proxyarp
while read address interface external haveroute; do
expandv address interface external haveroute
setup_one_proxy_arp
done < $TMP_DIR/proxyarp
interfaces=`find_interfaces_by_option proxyarp`
for interface in $interfaces; do
if echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp 2> /dev/null; then
echo " Enabled proxy ARP on $interface"
else
error_message "Warning: Unable to enable proxy ARP on $interface"
fi
done
}
###############################################################################
# Set up SYN flood protection #
###############################################################################
setup_syn_flood_chain ()
# $1 = policy chain
# $2 = synparams
{
local chain=$1
local limit=${2%:*}
local limit_burst=${2#*:}
run_iptables -N @$chain
run_iptables -A @$chain \
-m limit --limit $limit --limit-burst $limit_burst \
-j RETURN
run_iptables -A @$chain -j DROP
}
################################################################################
# Enable SYN flood protection on a chain #
# -----------------------------------------------------------------------------#
# Insert a jump rule to the protection chain from the first chain. Inserted #
# as the second rule and restrict the jump to SYN packets #
################################################################################
enable_syn_flood_protection() # $1 = chain, $2 = protection chain
{
run_iptables -I $1 2 -p tcp --syn -j @$2
echo " Enabled SYN flood protection"
}
################################################################################
# Delete existing Proxy ARP #
################################################################################
delete_proxy_arp() {
if [ -f ${STATEDIR}/proxyarp ]; then
while read address interface external haveroute; do
qt arp -i $external -d $address pub
[ -z "$haveroute" ] && qt ip route del $address dev $interface
done < ${STATEDIR}/proxyarp
rm -f ${STATEDIR}/proxyarp
fi
[ -d ${STATEDIR} ] && touch ${STATEDIR}/proxyarp
for f in `ls /proc/sys/net/ipv4/conf/*/proxy_arp`; do
echo 0 > $f
done
}
################################################################################
# Setup Static Network Address Translation (NAT) #
################################################################################
setup_nat() {
local allints
#
# At this point, we're just interested in the network translation
#
> ${STATEDIR}/nat
strip_file nat
echo "Setting up NAT..."
while read external interface internal allints localnat; do
expandv external interface internal allints localnat
if [ -n "$ADD_IP_ALIASES" ]; then
qt ip addr del $external dev $interface
fi
if [ -z "$allints" -o "$allints" = "Yes" \
-o "$allints" = "yes" ]
then
addnatrule nat_in -d $external -j DNAT --to-destination $internal
addnatrule nat_out -s $internal -j SNAT --to-source $external
if [ "$localnat" = "Yes" -o "$localnat" = "yes" ]; then
run_iptables -t nat -A OUTPUT -d $external \
-j DNAT --to-destination $internal
fi
else
addnatrule `input_chain $interface` \
-d $external -j DNAT --to-destination $internal
addnatrule `output_chain $interface` \
-s $internal -j SNAT --to-source $external
fi
if [ -n "$ADD_IP_ALIASES" ]; then
list_search $external $aliases_to_add || \
aliases_to_add="$aliases_to_add $external $interface"
fi
echo " Host $internal NAT $external on $interface"
done < $TMP_DIR/nat
}
################################################################################
# Delete existing Static NAT #
################################################################################
delete_nat() {
run_iptables -t nat -F
run_iptables -t nat -X
if [ -f ${STATEDIR}/nat ]; then
while read external interface; do
qt ip addr del $external dev $interface
done < ${STATEDIR}/nat
rm -f {$STATEDIR}/nat
fi
[ -d ${STATEDIR} ] && touch ${STATEDIR}/nat
}
################################################################################
# Process TC Rule #
################################################################################
process_tc_rule()
{
add_a_tc_rule() {
r=
chain=tcpre
if [ "x$source" != "x-" ]; then
case $source in
[0-9]*)
r="-s $source "
;;
~*)
r=`mac_match $source`
;;
$FW)
chain=tcout
;;
*)
r="-i $source "
;;
esac
fi
[ "x$dest" = "x-" ] || r="${r}-d $dest "
[ "$proto" = "all" ] || r="${r}-p $proto "
[ "x$port" = "x-" ] || r="${r}--dport $port "
[ "x$sport" = "x-" ] || r="${r}--sport $sport "
run_iptables -t mangle -A $chain $r -j MARK --set-mark $mark
}
for source in `separate_list ${sources:=-}`; do
for dest in `separate_list ${dests:=-}`; do
for port in `separate_list ${ports:=-}`; do
for sport in `separate_list ${sports:=-}`; do
add_a_tc_rule
done
done
done
done
echo " TC Rule \"$rule\" added"
}
################################################################################
# Setup queuing and classes #
################################################################################
setup_tc() {
echo "Setting up Traffic Control Rules..."
#
# Create the TC mangle chains
#
run_iptables -t mangle -N tcpre
run_iptables -t mangle -N tcout
#
# Process the TC Rules File
#
strip_file tcrules
while read mark sources dests proto ports sports; do
expandv mark sources dests proto ports sports
rule=`echo "$mark $sources $dests $proto $ports $sports"`
process_tc_rule
done < $TMP_DIR/tcrules
#
# Link to the TC mangle chains from the main chains
#
run_iptables -t mangle -A PREROUTING -j tcpre
run_iptables -t mangle -A OUTPUT -j tcout
run_user_exit tcstart
}
################################################################################
# Clear Traffic Shaping #
################################################################################
delete_tc()
{
clear_one_tc() {
tc qdisc del dev $1 root 2> /dev/null
tc qdisc del dev $1 ingress 2> /dev/null
}
run_user_exit tcclear
run_ip link list | \
while read inx interface details; do
case $inx in
[0-9]*)
clear_one_tc ${interface%:}
;;
*)
;;
esac
done
}
################################################################################
# Add a NAT rule - Helper function for the rules file processor #
#------------------------------------------------------------------------------#
# The caller has established the following variables: #
# cli = Source IP, interface or MAC Specification #
# serv = Destination IP Specification #
# servport = Port the server is listening on #
# dest_interface = Destination Interface Specification #
# proto = Protocol Specification #
# addr = Original Destination Address #
# dports = Destination Port Specification. 'dports' may be changed #
# by this function #
# cport = Source Port Specification #
# multiport = String to invoke multiport match if appropriate #
################################################################################
add_nat_rule() {
local chain
# Be sure NAT is enabled
if [ -z "$NAT_ENABLED" ]; then
fatal_error \
"Error - Rule \"$rule\" requires NAT which is disabled"
fi
# Onle ACCEPT (plus DNAT and REDIRECT) may result in NAT
if [ "$target" != "ACCEPT" ]; then
fatal_error "Error - Only DNAT and REDIRECT rules may specify " \
"port mapping; rule \"$rule\""
fi
# Parse SNAT address if any
if [ "$addr" != "${addr%:*}" ]; then
snat="${addr#*:}"
addr="${addr%:*}"
else
snat=""
fi
# Set original destination address
case $addr in
all)
addr=
;;
detect)
addr=
if [ -n "$DETECT_DNAT_IPADDRS" -a "$source" != "$FW" ]; then
eval interfaces=\$${source}_interfaces
for interface in $interfaces; do
addr="`find_interface_address $interface` $addr"
done
fi
;;
esac
addr=${addr:-0.0.0.0/0}
# Select target
if [ -n "$serv" ]; then
servport="${servport:+:$servport}"
target1="DNAT --to-destination ${serv}${servport}"
else
target1="REDIRECT --to-port $servport"
fi
# Generate nat table rules
if [ "$source" = "$FW" ]; then
run_iptables -t nat -A OUTPUT $proto $sports -d $addr \
$multiport $dports -j $target1
else
chain=`dnat_chain $source`
if [ -n "$excludezones" ]; then
chain=nonat${nonat_seq}
nonat_seq=$(($nonat_seq + 1))
createnatchain $chain
addnatrule `dnat_chain $source` -j $chain
for z in $excludezones; do
eval hosts=\$${z}_hosts
for host in $hosts; do
for adr in $addr; do
addnatrule $chain $proto -s ${host#*:} \
$multiport $sports -d $adr $dports -j RETURN
done
done
done
fi
for adr in $addr; do
addnatrule $chain $proto $cli $sports \
-d $adr $multiport $dports -j $target1
done
fi
# Replace destination port by the new destination port
if [ -n "$servport" ]; then
if [ -z "$multiport" ]; then
dports="--dport ${servport#*:}"
else
dports="--dports ${servport#*:}"
fi
fi
# Handle SNAT
if [ -n "$snat" ]; then
if [ -n "$cli" ]; then
addnatrule `snat_chain $dest` $proto $cli $multiport \
$sports -d $serv $dports -j SNAT --to-source $snat
else
for source_host in $source_hosts; do
[ "x${source_host#*:}" = "x0.0.0.0/0" ] && \
error_message "Warning: SNAT will occur on all connections to this server and port - rule \"$rule\""
addnatrule `snat_chain $dest` \
-s ${source_host#*:} $proto $sports $multiport \
-d $serv $dports -j SNAT --to-source $snat
done
fi
fi
}
################################################################################
# Add one Filter Rule -- Helper function for the rules file processor #
#------------------------------------------------------------------------------#
# The caller has established the following variables: #
# client = SOURCE IP or MAC #
# server = DESTINATION IP or interface #
# protocol = Protocol #
# address = Original Destination Address #
# port = Destination Port #
# cport = Source Port #
# multioption = String to invoke multiport match if appropriate #
# servport = Port the server listens on #
# chain = The canonical chain for this rule #
################################################################################
add_a_rule()
{
# Set source variables
cli=
[ -n "$client" ] && case "$client" in
-)
;;
[0-9]*|![0-9]*)
cli="-s $client"
;;
~*)
cli=`mac_match $client`
;;
*)
cli="-i $client"
;;
esac
# Set destination variables
dest_interface=
[ -n "$server" ] && case "$server" in
-)
serv=
;;
[0-9]*|![0-9]*)
serv=$server
;;
*)
dest_interface="-o $server"
serv=
;;
esac
# Setup protocol and port variables
sports=
dports=
state="-m state --state NEW"
proto=$protocol
addr=$address
servport=$serverport
multiport=
case $proto in
tcp|udp|TCP|UDP|6|17)
if [ -n "$port" -a "x${port}" != "x-" ]; then
dports="--dport"
if [ -n "$multioption" -a "$port" != "${port%,*}" ]; then
multiport="$multioption"
dports="--dports"
fi
dports="$dports $port"
fi
if [ -n "$cport" -a "x${cport}" != "x-" ]; then
sports="--sport"
if [ -n "$multioption" -a "$cport" != "${cport%,*}" ]; then
multiport="$multioption"
sports="--sports"
fi
sports="$sports $cport"
fi
;;
icmp|ICMP|1)
[ -n "$port" ] && [ "x${port}" != "x-" ] && \
dports="--icmp-type $port"
state=
;;
all|ALL)
[ -n "$port" ] && [ "x${port}" != "x-" ] && \
fatal_error "Port number not allowed with \"all\";" \
" rule: \"$rule\""
proto=
;;
related|RELATED)
proto=
state="-m state --state RELATED"
;;
*)
[ -n "$port" ] && [ "x${port}" != "x-" ] && \
fatal_error "Port number not allowed with protocol " \
"\"$proto\"; rule: \"$rule\""
;;
esac
proto="${proto:+-p $proto}"
# Some misc. setup
case "$logtarget" in
REJECT)
target=reject
[ -n "$servport" ] && \
fatal_error "Error: server port may not be specified in a REJECT rule;"\
"rule: \"$rule\""
;;
REDIRECT)
[ -n "$serv" ] && startup_error "Error: REDIRECT rules cannot"\
" specify a server IP; rule: \"$rule\""
servport=${servport:=$port}
;;
DNAT)
[ -n "$serv" ] || fatal_error "Error: DNAT rules require a" \
" server address; rule: \"$rule\""
;;
esac
# Complain if the rule is really a policy
if [ -z "$proto" -a -z "$cli" -a -z "$serv" -a -z "$servport" ]; then
error_message "Warning -- Rule \"$rule\" is a POLICY"
error_message " -- and should be moved to the policy file"
fi
if [ -n "${serv}${servport}" ]; then
# A specific server or server port given
[ -n "$addr" -a "$addr" != "$serv" ] && add_nat_rule
serv="${serv:+-d $serv}"
[ -n "$loglevel" ] && run_iptables -A $chain $proto $multiport \
$state $cli $sports $serv $dports -j LOG $LOGPARMS \
--log-prefix "Shorewall:$chain:$logtarget:" \
--log-level $loglevel
run_iptables -A $chain $proto $multiport $state $cli $sports \
$serv $dports -j $target
else
# Destination is a simple zone
[ -n "$addr" ] && fatal_error \
"Error: An ADDRESS ($addr) is only allowed in" \
" a DNAT or REDIRECT: \"$rule\""
[ -n "$loglevel" ] && run_iptables -A $chain $proto $multiport \
$dest_interface $state $cli $sports $dports -j LOG \
$LOGPARMS --log-prefix "Shorewall:$chain:$logtarget:" \
--log-level $loglevel
run_iptables -A $chain $proto $multiport $dest_interface $state \
$cli $sports $dports -j $target
fi
}
################################################################################
# Process a record from the rules file #
# #
# The caller has loaded the column contents from the record into the following #
# variables: #
# #
# target clients servers protocol ports cports address #
# #
# and has loaded a space-separated list of their values in "rule". #
# #
# The 'multioption' variable has also been loaded appropriately to reflect #
# the setting of the MULTIPORT option in /etc/shorewall/shorewall.conf #
################################################################################
process_rule() {
# Function Body -- isolate log level
if [ "$target" = "${target%:*}" ]; then
loglevel=
else
loglevel="${target#*:}"
target="${target%:*}"
expandv loglevel
fi
logtarget="$target"
# Convert 1.3 Rule formats to 1.2 format
case $target in
DNAT)
target=ACCEPT
address=${address:=detect}
;;
REDIRECT)
target=ACCEPT
address=${address:=all}
if [ "x-" = "x$servers" ]; then
servers=$FW
else
servers="$FW::$servers"
fi
;;
esac
# Parse and validate source
if [ "$clients" = "${clients%:*}" ]; then
clientzone="$clients"
clients=
else
clientzone="${clients%:*}"
clients="${clients#*:}"
[ -z "$clientzone" -o -z "$clients" ] && \
fatal_error "Error: Empty source zone or qualifier: rule \"$rule\""
fi
if [ "$clientzone" = "${clientzone%\!*}" ]; then
excludezones=
else
excludezones="${clientzone#*\!}"
clientzone="${clientzone%\!*}"
[ "$logtarget" = DNAT ] || [ "$logtarget" = REDIRECT ] ||\
fatal_error "Error: Exclude list only allowed with DNAT or REDIRECT"
fi
if ! validate_zone $clientzone; then
fatal_error "Error: Undefined Client Zone in rule \"$rule\""
fi
# Parse and validate destination
source=$clientzone
[ $source = $FW ] && source_hosts= || eval source_hosts=\"\$${source}_hosts\"
if [ "$servers" = "${servers%:*}" ] ; then
serverzone="$servers"
servers=
serverport=
else
serverzone="${servers%%:*}"
servers="${servers#*:}"
if [ "$servers" != "${servers%:*}" ] ; then
serverport="${servers#*:}"
servers="${servers%:*}"
[ -z "$serverzone" -o -z "$serverport" ] && \
fatal_error "Error: Empty destination zone or server port: rule \"$rule\""
else
serverport=
[ -z "$serverzone" -o -z "$servers" ] && \
startup_error "Error: Empty destination zone or qualifier: rule \"$rule\""
fi
fi
if ! validate_zone $serverzone; then
fatal_error "Error: Undefined Server Zone in rule \"$rule\""
fi
dest=$serverzone
# Create canonical chain if necessary
chain=${source}2${dest}
ensurechain $chain
# Generate Netfilter rule(s)
if [ -n "$MULTIPORT" -a \
"$ports" = "${ports%:*}" -a \
"$cports" = "${cports%:*}" -a \
`list_count $ports` -le 15 -a \
`list_count $cports` -le 15 ]
then
multioption="-m multiport"
for client in `separate_list ${clients:=-}`; do
for server in `separate_list ${servers:=-}`; do
port=${ports:=-}
cport=${cports:=-}
add_a_rule
done
done
else
multioption=
for client in `separate_list ${clients:=-}`; do
for server in `separate_list ${servers:=-}`; do
for port in `separate_list ${ports:=-}`; do
for cport in `separate_list ${cports:=-}`; do
add_a_rule
done
done
done
done
fi
echo " Rule \"$rule\" added."
}
################################################################################
# Process the rules file #
################################################################################
process_rules() # $1 = name of rules file
{
strip_file rules
while read target clients servers protocol ports cports address; do
case "$target" in
ACCEPT*|DROP*|REJECT*|DNAT*|REDIRECT*)
expandv clients servers protocol ports cports address
rule="`echo $target $clients $servers $protocol $ports $cports $address`"
process_rule
;;
*)
rule="`echo $target $clients $servers $protocol $ports $cports $address`"
fatal_error "Error: Invalid Target in rule \"$rule\""
;;
esac
done < $TMP_DIR/rules
}
################################################################################
# Process a record from the tos file #
# #
# The caller has loaded the column contents from the record into the following #
# variables: #
# #
# src dst protocol sport dport tos #
# #
# and has loaded a space-separated list of their values in "rule". #
################################################################################
process_tos_rule() {
############################################################################
# Parse the contents of the 'src' variable
#
if [ "$src" = "${src%:*}" ]; then
srczone="$src"
src=
else
srczone="${src%:*}"
src="${src#*:}"
fi
source=
#
# Validate the source zone
#
if validate_zone $srczone; then
source=$srczone
elif [ "$srczone" = "all" ]; then
source="all"
else
error_message "Warning: Undefined Source Zone - rule \"$rule\" ignored"
return
fi
[ -n "$src" ] && case "$src" in
[0-9]*|![0-9]*)
#
# IP Address or subnet
#
src="-s $src"
;;
~*)
src=`mac_match $src`
;;
*)
#
# Assume that this is a device name
#
src="-i $src"
;;
esac
############################################################################
# Parse the contents of the 'dst' variable
#
if [ "$dst" = "${dst%:*}" ]; then
dstzone="$dst"
dst=
else
dstzone="${dst%:*}"
dst="${dst#*:}"
fi
dest=
#
# Validate the destination zone
#
if validate_zone $dstzone; then
dest=$dstzone
elif [ "$dstzone" = "all" ]; then
dest="all"
else
error_message \
"Warning: Undefined Destination Zone - rule \"$rule\" ignored"
return
fi
[ -n "$dst" ] && case "$dst" in
[0-9]*|![0-9]*)
#
# IP Address or subnet
#
;;
*)
#
# Assume that this is a device name
#
error_message \
"Warning: Invalid Destination - rule \"$rule\" ignored"
return
;;
esac
############################################################################
# Setup PROTOCOL and PORT variables
#
sports=""
dports=""
case $protocol in
tcp|udp|TCP|UDP|6|17)
[ -n "$sport" ] && [ "x${sport}" != "x-" ] && \
sports="--sport $sport"
[ -n "$dport" ] && [ "x${dport}" != "x-" ] && \
dports="--dport $dport"
;;
icmp|ICMP|0)
[ -n "$dport" ] && [ "x${dport}" != "x-" ] && \
dports="--icmp-type $dport"
;;
all|ALL)
protocol=
;;
*)
;;
esac
protocol="${protocol:+-p $protocol}"
tos="-j TOS --set-tos $tos"
case "$dstzone" in
all|ALL)
dst=0.0.0.0/0
;;
*)
[ -z "$dst" ] && eval dst=\$${dstzone}_hosts
;;
esac
for dest in $dst; do
dest="-d $dest"
case $srczone in
$FW)
run_iptables -t mangle -A outtos \
$protocol $dest $dports $sports $tos
;;
all|ALL)
run_iptables -t mangle -A outtos \
$protocol $dest $dports $sports $tos
run_iptables -t mangle -A pretos \
$protocol $dest $dports $sports $tos
;;
*)
if [ -n "$src" ]; then
run_iptables -t mangle -A pretos $src \
$protocol $dest $dports $sports $tos
else
eval interfaces=\$${srczone}_interfaces
for interface in $interfaces; do
run_iptables -t mangle -A pretos -i $interface \
$protocol $dest $dports $sports $tos
done
fi
;;
esac
done
echo " Rule \"$rule\" added."
}
################################################################################
# Process the tos file #
################################################################################
process_tos() # $1 = name of tos file
{
echo "Processing $1..."
run_iptables -t mangle -N pretos
run_iptables -t mangle -N outtos
strip_file tos $1
while read src dst protocol sport dport tos; do
expandv src dst protocol sport dport tos
rule="`echo $src $dst $protocol $sport $dport $tos`"
process_tos_rule
done < $TMP_DIR/tos
run_iptables -t mangle -A PREROUTING -j pretos
run_iptables -t mangle -A OUTPUT -j outtos
}
################################################################################
# Load a Kernel Module #
################################################################################
loadmodule() # $1 = module name, $2 - * arguments
{
local modulename=$1
local modulefile
if [ -z "`lsmod | grep $modulename`" ]; then
shift
modulefile=$MODULESDIR/${modulename}.o
if [ -f $modulefile ]; then
insmod $modulefile $*
return
fi
#
# If the modules directory contains compressed modules then we'll
# assume that insmod can load them
#
modulefile=${modulefile}.gz
if [ -f $modulefile ]; then
insmod $modulefile $*
fi
fi
}
################################################################################
# Display elements of a list with leading white space #
################################################################################
display_list() # $1 = List Title, rest of $* = list to display
{
[ $# -gt 1 ] && echo " $*"
}
################################################################################
# Add rules to the "common" chain to silently drop packets addressed to any of #
# the passed addresses #
################################################################################
drop_broadcasts() # $* = broadcast addresses
{
while [ $# -gt 0 ]; do
run_iptables -A common -d $1 -j DROP
shift
done
}
################################################################################
# Add policy rule ( and possibly logging rule) to the passed chain #
################################################################################
policy_rules() # $1 = chain to add rules to
# $2 = policy
# $3 = loglevel
{
local target="$2"
case "$target" in
ACCEPT)
;;
DROP)
run_iptables -A $1 -j common
;;
REJECT)
run_iptables -A $1 -j common
target=reject
;;
CONTINUE)
target=
;;
*)
fatal_error "Invalid policy ($policy) for $1"
;;
esac
[ $# -eq 3 ] && [ "x${3}" != "x-" ] && run_iptables -A $1 -j LOG $LOGPARMS \
--log-prefix "Shorewall:${1}:${2}:" --log-level $3
[ -n "$target" ] && run_iptables -A $1 -j $target
}
################################################################################
# Generate default policy & log level rules for the passed client & server #
# zones #
#------------------------------------------------------------------------------#
# This function is only called when the canonical chain for this client/server #
# pair is known to exist. If the default policy for this pair specifies the #
# same chain then we add the policy (and logging) rule to the canonical chain; #
# otherwise add a rule to the canonical chain to jump to the appropriate #
# policy chain. #
################################################################################
default_policy() # $1 = client $2 = server
{
local chain="${1}2${2}"
local policy=
local loglevel=
local chain1
jump_to_policy_chain() {
########################################################################
# Add a jump to from the canonical chain to the policy chain. On return,
# $chain is set to the name of the policy chain
#
run_iptables -A $chain -j $chain1
chain=$chain1
}
apply_default()
{
########################################################################
# Add the appropriate rules to the canonical chain ($chain) to enforce
# the specified policy
#-----------------------------------------------------------------------
# Construct policy chain name
#
chain1=${client}2${server}
if [ "$chain" = "$chain1" ]; then
####################################################################
# The policy chain is the canonical chain; add policy rule to it
# The syn flood jump has already been added if required.
#
policy_rules $chain $policy $loglevel
else
####################################################################
# The policy chain is different from the canonical chain -- approach
# depends on the policy
#
case $policy in
ACCEPT)
if [ -n "$synparams" ]; then
############################################################
# To avoid double-counting SYN packets, enforce the policy
# in this chain.
#
enable_syn_flood_protection $chain $chain1
policy_rules $chain $policy $loglevel
else
############################################################
# No problem with double-counting so just jump to the
# policy chain.
#
jump_to_policy_chain
fi
;;
CONTINUE)
################################################################
# Silly to jump to the policy chain -- add any logging
# rules and enable SYN flood protection if requested
#
[ -n "$synparams" ] && \
enable_syn_flood_protection $chain $chain1
policy_rules $chain $policy $loglevel
;;
*)
################################################################
# DROP or REJECT policy -- enforce in the policy chain and
# enable SYN flood protection if requested.
#
[ -n "$synparams" ] && \
enable_syn_flood_protection $chain $chain1
jump_to_policy_chain
;;
esac
fi
echo " Policy $policy for $1 to $2 using chain $chain"
}
while read client server policy loglevel synparams; do
expandv client server policy loglevel synparams
case "$client" in
all|ALL)
if [ "$server" = "$2" -o "$server" = "all" ]; then
apply_default $1 $2
return
fi
;;
*)
if [ "$client" = "$1" ] && \
[ "$server" = "all" -o "$server" = "$2" ]
then
apply_default $1 $2
return
fi
;;
esac
done < $TMP_DIR/policy
fatal_error "Error: No default policy for zone $1 to zone $2"
}
################################################################################
# Complete a standard chain
#
# - run any supplied user exit
# - search the policy file for an applicable policy and add rules as
# appropriate
# - If no applicable policy is found, add rules for an assummed
# policy of DROP INFO
################################################################################
complete_standard_chain() # $1 = chain, $2 = source zone, $3 = destination zone
{
local policy=
local loglevel=
run_user_exit $1
while read client server policy loglevel synparams; do
expandv client server policy loglevelsynparams
[ "x$loglevel" = "x-" ] && loglevel=
case "$client" in
all|ALL)
if [ "$server" = "$3" -o "$server" = "all" ]; then
policy_rules $1 $policy $loglevel
return
fi
;;
*)
if [ "$client" = "$2" ] && \
[ "$server" = "all" -o "$server" = "$3" ]
then
policy_rules $1 $policy $loglevel
return
fi
;;
esac
done < $TMP_DIR/policy
policy_rules $1 DROP INFO
}
################################################################################
# Find the appropriate chain to pass packets from a source zone to a #
# destination zone #
# #
# If the canonical chain for this zone pair exists, echo it's name; otherwise #
# locate and echo the name of the appropriate policy chain #
################################################################################
rules_chain() # $1 = source zone, $2 = destination zone
{
local chain=${1}2${2}
havechain $chain && { echo $chain; return; }
while read client server policy loglevel ; do
expandv client server policy loglevel
case "$client" in
all|ALL)
if [ "$server" = "$2" -o "$server" = "all" ]; then
echo all2${server}
return
fi
;;
*)
if [ "$client" = "$1" -a "$server" = "all" ]; then
echo ${client}2${server}
return
fi
;;
esac
done < $TMP_DIR/policy
fatal_error "Error: No appropriate chain for zone $1 to zone $2"
}
################################################################################
# Set up Source NAT (including masquerading) #
################################################################################
setup_masq()
{
setup_one() {
local using
if [ "$interface" = "${interface%:*}" ]; then
destnet="0.0.0.0/0"
else
destnet="${interface#*:}"
interface="${interface%:*}"
fi
if ! list_search $interface $all_interfaces; then
fatal_error "Error: Unknown interface $interface"
fi
if [ "$subnet" = "${subnet%!*}" ]; then
nomasq=
else
nomasq="${subnet#*!}"
subnet="${subnet%!*}"
fi
chain=`masq_chain $interface`
iface=
case $subnet in
[0-9]*|![0-9]*)
source="$subnet"
subnet="-s $subnet"
;;
-)
#
# Note: This only works if you have the LOCAL NAT patches in the
# kernel and in the iptables utility
#
chain=OUTPUT
subnet=
source=$FW
iface="-o $interface"
;;
*)
ipaddr="`run_ip addr show $subnet | grep 'inet '`"
source="$subnet"
if [ -z "$ipaddr" ]; then
fatal_error \
"Interface $subnet must be up before Shorewall starts"
fi
subnet="`echo $ipaddr | sed s/" "// | cut -d' ' -f2`"
[ -z "`echo "$subnet" | grep '/'`" ] && subnet="${subnet}/32"
subnet="-s $subnet"
;;
esac
if [ -n "$address" -a -n "$ADD_SNAT_ALIASES" ]; then
list_search $address $aliases_to_add || \
aliases_to_add="$aliases_to_add $address $interface"
fi
destination=$destnet
if [ -n "$nomasq" ]; then
newchain=masq${masq_seq}
run_iptables -t nat -N $newchain
addnatrule $chain -d $destnet $iface $subnet -j $newchain
masq_seq=$(($masq_seq + 1))
chain=$newchain
subnet=
iface=
destnet=
for addr in `separate_list $nomasq`; do
addnatrule $chain -s $addr -j RETURN
done
else
destnet="-d $destnet"
fi
if [ -n "$address" ]; then
addnatrule $chain $subnet $destnet $iface \
-j SNAT --to-source $address
using=" using $address"
else
addnatrule $chain $subnet $destnet $iface -j MASQUERADE
using=
fi
[ -n "$nomasq" ] && source="$source except $nomasq"
echo " To $destination from $source through ${interface}${using}"
}
strip_file masq $1
[ -n "$NAT_ENABLED" ] && echo "Masqueraded Subnets and Hosts:"
while read interface subnet address; do
expandv interface subnet address
[ -n "$NAT_ENABLED" ] && setup_one || \
error_message "Warning: NAT disabled; masq rule ignored"
done < $TMP_DIR/masq
}
################################################################################
# Setup Intrazone chain if appropriate #
################################################################################
setup_intrazone() # $1 = zone
{
eval hosts=\$${1}_hosts
if [ "$hosts" != "${hosts% *}" ] || \
have_interfaces_in_zone_with_option $1 multi
then
ensurechain ${1}2${1}
fi
}
###############################################################################
# Add a record to the blacklst chain #
# #
# $source = address match #
# $proto = protocol selector #
# $dport = destination port selector #
###############################################################################
add_blacklist_rule() {
[ -n "$BLACKLIST_LOGLEVEL" ] && \
run_iptables -A blacklst $source $proto $dport -j \
LOG $LOGPARMS --log-prefix \
"Shorewall:blacklst:$BLACKLIST_DISPOSITION:" \
--log-level $BLACKLIST_LOGLEVEL
run_iptables -A blacklst $source $proto $dport -j $disposition
}
###############################################################################
# Process a record from the blacklist file #
# #
# $subnet = address/subnet #
# $protocol = Protocol Number/Name #
# $port = Port Number/Name #
###############################################################################
process_blacklist_rec() {
local source
local addr
local proto
local dport
for addr in `separate_list $subnet`; do
case $addr in
~*)
addr=`echo $addr | sed 's/~//;s/-/:/g'`
source="--match mac --mac-source $addr"
;;
*)
source="-s $addr"
;;
esac
if [ -n "$protocol" ]; then
proto=" -p $protocol "
case $protocol in
tcp|TCP|6|udp|UDP|17)
if [ -n "$ports" ]; then
if [ -n "$MULTIPORT" -a \
"$ports" != "${ports%,*}" -a \
"$ports" = "${ports%:*}" -a \
`list_count $ports` -le 15 ]
then
dport="-m multiport --dports $ports"
add_blacklist_rule
else
for dport in `separate_list $ports`; do
dport="--dport $dport"
add_blacklist_rule
done
fi
else
add_blacklist_rule
fi
;;
icmp|ICMP|0)
if [ -n "$ports" ]; then
for dport in `separate_list $ports`; do
dport="--icmp-type $dport"
add_blacklist_rule
done
else
add_blacklist_rule
fi
;;
*)
add_blacklist_rule
;;
esac
else
add_blacklist_rule
fi
if [ -n "$ports" ]; then
addr="$addr $protocol $ports"
elif [ -n "$protocol" ]; then
addr="$addr $protocol"
fi
echo " $addr added to Black List"
done
}
###############################################################################
# Setup the Black List #
###############################################################################
setup_blacklist() {
local interfaces=`find_interfaces_by_option blacklist`
local f=`find_file blacklist`
local disposition=$BLACKLIST_DISPOSITION
if [ -n "$interfaces" -a -f $f ]; then
echo "Setting up Blacklisting..."
strip_file blacklist $f
createchain blacklst no
for interface in $interfaces; do
for chain in `first_chains $interface`; do
run_iptables -A $chain -j blacklst
done
echo " Blacklisting enabled on $interface"
done
[ "$disposition" = REJECT ] && disposition=reject
while read subnet protocol ports; do
expandv subnet protocol ports
process_blacklist_rec
done < $TMP_DIR/blacklist
fi
}
###############################################################################
# Refresh the Black List #
###############################################################################
refresh_blacklist() {
local f=`find_file blacklist`
local disposition=$BLACKLIST_DISPOSITION
if qt iptables -L blacklst -n ; then
echo "Refreshing Black List..."
strip_file blacklist $f
[ "$disposition" = REJECT ] && disposition=reject
run_iptables -F blacklst
while read subnet protocol ports; do
expandv subnet protocol ports
process_blacklist_rec
done < $TMP_DIR/blacklist
fi
}
###############################################################################
# Verify that kernel has netfilter support #
###############################################################################
verify_os_version() {
osversion=`uname -r`
case $osversion in
2.4.*|2.5.*)
;;
*)
startup_error "Shorewall version $version does not work with kernel version $osversion"
;;
esac
}
################################################################################
# Add IP Aliases #
################################################################################
add_ip_aliases()
{
do_one()
{
#
# Folks feel uneasy if they don't see all of the same
# decoration on these IP addresses that they see when their
# distro's net config tool adds them. In an attempt to reduce
# the anxiety level, we have the following code which sets
# the VLSM and BRD from the primary address
#
# Get all of the lines that contain inet addresses with broadcast
#
val=`ip addr show $interface | grep 'inet.*brd '` 2> /dev/null
if [ -n "$val" ] ; then
#
# Hack off the leading 'inet <ip addr>' (actually cut off the
# "/" as well but add it back in).
#
val="/${val#*/}"
#
# Now get the VLSM, "brd" and the broadcast address
#
val=${val%% scope*}
fi
run_ip addr add ${external}${val} dev $interface
echo "$external $interface" >> ${STATEDIR}/nat
echo " IP Address $external added to interface $interface"
}
set -- $aliases_to_add
while [ $# -gt 0 ]; do
external=$1
interface=$2
shift;shift
do_one
done
}
################################################################################
# Load kernel modules required for Shorewall #
################################################################################
load_kernel_modules() {
[ -z "$MODULESDIR" ] &&
MODULESDIR=/lib/modules/$osversion/kernel/net/ipv4/netfilter
modules=`find_file modules`
if [ -f $modules -a -d $MODULESDIR ]; then
echo "Loading Modules..."
. $modules
fi
}
################################################################################
# Perform Initialization #
# - Delete all old rules #
# - Delete all user chains #
# - Set the POLICY on all standard chains and add a rule to allow packets#
# that are part of established connections. #
# - Determine the zones
################################################################################
initialize_netfilter () {
echo "Determining Zones..."
determine_zones
[ -z "$zones" ] && startup_error "ERROR: No Zones Defined"
display_list "Zones:" $zones
echo "Validating interfaces file..."
validate_interfaces_file
echo "Validating hosts file..."
validate_hosts_file
echo "Validating Policy file..."
validate_policy
echo "Determining Hosts in Zones..."
determine_interfaces
determine_hosts
deletechain shorewall
[ -n "$NAT_ENABLED" ] && delete_nat
delete_proxy_arp
[ -n "$MANGLE_ENABLED" ] && \
run_iptables -t mangle -F && \
run_iptables -t mangle -X
[ -n "$TC_ENABLED" ] && delete_tc
run_user_exit init
echo "Deleting user chains..."
setpolicy INPUT DROP
setpolicy OUTPUT DROP
setpolicy FORWARD DROP
deleteallchains
setcontinue FORWARD
setcontinue INPUT
setcontinue OUTPUT
[ -n "$CLAMPMSS" ] && \
run_iptables -A FORWARD -p tcp \
--tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
if [ -z "$NEWNOTSYN" ]; then
createchain newnotsyn no
run_user_exit newnotsyn
[ -n "$LOGNEWNOTSYN" ] && \
run_iptables -A newnotsyn -j LOG \
--log-prefix "Shorewall:newnotsyn:DROP:" --log-level $LOGNEWNOTSYN
run_iptables -A newnotsyn -j DROP
fi
createchain icmpdef no
createchain common no
createchain reject no
createchain dynamic no
if [ -f /var/lib/shorewall/save ]; then
echo "Restoring dynamic rules..."
while read target ignore1 ignore2 address rest; do
case $target in
DROP|reject)
run_iptables -A dynamic -s $address -j $target
;;
*)
;;
esac
done < /var/lib/shorewall/save
fi
echo "Creating input Chains..."
for interface in $all_interfaces; do
createchain `forward_chain $interface` no
run_iptables -A `forward_chain $interface` -j dynamic
createchain `input_chain $interface` no
run_iptables -A `input_chain $interface` -j dynamic
done
}
################################################################################
# Construct zone-independent rules #
################################################################################
add_common_rules() {
logdisp() # $1 = Chain Name
{
echo "LOG --log-prefix "Shorewall:${1}:DROP:" --log-level info"
}
############################################################################
# Reject Rules
#
run_iptables -A reject -p tcp -j REJECT --reject-with tcp-reset
run_iptables -A reject -j REJECT
############################################################################
# dropunclean rules
#
interfaces="`find_interfaces_by_option dropunclean`"
if [ -n "$interfaces" ]; then
createchain badpkt no
if [ -n "$LOGUNCLEAN" ]; then
logoptions="$LOGPARAMS --log-prefix Shorewall:badpkt:DROP:"
logoptions="$logoptions --log-level $LOGUNCLEAN --log-ip-options"
run_iptables -A badpkt -p tcp -j LOG $logoptions --log-tcp-options
run_iptables -A badpkt -p tcp -j DROP # Workaround for iptables 1.2.7
run_iptables -A badpkt -j LOG $logoptions
fi
run_iptables -A badpkt -j DROP
echo "Mangled/Invalid Packet filtering enabled on:"
for interface in $interfaces; do
for chain in `first_chains $interface`; do
run_iptables -A $chain --match unclean -j badpkt
done
echo " $interface"
done
fi
############################################################################
# logunclean rules
#
interfaces="`find_interfaces_by_option logunclean`"
if [ -n "$interfaces" ]; then
createchain logpkt no
[ -z"$LOGUNCLEAN" ] && LOGUNCLEAN=info
logoptions="$LOGPARAMS --log-prefix Shorewall:logpkt:LOG:"
logoptions="$logoptions --log-level $LOGUNCLEAN --log-ip-options"
run_iptables -A logpkt -p tcp -j LOG $logoptions --log-tcp-options
run_iptables -A logpkt -p tcp -j RETURN # Workaround for iptables 1.2.7
run_iptables -A logpkt -j LOG $logoptions
echo "Mangled/Invalid Packet Logging enabled on:"
for interface in $interfaces; do
for chain in `first_chains $interface`; do
run_iptables -A $chain --match unclean -j logpkt
done
echo " $interface"
done
fi
###########################################################################
# PING
#
[ -n "$FORWARDPING" ] && \
run_iptables -A icmpdef -p icmp --icmp-type echo-request -j ACCEPT
############################################################################
# Common ICMP rules
#
run_user_exit icmpdef
############################################################################
# Common rules in each chain
#
common=`find_file common`
if [ -f $common ]; then
. $common
else
. `find_file common.def`
fi
###########################################################################
# BROADCASTS
#
drop_broadcasts `find_broadcasts`
###########################################################################
# DHCP
#
echo "Adding rules for DHCP"
for interface in `find_interfaces_by_option dhcp`; do
run_iptables -A `input_chain $interface` -p udp --dport 67:68 -j ACCEPT
run_iptables -A OUTPUT -o $interface -p udp --dport 67:68 -j ACCEPT
done
###########################################################################
# RFC 1918
#
norfc1918_interfaces="`find_interfaces_by_option norfc1918`"
if [ -n "$norfc1918_interfaces" ]; then
echo "Enabling RFC1918 Filtering"
strip_file rfc1918
createchain rfc1918 no
createchain logdrop no
run_iptables -A logdrop -j `logdisp rfc1918`
run_iptables -A logdrop -j DROP
if [ -n "$MANGLE_ENABLED" ]; then
####################################################################
# Mangling is enabled -- create a chain in the mangle table to
# filter RFC1918 destination addresses. This must be done in the
# mangle table before we apply any DNAT rules in the nat table
#
# Also add a chain to log and drop any RFC1918 packets that we find
#
run_iptables -t mangle -N man1918
run_iptables -t mangle -N logdrop
run_iptables -t mangle -A logdrop -j `logdisp man1918`
run_iptables -t mangle -A logdrop -j DROP
fi
while read subnet target; do
case $target in
logdrop|DROP|RETURN)
;;
*)
fatal_error " Error:Illegal target ($target) for $subnet"
;;
esac
run_iptables -A rfc1918 -s $subnet -j $target
####################################################################
# If packet mangling is enabled, trap packets with an
# RFC1918 destination
#
if [ -n "$MANGLE_ENABLED" ]; then
run_iptables -t mangle -A man1918 -d $subnet -j $target
fi
done < $TMP_DIR/rfc1918
for interface in $norfc1918_interfaces; do
for chain in `first_chains $interface`; do
run_iptables -A $chain -j rfc1918
done
[ -n "$MANGLE_ENABLED" ] && \
run_iptables -t mangle -A PREROUTING -i $interface -j man1918
done
fi
############################################################################
# Process Black List
#
setup_blacklist
############################################################################
# Enable the Loopback interface
#
run_iptables -A INPUT -i lo -j ACCEPT
run_iptables -A OUTPUT -o lo -j ACCEPT
############################################################################
# Enable icmp output
#
run_iptables -A OUTPUT -m state --state ! INVALID -p icmp -j ACCEPT
############################################################################
# Route Filtering
#
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
echo 0 > $f
done
interfaces="`find_interfaces_by_option routefilter`"
if [ -n "$interfaces" -o -n "$ROUTE_FILTER" ]; then
echo "Setting up Kernel Route Filtering..."
if [ -n "$ROUTE_FILTER" ]; then
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
else
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
for interface in $interfaces; do
file=/proc/sys/net/ipv4/conf/$interface/rp_filter
if [ -f $file ]; then
echo 1 > $file
else
error_message \
"Warning: Cannot set route filtering on $interface"
fi
done
fi
fi
############################################################################
# IP Forwarding
#
case "$IP_FORWARDING" in
[Oo][Nn])
echo 1 > /proc/sys/net/ipv4/ip_forward
echo "IP Forwarding Enabled"
;;
[Oo][Ff][Ff])
echo 0 > /proc/sys/net/ipv4/ip_forward
echo "IP Forwarding Disabled!"
;;
esac
}
################################################################################
# Scan the policy file defining the necessary chains #
# Add the appropriate policy rule(s) to the end of each canonical chain #
################################################################################
apply_policy_rules() {
############################################################################
# Create policy chains
#
while read client server policy loglevel synparams; do
expandv client server policy loglevel synparams
chain=${client}2${server}
[ -n "$synparams" ] && setup_syn_flood_chain $chain $synparams
if havechain $chain; then
[ -n "$synparams" ] && \
run_iptables -I $chain 2 -p tcp --syn -j @$chain
else
#
# A wild-card rule. Create the chain and add policy
# rules
#
# We must include the ESTABLISHED and RELATED state
# rule here to account for replys and reverse
# related sessions associated with sessions going
# in the other direction
#
createchain $chain
[ "$client" = "all" -o "$server" = "all" ] && \
policy_rules $chain $policy $loglevel
[ -n "$synparams" ] && \
[ $policy = ACCEPT -o $policy = CONTINUE ] && \
run_iptables -I $chain 2 -p tcp --syn -j @$chain
fi
done < $TMP_DIR/policy
############################################################################
# Add policy rules to canonical chains
#
for zone in $FW $zones; do
setup_intrazone $zone
for zone1 in $FW $zones; do
chain=${zone}2${zone1}
if havechain $chain; then
run_user_exit $chain
default_policy $zone $zone1
fi
done
done
}
################################################################################
# Activate the rules #
################################################################################
activate_rules()
{
local PREROUTING_rule=1
local POSTROUTING_rule=1
############################################################################
# Jump to a NAT chain from one of the builtin nat chains
#
addnatjump() # $1 = BUILTIN chain, $2 = user chain, $3 - * other arguments
{
local sourcechain=$1 destchain=$2
shift
shift
havenatchain $destchain && \
run_iptables -t nat -A $sourcechain $@ -j $destchain
}
############################################################################
# Jump to a RULES chain from one of the builtin nat chains
#---------------------------------------------------------------------------
# If NAT_BEFORE_RULES then append the rule to the chain; otherwise, insert
# the jump near the front of the builtin chain
#
addrulejump() # $1 = BUILTIN chain, $2 = user chain, $3 - * other arguments
{
local sourcechain=$1 destchain=$2
shift
shift
if havenatchain $destchain; then
if [ -n "$NAT_BEFORE_RULES" ]; then
run_iptables -t nat -A $sourcechain $@ -j $destchain
else
eval run_iptables -t nat -I $sourcechain \
\$${sourcechain}_rule $@ -j $destchain
eval ${sourcechain}_rule=\$\(\(\$${sourcechain}_rule + 1\)\)
fi
fi
}
#
# Add jumps from the builtin chains to the nat chains
#
addnatjump PREROUTING nat_in
addnatjump POSTROUTING nat_out
for interface in $all_interfaces; do
addnatjump PREROUTING `input_chain $interface` -i $interface
addnatjump POSTROUTING `output_chain $interface` -o $interface
done
multi_interfaces=`find_interfaces_by_option multi`
for zone in $zones; do
eval source_hosts=\$${zone}_hosts
for host in $source_hosts; do
interface=${host%:*}
subnet=${host#*:}
run_iptables -A OUTPUT -o \
$interface -d $subnet -j `rules_chain $FW $zone`
#
# Add jumps from the builtin chains for DNAT and SNAT rules
#
addrulejump PREROUTING `dnat_chain $zone` -i $interface -s $subnet
addrulejump POSTROUTING `snat_chain $zone` -o $interface -d $subnet
run_iptables -A `input_chain $interface` -s $subnet \
-j `rules_chain $zone $FW`
done
for zone1 in $zones; do
eval dest_hosts=\$${zone1}_hosts
chain="`rules_chain $zone $zone1`"
if havechain ${zone}2${zone1} || havechain ${zone1}2${zone}; then
have_canonical=Yes
else
have_canonical=
fi
for host in $source_hosts; do
interface=${host%:*}
subnet=${host#*:}
chain1=`forward_chain $interface`
if [ -n "$have_canonical" ]; then
multi=yes
else
case $interface in
*+*)
multi=yes
;;
*)
list_search $interface $multi_interfaces && multi=yes || multi=
;;
esac
fi
for host1 in $dest_hosts; do
interface1=${host1%:*}
subnet1=${host1#*:}
if [ $interface != $interface1 -o -n "$multi" ]; then
run_iptables -A $chain1 -s $subnet \
-o $interface1 -d $subnet1 -j $chain
fi
done
done
done
done
for interface in $all_interfaces; do
run_iptables -A FORWARD -i $interface -j `forward_chain $interface`
run_iptables -A INPUT -i $interface -j `input_chain $interface`
addnatjump POSTROUTING `masq_chain $interface` -o $interface
done
complete_standard_chain INPUT all $FW
complete_standard_chain OUTPUT $FW all
complete_standard_chain FORWARD all all
run_iptables -D INPUT 1
run_iptables -D OUTPUT 1
run_iptables -D FORWARD 1
}
################################################################################
# Start/Restart the Firewall #
################################################################################
define_firewall() # $1 = Command (Start or Restart)
{
echo "${1}ing Shorewall..."
verify_os_version
load_kernel_modules
echo "Initializing..."
initialize_netfilter
echo "Configuring Proxy ARP"
setup_proxy_arp
setup_nat
echo "Adding Common Rules"
add_common_rules
tunnels=`find_file tunnels`
[ -f $tunnels ] && \
echo "Processing $tunnels..." && setup_tunnels $tunnels
rules=`find_file rules`
echo "Processing $rules..."
process_rules $rules
echo "Setting up ICMP Echo handling..."
filterping_interfaces="`find_interfaces_by_option filterping`"
noping_interfaces="`find_interfaces_by_option noping`"
for interface in $all_interfaces; do
if ! list_search $interface $filterping_interfaces; then
if list_search $interface $noping_interfaces; then
target=DROP
else
target=ACCEPT
fi
run_iptables -A `input_chain $interface` \
-p icmp --icmp-type echo-request -j $target
fi
done
policy=`find_file policy`
echo "Processing $policy..."
apply_policy_rules
masq=`find_file masq`
[ -f $masq ] && setup_masq $masq
tos=`find_file tos`
[ -f $tos ] && [ -n "$MANGLE_ENABLED" ] && process_tos $tos
[ -n "$TC_ENABLED" ] && setup_tc
echo "Activating Rules..."
activate_rules
[ -n "$aliases_to_add" ] && \
echo "Adding IP Addresses..." && \
add_ip_aliases
run_user_exit start
createchain shorewall no
date > /var/lib/shorewall/restarted
report "Shorewall ${1}ed"
rm -rf $TMP_DIR
}
################################################################################
# Check the configuration #
################################################################################
check_config() {
echo "Verifying Configuration..."
verify_os_version
load_kernel_modules
echo "Determining Zones..."
determine_zones
[ -z "$zones" ] && startup_error "ERROR: No Zones Defined"
display_list "Zones:" $zones
echo "Validating interfaces file..."
validate_interfaces_file
echo "Validating hosts file..."
validate_hosts_file
echo "Determining Hosts in Zones..."
determine_interfaces
determine_hosts
echo "Validating rules file..."
validate_rules
echo "Validating policy file..."
validate_policy
rm -rf $TMP_DIR
echo "Configuration Validated"
}
################################################################################
# Rebuild the common chain #
################################################################################
refresh_firewall()
{
echo "Refreshing Shorewall..."
echo "Determining Zones and Interfaces..."
determine_zones
[ -z "$zones" ] && startup_error "ERROR: No Zones Defined"
determine_interfaces
run_user_exit refresh
run_iptables -F common
echo "Adding Common Rules"
############################################################################
# Common rules in each chain
#
common=`find_file common`
if [ -f $common ]; then
. $common
else
. `find_file common.def`
fi
###########################################################################
# BROADCASTS
#
drop_broadcasts `find_broadcasts`
###########################################################################
# Blacklist
#
refresh_blacklist
report "Shorewall Refreshed"
rm -rf $TMP_DIR
}
################################################################################
# Determine the value for a parameter that defaults to Yes #
################################################################################
added_param_value_yes() # $1 = Parameter Name, $2 = Parameter value
{
local val="$2"
if [ -z "$val" ]; then
echo "Yes"
else case $val in
[Yy][Ee][Ss])
echo "Yes"
;;
[Nn][Oo])
echo ""
;;
*)
startup_error "Invalid value ($val) for $1"
;;
esac
fi
}
################################################################################
# Determine the value for a parameter that defaults to No #
################################################################################
added_param_value_no() # $1 = Parameter Name, $2 = Parameter value
{
local val="$2"
if [ -z "$val" ]; then
echo ""
else case $val in
[Yy][Ee][Ss])
echo "Yes"
;;
[Nn][Oo])
echo ""
;;
*)
startup_error "Invalid value ($val) for $1"
;;
esac
fi
}
################################################################################
# Initialize this program #
################################################################################
do_initialize() {
# Run all utility programs using the C locale
#
# Thanks to Vincent Planchenault for this tip #
export LC_ALL=C
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
############################################################################
# Clear all configuration variables
#
version=
FW=
SUBSYSLOCK=
STATEDIR=
ALLOWRELATED=
LOGRATE=
LOGBURST=
LOGPARMS=
NAT_ENABLED=
MANGLE_ENABLED=
ADD_IP_ALIASES=
ADD_SNAT_ALIASES=
TC_ENABLED=
LOGUNCLEAN=
BLACKLIST_DISPOSITION=
BLACKLIST_LOGLEVEL=
CLAMPMSS=
ROUTE_FILTER=
NAT_BEFORE_RULES=
MULTIPORT=
DETECT_DNAT_IPADDRS=
MERGE_HOSTS=
MUTEX_TIMEOUT=
NEWNOTSYN=
LOGNEWNOTSYN=
FORWARDPING=
stopping=
have_mutex=
masq_seq=1
nonat_seq=1
aliases_to_add=
TMP_DIR=/tmp/shorewall-$$
rm -rf $TMP_DIR
mkdir -p $TMP_DIR && chmod 700 $TMP_DIR || \
startup_error "Can't create $TMP_DIR"
trap "rm -rf $TMP_DIR; my_mutex_off; exit 2" 1 2 3 4 5 6 9
functions=/var/lib/shorewall/functions
if [ -f $functions ]; then
. $functions
else
startup_error "$functions does not exist!"
fi
version_file=/var/lib/shorewall/version
[ -f $version_file ] && version=`cat $version_file`
#
# Strip the files that we use often
#
strip_file interfaces
strip_file hosts
run_user_exit shorewall.conf
run_user_exit params
[ -z "${STATEDIR}" ] && STATEDIR=/var/state/shorewall
[ -d $STATEDIR ] || mkdir -p $STATEDIR
[ -z "$FW" ] && FW=fw
ALLOWRELATED="`added_param_value_yes ALLOWRELATED $ALLOWRELATED`"
NAT_ENABLED="`added_param_value_yes NAT_ENABLED $NAT_ENABLED`"
MANGLE_ENABLED="`added_param_value_yes MANGLE_ENABLED $MANGLE_ENABLED`"
ADD_IP_ALIASES="`added_param_value_yes ADD_IP_ALIASES $ADD_IP_ALIASES`"
TC_ENABLED="`added_param_value_yes TC_ENABLED $TC_ENABLED`"
if [ -n "${LOGRATE}${LOGBURST}" ]; then
LOGPARMS="--match limit"
[ -n "$LOGRATE" ] && LOGPARMS="$LOGPARMS --limit $LOGRATE"
[ -n "$LOGBURST" ] && LOGPARMS="$LOGPARMS --limit-burst $LOGBURST"
fi
if [ -n "$IP_FORWARDING" ]; then
case "$IP_FORWARDING" in
[Oo][Nn]|[Oo][Ff][Ff]|[Kk][Ee][Ee][Pp])
;;
*)
startup_error "Invalid value ($IP_FORWARDING) for IP_FORWARDING"
;;
esac
else
IP_FORWARDING=On
fi
if [ -n "$TC_ENABLED" -a -z "$MANGLE_ENABLED" ]; then
startup_error "Traffic Control requires Mangle"
fi
[ -z "$BLACKLIST_DISPOSITION" ] && BLACKLIST_DISPOSITION=DROP
CLAMPMSS=`added_param_value_no CLAMPMSS $CLAMPMSS`
ADD_SNAT_ALIASES=`added_param_value_no ADD_SNAT_ALIASES $ADD_SNAT_ALIASES`
ROUTE_FILTER=`added_param_value_no ROUTE_FILTER $ROUTE_FILTER`
NAT_BEFORE_RULES=`added_param_value_yes NAT_BEFORE_RULES $NAT_BEFORE_RULES`
MULTIPORT=`added_param_value_no MULTIPORT $MULTIPORT`
DETECT_DNAT_IPADDRS=`added_param_value_no DETECT_DNAT_IPADDRS $DETECT_DNAT_IPADDRS`
MERGE_HOSTS=`added_param_value_no MERGE_HOSTS $MERGE_HOSTS`
FORWARDPING=`added_param_value_no FORWARDPING $FORWARDPING`
NEWNOTSYN=`added_param_value_yes NEWNOTSYN $NEWNOTSYN`
}
################################################################################
# Give Usage Information #
################################################################################
usage() {
echo "Usage: $0 [debug] {start|stop|reset|restart|status|refresh|clear]}"
exit 1
}
################################################################################
# E X E C U T I O N B E G I N S H E R E #
################################################################################
#
# Start trace if first arg is "debug"
#
[ $# -gt 1 ] && [ "$1" = "debug" ] && { set -x ; shift ; }
nolock=
[ $# -gt 1 ] && [ "$1" = "nolock" ] && { nolock=Yes; shift ; }
[ $# -ne 1 ] && usage
trap "my_mutex_off; exit 2" 1 2 3 4 5 6 9
command="$1"
case "$command" in
stop)
do_initialize
my_mutex_on
echo -n "Stopping Shorewall..."
determine_zones
stop_firewall
[ -n "$SUBSYSLOCK" ] && rm -f $SUBSYSLOCK
echo "done."
my_mutex_off
;;
start)
do_initialize
my_mutex_on
if qt iptables -L shorewall -n ; then
[ -n "$SUBSYSLOCK" ] && touch $SUBSYSLOCK
echo "Shorewall Already Started"
my_mutex_off
exit 0;
fi
define_firewall "Start" && [ -n "$SUBSYSLOCK" ] && touch $SUBSYSLOCK
my_mutex_off
;;
restart)
do_initialize
my_mutex_on
if qt iptables -L shorewall -n ; then
define_firewall "Restart"
else
echo "Shorewall Not Currently Running"
define_firewall "Start"
fi
[ $? -eq 0 ] && [ -n "$SUBSYSLOCK" ] && touch $SUBSYSLOCK
my_mutex_off
;;
status)
echo -e "Shorewall-$version Status at $HOSTNAME - `date`\\n"
iptables -L -n -v
;;
reset)
iptables -L -n -Z -v
report "Shorewall Counters Reset"
date > /var/lib/shorewall/restarted
;;
refresh)
do_initialize
my_mutex_on
if ! qt iptables -L shorewall -n ; then
echo "Shorewall Not Started"
my_mutex_off
exit 2;
fi
refresh_firewall;
my_mutex_off
;;
clear)
do_initialize
my_mutex_on
echo -n "Clearing Shorewall..."
determine_zones
clear_firewall
[ -n "$SUBSYSLOCK" ] && rm -f $SUBSYSLOCK
echo "done."
my_mutex_off
;;
check)
do_initialize
check_config
;;
*)
usage
;;
esac