mirror of
https://gitlab.com/shorewall/code.git
synced 2024-12-28 09:08:48 +01:00
b66929a65e
1) Elimination of the "shorewall monitor" command. 2) The /etc/shorewall/ipsec and /etc/shorewall/zones file are combined into a single /etc/shorewall/zones file. This is done in an upwardly-compatible way so that current users can continue to use their existing files. 3) Support has been added for the arp_ignore interface option. 4) DROPINVALID has been removed from shorewall.conf. Behavior is as if DROPINVALID=No was specified. 5) The 'nobogons' option and BOGON_LOG_LEVEL are removed. 6) Error and warning messages have been made easier to spot by using capitalization (e.g., ERROR: and WARNING:). 7) The /etc/shorewall/policy file now contains a new connection policy and a policy for ESTABLISHED packets. Useful for users of snort-inline who want to pass all packets to the QUEUE target. 8) A new 'critical' option has been added to /etc/shorewall/routestopped. Shorewall insures communication between the firewall and 'critical' hosts throughout start, restart, stop and clear. Useful for diskless firewall's with NFS-mounted file systems, LDAP servers, Crossbow, etc. 9) Macros. Macros are very similar to actions but are easier to use, allow parameter substitution and are more efficient. Almost all of the standard actions have been converted to macros in the EXPERIMENTAL branch. 10) The default value of ADD_IP_ALIASES in shorewall.conf is changed to No. 11) If you have 'make' installed on your firewall, then when you use the '-f' option to 'shorewall start' (as happens when you reboot), if your /etc/shorewall/ directory contains files that were modified after Shorewall was last restarted then Shorewall is started using the config files rather than using the saved configuration. git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@2409 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
8855 lines
210 KiB
Bash
Executable File
8855 lines
210 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# The Shoreline Firewall (Shorewall) Packet Filtering Firewall - V2.6
|
|
#
|
|
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
|
|
#
|
|
# (c) 1999,2000,2001,2002,2003,2004,2005 - Tom Eastep (teastep@shorewall.net)
|
|
#
|
|
# 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 iptables 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.
|
|
#
|
|
# Mutual exclusion -- These functions are jackets for the mutual exclusion
|
|
# routines in $FUNCTIONS. They invoke
|
|
# the corresponding function in that file if the user did
|
|
# not specify "nolock" on the runline.
|
|
#
|
|
my_mutex_on() {
|
|
[ -n "$nolock" ] || { mutex_on; have_mutex=Yes; }
|
|
}
|
|
|
|
my_mutex_off() {
|
|
[ -n "$have_mutex" ] && { mutex_off; have_mutex=; }
|
|
}
|
|
|
|
#
|
|
# Message to stderr
|
|
#
|
|
error_message() # $* = Error Message
|
|
{
|
|
echo " $@" >&2
|
|
}
|
|
|
|
#
|
|
# Fatal error -- stops the firewall after issuing the error message
|
|
#
|
|
fatal_error() # $* = Error Message
|
|
{
|
|
echo " ERROR: $@" >&2
|
|
if [ $COMMAND = check ]; then
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
else
|
|
stop_firewall
|
|
fi
|
|
exit 2
|
|
}
|
|
|
|
#
|
|
# Fatal error during startup -- generate an error message and abend without
|
|
# altering the state of the firewall
|
|
#
|
|
startup_error() # $* = Error Message
|
|
{
|
|
echo " ERROR: $@" >&2
|
|
my_mutex_off
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
[ -n "$RESTOREBASE" ] && rm -f $RESTOREBASE
|
|
kill $$
|
|
exit 2
|
|
}
|
|
|
|
#
|
|
# Send a message to STDOUT and the System Log
|
|
#
|
|
report () { # $* = message
|
|
echo "$@"
|
|
logger "$@"
|
|
}
|
|
|
|
#
|
|
# Write the passed args to $RESTOREBASE
|
|
#
|
|
save_command()
|
|
{
|
|
echo "$@" >> $RESTOREBASE
|
|
}
|
|
|
|
#
|
|
# Write a progress_message command to $RESTOREBASE
|
|
#
|
|
save_progress_message()
|
|
{
|
|
|
|
echo >> $RESTOREBASE
|
|
echo "progress_message \"$@\"" >> $RESTOREBASE
|
|
echo >> $RESTOREBASE
|
|
}
|
|
|
|
#
|
|
# Save the passed command in the restore script then run it -- returns the status of the command
|
|
# If the command involves file redirection then it must be enclosed in quotes as in:
|
|
#
|
|
# run_and_save_command "echo 1 > /proc/sys/net/ipv4/ip_forward"
|
|
#
|
|
run_and_save_command()
|
|
{
|
|
echo "$@" >> $RESTOREBASE
|
|
eval $*
|
|
}
|
|
|
|
#
|
|
# Run the passed command and if it succeeds, save it in the restore script. If it fails, stop the firewall and die
|
|
#
|
|
ensure_and_save_command()
|
|
{
|
|
if eval $* ; then
|
|
echo "$@" >> $RESTOREBASE
|
|
else
|
|
[ -z "$stopping" ] && { stop_firewall; exit 2; }
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Append a file in /var/lib/shorewall to $RESTOREBASE
|
|
#
|
|
append_file() # $1 = File Name
|
|
{
|
|
save_command "cat > /var/lib/shorewall/$1 << __EOF__"
|
|
cat /var/lib/shorewall/$1 >> $RESTOREBASE
|
|
save_command __EOF__
|
|
}
|
|
|
|
#
|
|
# Run iptables and if an error occurs, stop the firewall and quit
|
|
#
|
|
run_iptables() {
|
|
|
|
[ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
|
|
[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
|
|
|
|
if ! $IPTABLES $@ ; then
|
|
if [ -z "$stopping" ]; then
|
|
error_message "ERROR: Command \"$IPTABLES $@\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Version of 'run_iptables' that inserts white space after "!" in the arg list
|
|
#
|
|
run_iptables2() {
|
|
|
|
case "$@" in
|
|
*!*)
|
|
run_iptables $(fix_bang $@)
|
|
;;
|
|
*)
|
|
run_iptables $@
|
|
;;
|
|
esac
|
|
|
|
}
|
|
|
|
#
|
|
# Quietly run iptables
|
|
#
|
|
qt_iptables() {
|
|
|
|
[ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
|
|
[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
|
|
|
|
qt $IPTABLES $@
|
|
}
|
|
|
|
#
|
|
# Run ip and if an error occurs, stop the firewall and quit
|
|
#
|
|
run_ip() {
|
|
if ! ip $@ ; then
|
|
if [ -z "$stopping" ]; then
|
|
error_message "ERROR: Command \"ip $@\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Run tc and if an error occurs, stop the firewall and quit
|
|
#
|
|
run_tc() {
|
|
if ! tc $@ ; then
|
|
if [ -z "$stopping" ]; then
|
|
error_message "ERROR: Command \"tc $@\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Run ipset and if an error occurs, stop the firewall and quit
|
|
#
|
|
run_ipset() {
|
|
if ! ipset $@ ; then
|
|
if [ -z "$stopping" ]; then
|
|
error_message "ERROR: Command \"ipset $@\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
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 exists_${1} and set its value to Yes to indicate that the chain now
|
|
# exists.
|
|
#
|
|
createchain() # $1 = chain name, $2 = If "yes", create newnotsyn rule
|
|
{
|
|
local c=$(chain_base $1)
|
|
|
|
run_iptables -N $1
|
|
|
|
if [ $2 = yes ]; then
|
|
[ -z "$NEWNOTSYN" ] && \
|
|
run_iptables -A $1 -m state --state NEW -p tcp ! --syn -j newnotsyn
|
|
fi
|
|
|
|
eval exists_${c}=Yes
|
|
}
|
|
|
|
createchain2() # $1 = chain name, $2 = If "yes", create default rules
|
|
{
|
|
local c=$(chain_base $1)
|
|
|
|
if $IPTABLES -N $1; then
|
|
|
|
if [ $2 = yes ]; then
|
|
[ -z "$NEWNOTSYN" ] && \
|
|
run_iptables -A $1 -m state --state NEW -p tcp ! --syn -j newnotsyn
|
|
fi
|
|
|
|
eval exists_${c}=Yes
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Determine if a chain exists
|
|
#
|
|
# When we create a chain "x", we create a variable named exists_x 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
|
|
{
|
|
local c=$(chain_base $1)
|
|
|
|
eval test \"\$exists_${c}\" = Yes
|
|
}
|
|
|
|
#
|
|
# Query NetFilter about the existence of a filter chain
|
|
#
|
|
chain_exists() # $1 = chain name
|
|
{
|
|
qt $IPTABLES -L $1 -n
|
|
}
|
|
|
|
#
|
|
# Query NetFilter about the existence of a mangle chain
|
|
#
|
|
mangle_chain_exists() # $1 = chain name
|
|
{
|
|
qt $IPTABLES -t mangle -L $1 -n
|
|
}
|
|
|
|
#
|
|
# Ensure that a chain exists (create it if it doesn't)
|
|
#
|
|
ensurechain() # $1 = chain name
|
|
{
|
|
havechain $1 || createchain $1 yes
|
|
}
|
|
|
|
ensurechain1() # $1 = chain name
|
|
{
|
|
havechain $1 || createchain $1 no
|
|
}
|
|
|
|
#
|
|
# 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 $@
|
|
}
|
|
|
|
addrule2() # $1 = chain name, remainder of arguments specify the rule
|
|
{
|
|
ensurechain $1
|
|
run_iptables2 -A $@
|
|
}
|
|
|
|
#
|
|
# Create a nat chain
|
|
#
|
|
# Create a variable exists_nat_${1} and set its value to Yes to indicate that
|
|
# the chain now exists.
|
|
#
|
|
createnatchain() # $1 = chain name
|
|
{
|
|
run_iptables -t nat -N $1
|
|
|
|
eval exists_nat_${1}=Yes
|
|
}
|
|
|
|
#
|
|
# Determine if a nat chain exists
|
|
#
|
|
# When we create a chain "chain", we create a variable named exists_nat_chain
|
|
# 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 \"\$exists_nat_${1}\" = Yes
|
|
}
|
|
|
|
#
|
|
# Ensure that a nat 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_iptables2 -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
|
|
}
|
|
|
|
#
|
|
# Determine if a chain is a policy chain
|
|
#
|
|
is_policy_chain() # $1 = name of chain
|
|
{
|
|
eval test \"\$${1}_is_policy\" = Yes
|
|
}
|
|
|
|
#
|
|
# 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 and related connections
|
|
#
|
|
setcontinue() # $1 = name of chain
|
|
{
|
|
run_iptables -A $1 -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
}
|
|
|
|
#
|
|
# Flush one of the NAT table chains
|
|
#
|
|
flushnat() # $1 = name of chain
|
|
{
|
|
run_iptables -t nat -F $1
|
|
}
|
|
|
|
#
|
|
# Flush one of the Mangle table chains
|
|
#
|
|
flushmangle() # $1 = name of chain
|
|
{
|
|
run_iptables -t mangle -F $1
|
|
}
|
|
|
|
#
|
|
# Find the zones
|
|
#
|
|
find_zones() # $1 = name of the zone file
|
|
{
|
|
local zone rest
|
|
|
|
while read zone rest; do
|
|
expandv zone
|
|
|
|
[ ${#zone} -gt 5 ] && startup_error "Zone name longer than 5 characters: $zone"
|
|
|
|
case "$zone" in
|
|
[0-9*])
|
|
startup_error "Illegal zone name \"$zone\" in zones file"
|
|
;;
|
|
$FW|all|none)
|
|
startup_error "Reserved zone name \"$zone\" in zones file ignored"
|
|
;;
|
|
*)
|
|
echo $zone
|
|
;;
|
|
esac
|
|
done < $1
|
|
}
|
|
|
|
#
|
|
# This function assumes that the TMP_DIR variable is set and that
|
|
# its value named an existing directory.
|
|
#
|
|
determine_zones()
|
|
{
|
|
local zonefile=$(find_file zones) zones=
|
|
|
|
strip_file zones $zonefile
|
|
|
|
ZONES=
|
|
zones=$(find_zones $TMP_DIR/zones)
|
|
|
|
for zone in $zones; do
|
|
list_search $zone $ZONES && startup_error "Zone $zone is defined more than once"
|
|
ZONES="$ZONES $zone"
|
|
done
|
|
|
|
[ -z "$ZONES" ] && startup_error "ERROR: No Zones Defined"
|
|
}
|
|
|
|
#
|
|
# Find interfaces to a given zone
|
|
#
|
|
# Search the variables representing the contents of 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
|
|
local z
|
|
local interface
|
|
|
|
for interface in $ALL_INTERFACES; do
|
|
eval z=\$$(chain_base $interface)_zone
|
|
[ "x${z}" = x${zne} ] && echo $interface
|
|
done
|
|
}
|
|
|
|
#
|
|
# 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
|
|
}
|
|
|
|
#
|
|
# MAC Verification Chain for an interface
|
|
#
|
|
mac_chain() # $1 = interface
|
|
{
|
|
echo $(chain_base $1)_mac
|
|
}
|
|
|
|
macrecent_target() # $1 - interface
|
|
{
|
|
[ -n "$MACLIST_TTL" ] && echo $(chain_base $1)_rec || echo RETURN
|
|
}
|
|
|
|
#
|
|
# Functions for creating dynamic zone rules
|
|
#
|
|
dynamic_fwd() # $1 = interface
|
|
{
|
|
echo $(chain_base $1)_dynf
|
|
}
|
|
|
|
dynamic_in() # $1 = interface
|
|
{
|
|
echo $(chain_base $1)_dyni
|
|
}
|
|
|
|
dynamic_out() # $1 = interface
|
|
{
|
|
echo $(chain_base $1)_dyno
|
|
}
|
|
|
|
dynamic_chains() #$1 = interface
|
|
{
|
|
local c=$(chain_base $1)
|
|
|
|
echo ${c}_dyni ${c}_dynf ${c}_dyno
|
|
}
|
|
|
|
#
|
|
# DNAT Chain from a zone
|
|
#
|
|
dnat_chain() # $1 = zone
|
|
{
|
|
echo ${1}_dnat
|
|
}
|
|
|
|
#
|
|
# SNAT Chain to a zone or from an interface
|
|
#
|
|
snat_chain() # $1 = zone or interface
|
|
{
|
|
echo $(chain_base $1)_snat
|
|
}
|
|
|
|
#
|
|
# ECN Chain to an interface
|
|
#
|
|
ecn_chain() # $1 = interface
|
|
{
|
|
echo $(chain_base $1)_ecn
|
|
}
|
|
|
|
#
|
|
# First chains for an interface
|
|
#
|
|
first_chains() #$1 = interface
|
|
{
|
|
local c=$(chain_base $1)
|
|
|
|
echo ${c}_fwd ${c}_in
|
|
}
|
|
|
|
#
|
|
# Horrible hack to work around an iptables limitation
|
|
#
|
|
iprange_echo()
|
|
{
|
|
if [ -f $TMP_DIR/iprange ]; then
|
|
echo $@
|
|
else
|
|
echo "-m iprange $@"
|
|
> $TMP_DIR/iprange
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Get set flags (ipsets).
|
|
#
|
|
get_set_flags() # $1 = set name and optional [levels], $2 = src or dst
|
|
{
|
|
local temp setname=$1 options=$2
|
|
|
|
[ -n "$IPSET_MATCH" ] || fatal_error "Your kernel and/or iptables does not include ipset match: $1"
|
|
|
|
case $1 in
|
|
*\[[1-6]\])
|
|
temp=${1#*\[}
|
|
temp=${temp%\]}
|
|
setname=${1%\[*}
|
|
while [ $temp -gt 1 ]; do
|
|
options="$options,$2"
|
|
temp=$(($temp - 1))
|
|
done
|
|
;;
|
|
*\[*\])
|
|
options=${1#*\[}
|
|
options=${options%\]}
|
|
setname=${1%\[*}
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
|
|
echo "--set ${setname#+} $options"
|
|
}
|
|
|
|
#
|
|
# Source IP range
|
|
#
|
|
source_ip_range() # $1 = Address or Address Range
|
|
{
|
|
case $1 in
|
|
*.*.*.*-*.*.*.*)
|
|
case $1 in
|
|
!*)
|
|
iprange_echo "! --src-range ${1#!}"
|
|
;;
|
|
*)
|
|
iprange_echo "--src-range $1"
|
|
;;
|
|
esac
|
|
;;
|
|
!+*)
|
|
echo "-m set ! $(get_set_flags ${1#!} src)"
|
|
;;
|
|
+*)
|
|
echo "-m set $(get_set_flags $1 src)"
|
|
;;
|
|
*)
|
|
echo "-s $1"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
#
|
|
# Destination IP range
|
|
#
|
|
dest_ip_range() # $1 = Address or Address Range
|
|
{
|
|
case $1 in
|
|
*.*.*.*-*.*.*.*)
|
|
case $1 in
|
|
!*)
|
|
iprange_echo "! --dst-range ${1#!}"
|
|
;;
|
|
*)
|
|
iprange_echo "--dst-range $1"
|
|
;;
|
|
esac
|
|
;;
|
|
!+*)
|
|
echo "-m set ! $(get_set_flags ${1#!} dst)"
|
|
;;
|
|
+*)
|
|
echo "-m set $(get_set_flags $1 dst)"
|
|
;;
|
|
*)
|
|
echo "-d $1"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
both_ip_ranges() # $1 = Source address or range, $2 = dest address or range
|
|
{
|
|
local rangeprefix= setprefix= rangematch= setmatch=
|
|
|
|
case $1 in
|
|
*.*.*.*-*.*.*.*)
|
|
rangeprefix="-m iprange"
|
|
rangematch="--src-range $1"
|
|
;;
|
|
!+*)
|
|
setprefix="-m set"
|
|
setmatch="! $(get_set_flags ${1#!} src)"
|
|
;;
|
|
+*)
|
|
setprefix="-m set"
|
|
setmatch="$(get_set_flags $1 src)"
|
|
;;
|
|
*)
|
|
rangematch="-s $1"
|
|
;;
|
|
esac
|
|
|
|
case $2 in
|
|
*.*.*.*-*.*.*.*)
|
|
rangeprefix="-m iprange"
|
|
rangematch="$rangematch --dst-range $2"
|
|
;;
|
|
!+*)
|
|
setprefix="-m set"
|
|
match="$setmatch ! $(get_set_flags ${2#!} dst)"
|
|
;;
|
|
+*)
|
|
setprefix="-m set"
|
|
setmatch="$setmatch $(get_set_flags $2 dst)"
|
|
;;
|
|
*)
|
|
rangematch="$rangematch -d $2"
|
|
;;
|
|
esac
|
|
|
|
echo "$rangeprefix $rangematch $setprefix $setmatch"
|
|
}
|
|
|
|
#
|
|
# Horrible hack to work around an iptables limitation
|
|
#
|
|
physdev_echo()
|
|
{
|
|
if [ -f $TMP_DIR/physdev ]; then
|
|
echo $@
|
|
else
|
|
echo -m physdev $@
|
|
> $TMP_DIR/physdev
|
|
fi
|
|
}
|
|
|
|
#
|
|
# We allow hosts to be specified by IP address or by physdev. These two functions
|
|
# are used to produce the proper match in a netfilter rule.
|
|
#
|
|
match_source_hosts()
|
|
{
|
|
if [ -n "$BRIDGING" ]; then
|
|
case $1 in
|
|
*:*)
|
|
physdev_echo "--physdev-in ${1%:*} $(source_ip_range ${1#*:})"
|
|
;;
|
|
*.*.*.*|+*|!+*)
|
|
echo $(source_ip_range $1)
|
|
;;
|
|
*)
|
|
physdev_echo "--physdev-in $1"
|
|
;;
|
|
esac
|
|
else
|
|
echo $(source_ip_range $1)
|
|
fi
|
|
}
|
|
|
|
match_dest_hosts()
|
|
{
|
|
if [ -n "$BRIDGING" ]; then
|
|
case $1 in
|
|
*:*)
|
|
physdev_echo "--physdev-out ${1%:*} $(dest_ip_range ${1#*:})"
|
|
;;
|
|
*.*.*.*|+*|!+*)
|
|
echo $(dest_ip_range $1)
|
|
;;
|
|
*)
|
|
physdev_echo "--physdev-out $1"
|
|
;;
|
|
esac
|
|
else
|
|
echo $(dest_ip_range $1)
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Similarly, the source or destination in a rule can be qualified by a device name. If
|
|
# the device is defined in /etc/shorewall/interfaces then a normal interface match is
|
|
# generated (-i or -o); otherwise, a physdev match is generated.
|
|
#-------------------------------------------------------------------------------------
|
|
#
|
|
# loosely match the passed interface with those in /etc/shorewall/interfaces.
|
|
#
|
|
known_interface() # $1 = interface name
|
|
{
|
|
local iface
|
|
|
|
for iface in $ALL_INTERFACES ; do
|
|
if if_match $iface $1 ; then
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
match_source_dev()
|
|
{
|
|
if [ -n "$BRIDGING" ]; then
|
|
list_search $1 $all_ports && physdev_echo "--physdev-in $1" || echo -i $1
|
|
else
|
|
echo -i $1
|
|
fi
|
|
}
|
|
|
|
match_dest_dev()
|
|
{
|
|
if [ -n "$BRIDGING" ]; then
|
|
list_search $1 $all_ports && physdev_echo "--physdev-out $1" || echo -o $1
|
|
else
|
|
echo -o $1
|
|
fi
|
|
}
|
|
|
|
verify_interface()
|
|
{
|
|
known_interface $1 || { [ -n "$BRIDGING" ] && list_search $1 $all_ports ; }
|
|
}
|
|
|
|
#
|
|
# Determine if communication to/from a host is encrypted using IPSEC
|
|
#
|
|
is_ipsec_host() # $1 = zone, $2 = host
|
|
{
|
|
eval local is_ipsec=\$${1}_is_ipsec
|
|
eval local hosts=\"\$${1}_ipsec_hosts\"
|
|
|
|
test -n "$is_ipsec" || list_search $2 $hosts
|
|
}
|
|
|
|
#
|
|
# Generate a match for decrypted packets
|
|
#
|
|
match_ipsec_in() # $1 = zone, $2 = host
|
|
{
|
|
if is_ipsec_host $1 $2 ; then
|
|
eval local options=\"\$${1}_ipsec_options \$${1}_ipsec_in_options\"
|
|
echo "-m policy --pol ipsec --dir in $options"
|
|
elif [ -n "$POLICY_MATCH" ]; then
|
|
echo "-m policy --pol none --dir in"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Generate a match for packets that will be encrypted
|
|
#
|
|
match_ipsec_out() # $1 = zone, $2 = host
|
|
{
|
|
if is_ipsec_host $1 $2 ; then
|
|
eval local options=\"\$${1}_ipsec_options \$${1}_ipsec_out_options\"
|
|
echo "-m policy --pol ipsec --dir out $options"
|
|
elif [ -n "$POLICY_MATCH" ]; then
|
|
echo "-m policy --pol none --dir out"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Jacket for ip_range() that takes care of iprange match
|
|
#
|
|
|
|
firewall_ip_range() # $1 = IP address or range
|
|
{
|
|
[ -n "$IPRANGE_MATCH" ] && echo $1 || ip_range $1
|
|
}
|
|
|
|
#
|
|
#
|
|
# 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 interface address addresses
|
|
|
|
while read z hosts options; do
|
|
if [ "x$(expand $z)" = "x$1" ]; then
|
|
expandv hosts
|
|
interface=${hosts%%:*}
|
|
addresses=${hosts#*:}
|
|
for address in $(separate_list $addresses); do
|
|
echo $interface:$address
|
|
done
|
|
fi
|
|
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 if an interface has a given option
|
|
#
|
|
interface_has_option() # $1 = interface, #2 = option
|
|
{
|
|
local options
|
|
|
|
eval options=\$$(chain_base $1)_options
|
|
|
|
list_search $2 $options
|
|
}
|
|
|
|
#
|
|
# Determine the defined hosts in each zone and generate report
|
|
#
|
|
determine_hosts() {
|
|
for zone in $ZONES; do
|
|
hosts=$(find_hosts $zone)
|
|
hosts=$(echo $hosts) # Remove extra trash
|
|
|
|
eval interfaces=\$${zone}_interfaces
|
|
|
|
for interface in $interfaces; do
|
|
if interface_has_option $interface detectnets; then
|
|
networks=$(get_routed_networks $interface)
|
|
else
|
|
networks=0.0.0.0/0
|
|
fi
|
|
|
|
for network in $networks; do
|
|
if [ -z "$hosts" ]; then
|
|
hosts=$interface:$network
|
|
else
|
|
hosts="$hosts $interface:$network"
|
|
fi
|
|
|
|
if interface_has_option $interface routeback; then
|
|
eval ${zone}_routeback=\"$interface:$network \$${zone}_routeback\"
|
|
fi
|
|
done
|
|
done
|
|
|
|
interfaces=
|
|
|
|
for host in $hosts; do
|
|
interface=${host%:*}
|
|
if list_search $interface $interfaces; then
|
|
list_search $interface:0.0.0.0/0 $hosts && \
|
|
startup_error "Invalid zone definition for zone $zone"
|
|
list_search $interface:0/0 $hosts && \
|
|
startup_error "Invalid zone definition for zone $zone"
|
|
eval ${zone}_is_complex=Yes
|
|
else
|
|
if [ -z "$interfaces" ]; then
|
|
interfaces=$interface
|
|
else
|
|
interfaces="$interfaces $interface"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
eval ${zone}_interfaces="\$interfaces"
|
|
eval ${zone}_hosts="\$hosts"
|
|
|
|
if [ -n "$hosts" ]; then
|
|
display_list "$zone 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
|
|
}
|
|
#
|
|
# Ensure that the passed zone is defined in the zones file.
|
|
#
|
|
validate_zone1() # $1 = zone
|
|
{
|
|
list_search $1 $ZONES
|
|
}
|
|
|
|
#
|
|
# Validate the zone names and options in the interfaces file
|
|
#
|
|
validate_interfaces_file() {
|
|
local wildcard
|
|
local found_obsolete_option=
|
|
local z interface networks options r iface option
|
|
|
|
while read z interface networks options; do
|
|
expandv z interface networks options
|
|
r="$z $interface $networks $options"
|
|
|
|
[ "x$z" = "x-" ] && z=
|
|
|
|
if [ -n "$z" ]; then
|
|
validate_zone $z || startup_error "Invalid zone ($z) in record \"$r\""
|
|
fi
|
|
|
|
list_search $interface $ALL_INTERFACES && \
|
|
startup_error "Duplicate Interface $interface"
|
|
|
|
wildcard=
|
|
|
|
case $interface in
|
|
*:*|+)
|
|
startup_error "Invalid Interface Name: $interface"
|
|
;;
|
|
*+)
|
|
wildcard=Yes
|
|
;;
|
|
esac
|
|
|
|
ALL_INTERFACES="$ALL_INTERFACES $interface"
|
|
options=$(separate_list $options)
|
|
iface=$(chain_base $interface)
|
|
|
|
eval ${iface}_broadcast="$networks"
|
|
eval ${iface}_zone="$z"
|
|
eval ${iface}_options=\"$options\"
|
|
|
|
for option in $options; do
|
|
case $option in
|
|
-)
|
|
;;
|
|
dhcp|norfc1918|tcpflags|newnotsyn|arp_filter|routefilter|logmartians|sourceroute|blacklist|proxyarp|maclist|nosmurfs|upnp|-)
|
|
;;
|
|
arp_ignore=*)
|
|
eval ${iface}_arp_ignore=${option#*=}
|
|
;;
|
|
arp_ignore)
|
|
eval ${iface}_arp_ignore=1
|
|
;;
|
|
detectnets)
|
|
[ -n "$wildcard" ] && \
|
|
startup_error "The \"detectnets\" option may not be used with a wild-card interface"
|
|
;;
|
|
routeback)
|
|
[ -n "$z" ] || startup_error "The routeback option may not be specified on a multi-zone interface"
|
|
;;
|
|
*)
|
|
error_message "WARNING: Invalid option ($option) in record \"$r\""
|
|
;;
|
|
esac
|
|
done
|
|
done < $TMP_DIR/interfaces
|
|
|
|
[ -z "$ALL_INTERFACES" ] && startup_error "No Interfaces Defined"
|
|
}
|
|
|
|
#
|
|
# Check that a mark value or mask is less that 256
|
|
#
|
|
verify_mark() # $1 = value to test
|
|
{
|
|
verify_mark1()
|
|
{
|
|
[ $1 -lt 256 ]
|
|
}
|
|
|
|
verify_mark2()
|
|
{
|
|
verify_mark1 $1 2> /dev/null
|
|
}
|
|
|
|
verify_mark2 $1 || fatal_error "Invalid Mark or Mask value: $1"
|
|
}
|
|
|
|
#
|
|
# Process the providers file
|
|
#
|
|
setup_providers()
|
|
{
|
|
local table number mark duplicate interface gateway options provider address copy route loose addresses rulenum pref
|
|
|
|
copy_table() {
|
|
run_ip route show table $duplicate | while read net route; do
|
|
case $net in
|
|
default|nexthop)
|
|
;;
|
|
*)
|
|
ensure_and_save_command ip route add table $number $net $route
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
copy_and_edit_table() {
|
|
run_ip route show table $duplicate | while read net route; do
|
|
case $net in
|
|
default|nexthop)
|
|
;;
|
|
*)
|
|
if list_search $(find_device $route) $copy; then
|
|
ensure_and_save_command ip route add table $number $net $route
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
add_a_provider() {
|
|
local t n iface option
|
|
|
|
for t in $PROVIDERS; do
|
|
if [ "$t" = "$table" ]; then
|
|
fatal_error "Duplicate Provider: $table, provider: \"$provider\""
|
|
fi
|
|
|
|
eval n=\$${t}_number
|
|
|
|
if [ $n -eq $number ]; then
|
|
fatal_error "Duplicate Provider number: $number, provider: \"$provider\""
|
|
fi
|
|
done
|
|
|
|
eval ${table}_number=$number
|
|
|
|
run_and_save_command qt ip route flush table $number
|
|
|
|
if [ "x${duplicate:=-}" != x- ]; then
|
|
if [ "x${copy:=-}" != "x-" ]; then
|
|
copy="$interface $(separate_list $copy)"
|
|
copy_and_edit_table
|
|
else
|
|
copy_table
|
|
fi
|
|
fi
|
|
|
|
if [ "x$gateway" = xdetect ] ; then
|
|
#
|
|
# First assume that this is some sort of point-to-point interface
|
|
#
|
|
gateway=$( find_peer $(ip addr ls $interface ) )
|
|
#
|
|
# Maybe there's a default route through this gateway already
|
|
#
|
|
[ -n "$gateway" ] || gateway=$(find_gateway $(ip route ls dev $interface))
|
|
#
|
|
# Last hope -- is there a load-balancing route through the interface?
|
|
#
|
|
[ -n "$gateway" ] || gateway=$(find_nexthop $interface)
|
|
#
|
|
# Be sure we found one
|
|
#
|
|
[ -n "$gateway" ] || fatal_error "Unable to detect the gateway through interface $interface"
|
|
fi
|
|
|
|
ensure_and_save_command ip route replace $gateway dev $interface table $number
|
|
ensure_and_save_command ip route add default via $gateway dev $interface table $number
|
|
|
|
verify_mark $mark
|
|
|
|
eval ${table}_mark=$mark
|
|
|
|
run_and_save_command qt ip rule del fwmark $mark
|
|
|
|
ensure_and_save_command ip rule add fwmark $mark pref $((10000 + $mark)) table $number
|
|
|
|
loose=
|
|
|
|
for option in $(separate_list $options); do
|
|
case $option in
|
|
-)
|
|
;;
|
|
track)
|
|
list_search $interface $ROUTEMARK_INTERFACES && \
|
|
fatal_error "Interface $interface is tracked through an earlier provider"
|
|
iface=$(chain_base $interface)
|
|
eval ${iface}_routemark=$mark
|
|
ROUTEMARK_INTERFACES="$ROUTEMARK_INTERFACES $interface"
|
|
;;
|
|
balance=*)
|
|
DEFAULT_ROUTE="$DEFAULT_ROUTE nexthop via $gateway dev $interface weight ${option#*=}"
|
|
;;
|
|
balance)
|
|
DEFAULT_ROUTE="$DEFAULT_ROUTE nexthop via $gateway dev $interface weight 1"
|
|
;;
|
|
loose)
|
|
loose=Yes
|
|
;;
|
|
*)
|
|
error_message " WARNING: Invalid option ($option) ignored in provider \"$provider\""
|
|
;;
|
|
esac
|
|
done
|
|
|
|
rulenum=0
|
|
|
|
find_interface_addresses $interface | while read address; do
|
|
run_and_save_command qt ip rule del from $address
|
|
if [ -z "$loose" ]; then
|
|
pref=$((20000 + $rulenum * 1000 + $mark ))
|
|
rulenum=$(($rulenum + 1))
|
|
ensure_and_save_command ip rule add from $address pref $pref table $number
|
|
fi
|
|
done
|
|
}
|
|
|
|
strip_file providers $1
|
|
|
|
if [ -s $TMP_DIR/providers ]; then
|
|
echo "Processing $1..."
|
|
|
|
save_progress_message "Restoring Providers..."
|
|
|
|
while read table number mark duplicate interface gateway options copy; do
|
|
expandv table number mark duplicate interface gateway options copy
|
|
provider="$table $number $mark $duplicate $interface $gateway $options $copy"
|
|
add_a_provider
|
|
PROVIDERS="$PROVIDERS $table"
|
|
progress_message " Provider $provider Added"
|
|
done < $TMP_DIR/providers
|
|
|
|
if [ -n "$PROVIDERS" ]; then
|
|
if [ -n "$DEFAULT_ROUTE" ]; then
|
|
ensure_and_save_command ip route replace default scope global $DEFAULT_ROUTE
|
|
progress_message " Default route $DEFAULT_ROUTE Added."
|
|
fi
|
|
|
|
cat > /etc/iproute2/rt_tables <<EOF
|
|
#
|
|
# reserved values
|
|
#
|
|
255 local
|
|
254 main
|
|
253 default
|
|
0 unspec
|
|
#
|
|
# local
|
|
#
|
|
EOF
|
|
|
|
for table in $PROVIDERS; do
|
|
eval number=\$${table}_number
|
|
/bin/echo -e "$number\t$table" >> /etc/iproute2/rt_tables
|
|
done
|
|
|
|
save_command "cat > /etc/iproute2/rt_tables << __EOF__"
|
|
cat /etc/iproute2/rt_tables >> $RESTOREBASE
|
|
save_command __EOF__
|
|
|
|
fi
|
|
|
|
ensure_and_save_command ip route flush cache
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Validate the zone names and options in the hosts file
|
|
#
|
|
validate_hosts_file() {
|
|
local z hosts options r interface host option port ports
|
|
|
|
check_bridge_port()
|
|
{
|
|
list_search $1 $ports || ports="$ports $1"
|
|
list_search ${interface}:${1} $zports || zports="$zports ${interface}:${1}"
|
|
list_search $1 $all_ports || all_ports="$all_ports $1"
|
|
}
|
|
|
|
while read z hosts options; do
|
|
expandv z hosts options
|
|
r="$z $hosts $options"
|
|
validate_zone1 $z || startup_error "Invalid zone ($z) in record \"$r\""
|
|
|
|
case $hosts in
|
|
*:*)
|
|
|
|
interface=${hosts%%:*}
|
|
iface=$(chain_base $interface)
|
|
|
|
list_search $interface $ALL_INTERFACES || \
|
|
startup_error "Unknown interface ($interface) in record \"$r\""
|
|
|
|
hosts=${hosts#*:}
|
|
;;
|
|
*)
|
|
fatal_error "Invalid HOST(S) column contents: $hosts"
|
|
;;
|
|
esac
|
|
|
|
eval ports=\$${iface}_ports
|
|
eval zports=\$${z}_ports
|
|
|
|
for host in $(separate_list $hosts); do
|
|
if [ -n "$BRIDGING" ]; then
|
|
case $host in
|
|
*:*)
|
|
known_interface ${host%:*} && \
|
|
startup_error "Bridged interfaces may not be defined in /etc/shorewall/interfaces: $host"
|
|
check_bridge_port ${host%%:*}
|
|
;;
|
|
*.*.*.*)
|
|
;;
|
|
+*)
|
|
eval ${z}_is_complex=Yes
|
|
;;
|
|
*)
|
|
known_interface $host && \
|
|
startup_error "Bridged interfaces may not be defined in /etc/shorewall/interfaces: $host"
|
|
check_bridge_port $host
|
|
;;
|
|
esac
|
|
else
|
|
case $host in
|
|
+*)
|
|
eval ${z}_is_complex=Yes
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
for option in $(separate_list $options) ; do
|
|
case $option in
|
|
maclist|norfc1918|blacklist|tcpflags|nosmurfs|newnotsyn|-)
|
|
;;
|
|
ipsec)
|
|
[ -n "$POLICY_MATCH" ] || \
|
|
startup_error "Your kernel and/or iptables does not support policy match: ipsec"
|
|
eval ${z}_ipsec_hosts=\"\$${z}_ipsec_hosts $interface:$host\"
|
|
eval ${z}_is_complex=Yes
|
|
;;
|
|
routeback)
|
|
[ -z "$ports" ] && \
|
|
eval ${z}_routeback=\"$interface:$host \$${z}_routeback\"
|
|
;;
|
|
*)
|
|
error_message "WARNING: Invalid option ($option) in record \"$r\""
|
|
;;
|
|
esac
|
|
done
|
|
done
|
|
|
|
if [ -n "$ports" ]; then
|
|
eval ${iface}_ports=\"$ports\"
|
|
eval ${z}_ports=\"$zports\"
|
|
fi
|
|
|
|
done < $TMP_DIR/hosts
|
|
|
|
[ -n "$all_ports" ] && echo " Bridge ports are: $all_ports"
|
|
}
|
|
|
|
#
|
|
# 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 the policy file
|
|
#
|
|
validate_policy()
|
|
{
|
|
local clientwild
|
|
local serverwild
|
|
local zone
|
|
local zone1
|
|
local pc
|
|
local chain
|
|
local policy
|
|
local loglevel
|
|
local synparams
|
|
|
|
print_policy() # $1 = source zone, $2 = destination zone
|
|
{
|
|
[ $COMMAND != check ] || \
|
|
[ $1 = $2 ] || \
|
|
[ $1 = all ] || \
|
|
[ $2 = all ] || \
|
|
progress_message " Policy for $1 to $2 is $policy using chain $chain"
|
|
}
|
|
|
|
all_policy_chains=
|
|
|
|
strip_file policy
|
|
|
|
while read client server policy loglevel synparams; do
|
|
expandv client server policy loglevel synparams
|
|
|
|
clientwild=
|
|
serverwild=
|
|
|
|
case "$client" in
|
|
all|ALL)
|
|
clientwild=Yes
|
|
;;
|
|
*)
|
|
if ! validate_zone $client; then
|
|
startup_error "Undefined zone $client"
|
|
fi
|
|
esac
|
|
|
|
case "$server" in
|
|
all|ALL)
|
|
serverwild=Yes
|
|
;;
|
|
*)
|
|
if ! validate_zone $server; then
|
|
startup_error "Undefined zone $server"
|
|
fi
|
|
esac
|
|
|
|
case $policy in
|
|
*:*)
|
|
epolicy=${policy#*:}
|
|
policy=${policy%:*}
|
|
|
|
case $epolicy in
|
|
ACCEPT|QUEUE)
|
|
;;
|
|
*)
|
|
startup_error " $client $server $policy $loglevel $synparams: Invalid ESTABLISHED/RELATED policy: $epolicy"
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
epolicy=ACCEPT
|
|
;;
|
|
esac
|
|
|
|
case $policy in
|
|
ACCEPT|REJECT|DROP|CONTINUE|QUEUE)
|
|
;;
|
|
NONE)
|
|
[ "$client" = "$FW" -o "$server" = "$FW" ] && \
|
|
startup_error " $client $server $policy $loglevel $synparams: NONE policy not allowed to/from the $FW zone"
|
|
|
|
[ -n "$clientwild" -o -n "$serverwild" ] && \
|
|
startup_error " $client $server $policy $loglevel $synparams: NONE policy not allowed with \"all\""
|
|
;;
|
|
*)
|
|
startup_error "Invalid policy $policy"
|
|
;;
|
|
esac
|
|
|
|
chain=${client}2${server}
|
|
|
|
if is_policy_chain $chain ; then
|
|
startup_error "Duplicate policy $policy"
|
|
fi
|
|
|
|
[ "x$loglevel" = "x-" ] && loglevel=
|
|
[ "x$synparms" = "x-" ] && synparms=
|
|
[ "x$epolicy" = "x-" ] && epolicy=
|
|
|
|
[ $policy = NONE ] || all_policy_chains="$all_policy_chains $chain"
|
|
|
|
eval ${chain}_is_policy=Yes
|
|
eval ${chain}_policy=$policy
|
|
eval ${chain}_loglevel=$loglevel
|
|
eval ${chain}_synparams=$synparams
|
|
eval ${chain}_epolicy=$epolicy
|
|
|
|
if [ -n "${clientwild}" ]; then
|
|
if [ -n "${serverwild}" ]; then
|
|
for zone in $ZONES $FW all; do
|
|
for zone1 in $ZONES $FW all; do
|
|
eval pc=\$${zone}2${zone1}_policychain
|
|
|
|
if [ -z "$pc" ]; then
|
|
eval ${zone}2${zone1}_policychain=$chain
|
|
eval ${zone}2${zone1}_policy=$policy
|
|
print_policy $zone $zone1
|
|
fi
|
|
done
|
|
done
|
|
else
|
|
for zone in $ZONES $FW all; do
|
|
eval pc=\$${zone}2${server}_policychain
|
|
|
|
if [ -z "$pc" ]; then
|
|
eval ${zone}2${server}_policychain=$chain
|
|
eval ${zone}2${server}_policy=$policy
|
|
print_policy $zone $server
|
|
fi
|
|
done
|
|
fi
|
|
elif [ -n "$serverwild" ]; then
|
|
for zone in $ZONES $FW all; do
|
|
eval pc=\$${client}2${zone}_policychain
|
|
|
|
if [ -z "$pc" ]; then
|
|
eval ${client}2${zone}_policychain=$chain
|
|
eval ${client}2${zone}_policy=$policy
|
|
print_policy $client $zone
|
|
fi
|
|
done
|
|
else
|
|
eval ${chain}_policychain=${chain}
|
|
print_policy $client $server
|
|
fi
|
|
|
|
done < $TMP_DIR/policy
|
|
}
|
|
|
|
#
|
|
# Find broadcast addresses
|
|
#
|
|
find_broadcasts() {
|
|
for interface in $ALL_INTERFACES; do
|
|
eval bcast=\$$(chain_base $interface)_broadcast
|
|
if [ "x$bcast" = "xdetect" ]; then
|
|
ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u
|
|
elif [ "x${bcast}" != "x-" ]; then
|
|
echo $(separate_list $bcast)
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# Find interface address--returns the first IP address assigned to the passed
|
|
# device
|
|
#
|
|
find_first_interface_address() # $1 = interface
|
|
{
|
|
#
|
|
# get the line of output containing the first IP address
|
|
#
|
|
addr=$(ip -f inet 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
|
|
{
|
|
for interface in $ALL_INTERFACES; do
|
|
eval options=\$$(chain_base $interface)_options
|
|
list_search $1 $options && echo $interface
|
|
done
|
|
}
|
|
|
|
#
|
|
# This slightly slower version is used to find both the option and option followed
|
|
# by equal sign ("=") and a value
|
|
#
|
|
find_interfaces_by_option1() # $1 = option
|
|
{
|
|
local options option
|
|
|
|
for interface in $ALL_INTERFACES; do
|
|
eval options=\$$(chain_base $interface)_options
|
|
for option in $options; do
|
|
if [ "${option%=*}" = "$1" ]; then
|
|
echo $interface
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
|
|
#
|
|
# Find hosts with the passed option
|
|
#
|
|
find_hosts_by_option() # $1 = option
|
|
{
|
|
local ignore hosts interface address addresses options ipsec= list
|
|
|
|
while read ignore hosts options; do
|
|
expandv options
|
|
list=$(separate_list $options)
|
|
if list_search $1 $list; then
|
|
list_search ipsec $list && ipsec=ipsec || ipsec=none
|
|
expandv hosts
|
|
interface=${hosts%%:*}
|
|
addresses=${hosts#*:}
|
|
for address in $(separate_list $addresses); do
|
|
echo ${ipsec}^$interface:$address
|
|
done
|
|
fi
|
|
done < $TMP_DIR/hosts
|
|
|
|
for interface in $ALL_INTERFACES; do
|
|
interface_has_option $interface $1 && \
|
|
echo none^${interface}:0.0.0.0/0
|
|
done
|
|
}
|
|
|
|
#
|
|
# 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
|
|
progress_message "Processing $user_exit ..."
|
|
. $user_exit
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Add a logging rule.
|
|
#
|
|
log_rule_limit() # $1 = log level, $2 = chain, $3 = display Chain $4 = disposition , $5 = rate limit $6=log tag $7=command $... = predicates for the rule
|
|
{
|
|
local level=$1
|
|
local chain=$2
|
|
local displayChain=$3
|
|
local disposition=$4
|
|
local rulenum=
|
|
local limit="${5:-$LOGLIMIT}"
|
|
local tag=${6:+$6 }
|
|
local command=${7:--A}
|
|
local prefix
|
|
local base=$(chain_base $displayChain)
|
|
|
|
shift;shift;shift;shift;shift;shift;shift
|
|
|
|
if [ -n "$tag" -a -n "$LOGTAGONLY" ]; then
|
|
displayChain=$tag
|
|
tag=
|
|
fi
|
|
|
|
if [ -n "$LOGRULENUMBERS" ]; then
|
|
eval rulenum=\$${base}_logrules
|
|
|
|
rulenum=${rulenum:-1}
|
|
|
|
prefix="$(printf "$LOGFORMAT" $displayChain $rulenum $disposition)${tag}"
|
|
|
|
rulenum=$(($rulenum + 1))
|
|
eval ${base}_logrules=$rulenum
|
|
else
|
|
prefix="$(printf "$LOGFORMAT" $displayChain $disposition)${tag}"
|
|
fi
|
|
|
|
if [ ${#prefix} -gt 29 ]; then
|
|
prefix="$(echo $prefix | truncate 29)"
|
|
error_message "WARNING: Log Prefix shortened to \"$prefix\""
|
|
fi
|
|
|
|
case $level in
|
|
ULOG)
|
|
if ! $IPTABLES $command $chain $@ $limit -j ULOG $LOGPARMS --ulog-prefix "$prefix" ; then
|
|
if [ -z "$stopping" ]; then
|
|
error_message "ERROR: Command \"$IPTABLES $command $chain $@ $limit -j ULOG $LOGPARMS --ulog-prefix \"$prefix\"\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
fi
|
|
;;
|
|
*)
|
|
if ! $IPTABLES $command $chain $@ $limit -j LOG $LOGPARMS --log-level $level --log-prefix "$prefix"; then
|
|
if [ -z "$stopping" ]; then
|
|
error_message "ERROR: Command \"$IPTABLES $command $chain $@ $limit -j LOG $LOGPARMS --log-level $level --log-prefix \"$prefix\"\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [ $? -ne 0 ] ; then
|
|
[ -z "$stopping" ] && { stop_firewall; exit 2; }
|
|
fi
|
|
}
|
|
|
|
log_rule() # $1 = log level, $2 = chain, $3 = disposition , $... = predicates for the rule
|
|
{
|
|
local level=$1
|
|
local chain=$2
|
|
local disposition=$3
|
|
|
|
shift;shift;shift
|
|
|
|
log_rule_limit $level $chain $chain $disposition "$LOGLIMIT" "" -A $@
|
|
}
|
|
|
|
#
|
|
# Set /proc/sys/net/ipv4/ip_forward based on $IP_FORWARDING
|
|
#
|
|
setup_forwarding() {
|
|
|
|
save_progress_message "Restoring IP Forwarding..."
|
|
|
|
case "$IP_FORWARDING" in
|
|
[Oo][Nn])
|
|
run_and_save_command "echo 1 > /proc/sys/net/ipv4/ip_forward"
|
|
echo "IP Forwarding Enabled"
|
|
;;
|
|
[Oo][Ff][Ff])
|
|
run_and_save_command "echo 0 > /proc/sys/net/ipv4/ip_forward"
|
|
echo "IP Forwarding Disabled!"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
#
|
|
# Disable IPV6
|
|
#
|
|
disable_ipv6() {
|
|
local foo="$(ip -f inet6 addr ls 2> /dev/null)"
|
|
|
|
if [ -n "$foo" ]; then
|
|
if qt which ip6tables; then
|
|
save_progress_message "Disabling IPV6..."
|
|
ip6tables -P FORWARD DROP && save_command ip6tables -P FORWARD DROP
|
|
ip6tables -P INPUT DROP && save_command ip6tables -P INPUT DROP
|
|
ip6tables -P OUTPUT DROP && save_command ip6tables -P OUTPUT DROP
|
|
else
|
|
error_message "WARNING: DISABLE_IPV6=Yes in shorewall.conf but this system does not appear to have ip6tables"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
disable_ipv6_1() {
|
|
local foo="$(ip -f inet6 addr ls 2> /dev/null)"
|
|
|
|
if [ -n "$foo" ]; then
|
|
if qt which ip6tables; then
|
|
progress_message "Disabling IPV6..."
|
|
ip6tables -P FORWARD DROP
|
|
ip6tables -P INPUT DROP
|
|
ip6tables -P OUTPUT DROP
|
|
else
|
|
error_message "WARNING: DISABLE_IPV6=Yes in shorewall.conf but this system does not appear to have ip6tables"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Process the routestopped file either adding or deleting rules
|
|
#
|
|
|
|
process_routestopped() # $1 = command
|
|
{
|
|
local hosts= interface host host1 options networks source= dest= matched
|
|
|
|
while read interface host options; do
|
|
expandv interface host options
|
|
[ "x$host" = "x-" -o -z "$host" ] && host=0.0.0.0/0
|
|
for h in $(separate_list $host); do
|
|
hosts="$hosts $interface:$h"
|
|
done
|
|
|
|
routeback=
|
|
|
|
if [ -n "$options" ]; then
|
|
for option in $(separate_list $options); do
|
|
case $option in
|
|
routeback)
|
|
if [ -n "$routeback" ]; then
|
|
error_message "WARNING: Duplicate routestopped option ignored: routeback"
|
|
else
|
|
routeback=Yes
|
|
for h in $(separate_list $host); do
|
|
run_iptables $1 FORWARD -i $interface -o $interface $(both_ip_ranges $h $h) -j ACCEPT
|
|
done
|
|
fi
|
|
;;
|
|
source)
|
|
for h in $(separate_list $host); do
|
|
source="$source $interface:$h"
|
|
done
|
|
;;
|
|
dest)
|
|
for h in $(separate_list $host); do
|
|
dest="$dest $interface:$h"
|
|
done
|
|
;;
|
|
critical)
|
|
;;
|
|
*)
|
|
error_message "WARNING: Unknown routestopped option ignored: $option"
|
|
;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
done < $TMP_DIR/routestopped
|
|
|
|
|
|
for host in $hosts; do
|
|
interface=${host%:*}
|
|
networks=${host#*:}
|
|
$IPTABLES $1 INPUT -i $interface $(source_ip_range $networks) -j ACCEPT
|
|
[ -z "$ADMINISABSENTMINDED" ] && \
|
|
run_iptables $1 OUTPUT -o $interface $(dest_ip_range $networks) -j ACCEPT
|
|
|
|
matched=
|
|
|
|
if list_search $host $source ; then
|
|
run_iptables $1 FORWARD -i $interface $(source_ip_range $networks) -j ACCEPT
|
|
matched=Yes
|
|
fi
|
|
|
|
if list_search $host $dest ; then
|
|
run_iptables $1 FORWARD -o $interface $(dest_ip_range $networks) -j ACCEPT
|
|
matched=Yes
|
|
fi
|
|
|
|
if [ -z "$matched" ]; then
|
|
for host1 in $hosts; do
|
|
[ "$host" != "$host1" ] && run_iptables $1 FORWARD -i $interface -o ${host1%:*} $(both_ip_ranges $networks ${host1#*:}) -j ACCEPT
|
|
done
|
|
fi
|
|
done
|
|
}
|
|
|
|
process_criticalhosts()
|
|
{
|
|
local hosts= interface host h options networks criticalhosts=
|
|
|
|
[ -f $TMP_DIR/routestopped ] || strip_file routestopped
|
|
|
|
while read interface host options; do
|
|
expandv interface host options
|
|
|
|
[ "x$host" = "x-" -o -z "$host" ] && host=0.0.0.0/0 || host=$(separate_list $host)
|
|
|
|
if [ -n "$options" ]; then
|
|
for option in $(separate_list $options); do
|
|
case $option in
|
|
routeback|source|dest)
|
|
;;
|
|
critical)
|
|
for h in $host; do
|
|
criticalhosts="$criticalhosts $interface:$h"
|
|
done
|
|
;;
|
|
*)
|
|
error_message "WARNING: Unknown routestopped option ignored: $option"
|
|
;;
|
|
esac
|
|
done
|
|
fi
|
|
done < $TMP_DIR/routestopped
|
|
|
|
if [ -n "$criticalhosts" ]; then
|
|
CRITICALHOSTS=$criticalhosts
|
|
progress_message "Critical Hosts are:$CRITICALHOSTS"
|
|
fi
|
|
|
|
}
|
|
|
|
#
|
|
# For each entry in the CRITICALHOSTS global list, add INPUT and OUTPUT rules to
|
|
# enable traffic to/from those hosts.
|
|
#
|
|
enable_critical_hosts()
|
|
{
|
|
for host in $CRITICALHOSTS; do
|
|
interface=${host%:*}
|
|
networks=${host#*:}
|
|
$IPTABLES -A INPUT -i $interface $(source_ip_range $networks) -j ACCEPT
|
|
$IPTABLES -A OUTPUT -o $interface $(dest_ip_range $networks) -j ACCEPT
|
|
done
|
|
}
|
|
|
|
#
|
|
# For each entry in the CRITICALHOSTS global list, delete the INPUT and OUTPUT rules that
|
|
# enable traffic to/from those hosts.
|
|
#
|
|
disable_critical_hosts()
|
|
{
|
|
for host in $CRITICALHOSTS; do
|
|
interface=${host%:*}
|
|
networks=${host#*:}
|
|
$IPTABLES -D INPUT -i $interface $(source_ip_range $networks) -j ACCEPT
|
|
$IPTABLES -D OUTPUT -o $interface $(dest_ip_range $networks) -j ACCEPT
|
|
done
|
|
}
|
|
|
|
#
|
|
# Stop the Firewall
|
|
#
|
|
stop_firewall() {
|
|
#
|
|
# Turn off trace unless we were tracing "stop" or "clear"
|
|
#
|
|
|
|
[ -n "$RESTOREBASE" ] && rm -f $RESTOREBASE
|
|
|
|
case $COMMAND in
|
|
stop|clear)
|
|
;;
|
|
check)
|
|
kill $$
|
|
exit 2
|
|
;;
|
|
*)
|
|
set +x
|
|
|
|
[ -z "$RESTOREFILE" ] && RESTOREFILE=restore
|
|
|
|
RESTOREPATH=/var/lib/shorewall/$RESTOREFILE
|
|
|
|
if [ -x $RESTOREPATH ]; then
|
|
|
|
if [ -x ${RESTOREPATH}-ipsets ]; then
|
|
echo Restoring Ipsets...
|
|
#
|
|
# We must purge iptables to be sure that there are no
|
|
# references to ipsets
|
|
#
|
|
iptables -F
|
|
iptables -X
|
|
${RESTOREPATH}-ipsets
|
|
fi
|
|
|
|
echo Restoring Shorewall...
|
|
$RESTOREPATH
|
|
echo "Shorewall restored from $RESTOREPATH"
|
|
my_mutex_off
|
|
kill $$
|
|
exit 2
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
stopping="Yes"
|
|
|
|
terminator=
|
|
|
|
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 "$CLEAR_TC" ] && delete_tc1
|
|
|
|
[ -n "$DISABLE_IPV6" ] && disable_ipv6_1
|
|
|
|
process_criticalhosts
|
|
|
|
if [ -n "$CRITICALHOSTS" ]; then
|
|
if [ -z "$ADMINISABSENTMINDED" ]; then
|
|
for chain in INPUT OUTPUT; do
|
|
setpolicy $chain ACCEPT
|
|
done
|
|
|
|
setpolicy FORWARD DROP
|
|
|
|
deleteallchains
|
|
|
|
enable_critical_hosts
|
|
|
|
for chain in INPUT OUTPUT; do
|
|
setpolicy $chain DROP
|
|
done
|
|
else
|
|
for chain in INPUT OUTPUT; do
|
|
setpolicy $chain ACCEPT
|
|
done
|
|
|
|
setpolicy FORWARD DROP
|
|
|
|
deleteallchains
|
|
|
|
enable_critical_hosts
|
|
|
|
setpolicy INPUT DROP
|
|
|
|
for chain in INPUT FORWARD; do
|
|
setcontinue $chain
|
|
done
|
|
fi
|
|
elif [ -z "$ADMINISABSENTMINDED" ]; then
|
|
for chain in INPUT OUTPUT FORWARD; do
|
|
setpolicy $chain DROP
|
|
done
|
|
|
|
deleteallchains
|
|
else
|
|
for chain in INPUT FORWARD; do
|
|
setpolicy $chain DROP
|
|
done
|
|
|
|
setpolicy OUTPUT ACCEPT
|
|
|
|
deleteallchains
|
|
|
|
for chain in INPUT FORWARD; do
|
|
setcontinue $chain
|
|
done
|
|
fi
|
|
|
|
process_routestopped -A
|
|
|
|
$IPTABLES -A INPUT -i lo -j ACCEPT
|
|
[ -z "$ADMINISABSENTMINDED" ] && \
|
|
$IPTABLES -A OUTPUT -o lo -j ACCEPT
|
|
|
|
for interface in $(find_interfaces_by_option dhcp); do
|
|
$IPTABLES -A INPUT -p udp -i $interface --dport 67:68 -j ACCEPT
|
|
[ -z "$ADMINISABSENTMINDED" ] && \
|
|
$IPTABLES -A OUTPUT -p udp -o $interface --dport 67:68 -j ACCEPT
|
|
#
|
|
# This might be a bridge
|
|
#
|
|
$IPTABLES -A FORWARD -p udp -i $interface -o $interface --dport 67:68 -j ACCEPT
|
|
done
|
|
|
|
case "$IP_FORWARDING" in
|
|
[Oo][Nn])
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
echo "IP Forwarding Enabled"
|
|
;;
|
|
[Oo][Ff][Ff])
|
|
echo 0 > /proc/sys/net/ipv4/ip_forward
|
|
echo "IP Forwarding Disabled!"
|
|
;;
|
|
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
|
|
|
|
setpolicy INPUT ACCEPT
|
|
setpolicy FORWARD ACCEPT
|
|
setpolicy OUTPUT ACCEPT
|
|
|
|
run_iptables -F
|
|
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
|
|
if qt which ip6tables; then
|
|
ip6tables -P INPUT ACCEPT 2> /dev/null
|
|
ip6tables -P OUTPUT ACCEPT 2> /dev/null
|
|
ip6tables -P FORWARD ACCEPT 2> /dev/null
|
|
fi
|
|
|
|
run_user_exit clear
|
|
|
|
logger "Shorewall Cleared"
|
|
}
|
|
|
|
#
|
|
# Set up ipsec tunnels
|
|
#
|
|
setup_tunnels() # $1 = name of tunnels file
|
|
{
|
|
local inchain
|
|
local outchain
|
|
|
|
|
|
setup_one_ipsec() # $1 = gateway $2 = Tunnel Kind $3 = gateway zones
|
|
{
|
|
local kind=$2 noah=
|
|
|
|
case $kind in
|
|
*:*)
|
|
noah=${kind#*:}
|
|
[ $noah = noah -o $noah = NOAH ] || fatal_error "Invalid IPSEC modifier $noah in tunnel \"$tunnel\""
|
|
kind=${kind%:*}
|
|
;;
|
|
esac
|
|
|
|
[ $kind = IPSEC ] && kind=ipsec
|
|
|
|
options="-m state --state NEW -j ACCEPT"
|
|
addrule2 $inchain -p 50 $(source_ip_range $1) -j ACCEPT
|
|
addrule2 $outchain -p 50 $(dest_ip_range $1) -j ACCEPT
|
|
|
|
if [ -z "$noah" ]; then
|
|
run_iptables -A $inchain -p 51 $(source_ip_range $1) -j ACCEPT
|
|
run_iptables -A $outchain -p 51 $(dest_ip_range $1) -j ACCEPT
|
|
fi
|
|
|
|
run_iptables -A $outchain -p udp $(dest_ip_range $1) --dport 500 $options
|
|
|
|
if [ $kind = ipsec ]; then
|
|
run_iptables -A $inchain -p udp $(source_ip_range $1) --dport 500 $options
|
|
else
|
|
run_iptables -A $inchain -p udp $(source_ip_range $1) --dport 500 $options
|
|
run_iptables -A $inchain -p udp $(source_ip_range $1) --dport 4500 $options
|
|
fi
|
|
|
|
for z in $(separate_list $3); do
|
|
if validate_zone $z; then
|
|
addrule ${FW}2${z} -p udp --dport 500 $options
|
|
if [ $kind = ipsec ]; then
|
|
addrule ${z}2${FW} -p udp --dport 500 $options
|
|
else
|
|
addrule ${z}2${FW} -p udp --dport 500 $options
|
|
addrule ${z}2${FW} -p udp --dport 4500 $options
|
|
fi
|
|
else
|
|
fatal_error "Invalid gateway zone ($z) -- Tunnel \"$tunnel\""
|
|
fi
|
|
done
|
|
|
|
progress_message " IPSEC tunnel to $gateway defined."
|
|
}
|
|
|
|
setup_one_other() # $1 = TYPE, $2 = gateway, $3 = protocol
|
|
{
|
|
addrule2 $inchain -p $3 $(source_ip_range $2) -j ACCEPT
|
|
addrule2 $outchain -p $3 $(dest_ip_range $2) -j ACCEPT
|
|
|
|
progress_message " $1 tunnel to $2 defined."
|
|
}
|
|
|
|
setup_pptp_client() # $1 = gateway
|
|
{
|
|
addrule2 $outchain -p 47 $(dest_ip_range $1) -j ACCEPT
|
|
addrule2 $inchain -p 47 $(source_ip_range $1) -j ACCEPT
|
|
addrule2 $outchain -p tcp --dport 1723 $(dest_ip_range $1) -j ACCEPT
|
|
|
|
progress_message " PPTP tunnel to $1 defined."
|
|
}
|
|
|
|
setup_pptp_server() # $1 = gateway
|
|
{
|
|
addrule2 $inchain -p 47 $(source_ip_range $1) -j ACCEPT
|
|
addrule2 $outchain -p 47 $(dest_ip_range $1) -j ACCEPT
|
|
addrule2 $inchain -p tcp --dport 1723 $(source_ip_range $1) -j ACCEPT
|
|
|
|
progress_message " PPTP server defined."
|
|
}
|
|
|
|
setup_one_openvpn() # $1 = gateway, $2 = kind[:port]
|
|
{
|
|
local protocol=udp
|
|
local p=1194
|
|
|
|
case $2 in
|
|
*:*:*)
|
|
protocol=${2%:*}
|
|
protocol=${protocol#*:}
|
|
p=${2##*:}
|
|
;;
|
|
*:*)
|
|
p=${2#*:}
|
|
;;
|
|
esac
|
|
|
|
addrule2 $inchain -p $protocol $(source_ip_range $1) --dport $p -j ACCEPT
|
|
addrule2 $outchain -p $protocol $(dest_ip_range $1) --dport $p -j ACCEPT
|
|
|
|
progress_message " OPENVPN tunnel to $1:$protocol:$p defined."
|
|
}
|
|
|
|
setup_one_generic() # $1 = gateway, $2 = kind:protocol[:port], $3 = Gateway Zone
|
|
{
|
|
local protocol
|
|
local p=
|
|
|
|
case $2 in
|
|
*:*:*)
|
|
p=${2##*:}
|
|
protocol=${2%:*}
|
|
protocol=${protocol#*:}
|
|
;;
|
|
*:*)
|
|
protocol=${2#*:}
|
|
;;
|
|
*)
|
|
protocol=udp
|
|
p=5000
|
|
;;
|
|
esac
|
|
|
|
p=${p:+--dport $p}
|
|
|
|
addrule2 $inchain -p $protocol $(source_ip_range $1) $p -j ACCEPT
|
|
addrule2 $outchain -p $protocol $(dest_ip_range $1) $p -j ACCEPT
|
|
|
|
for z in $(separate_list $3); do
|
|
if validate_zone $z; then
|
|
addrule ${FW}2${z} -p $protocol $p -j ACCEPT
|
|
addrule ${z}2${FW} -p $protocol $p -j ACCEPT
|
|
else
|
|
error_message "WARNING: Invalid gateway zone ($z)" \
|
|
" -- Tunnel \"$tunnel\" may encounter problems"
|
|
fi
|
|
done
|
|
|
|
progress_message " GENERIC tunnel to $1:$p 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}
|
|
gateway=${gateway:-0.0.0.0/0}
|
|
case $kind in
|
|
ipsec|IPSEC|ipsec:*|IPSEC:*)
|
|
setup_one_ipsec $gateway $kind $z1
|
|
;;
|
|
ipsecnat|IPSECNAT|ipsecnat:*|IPSECNAT:*)
|
|
setup_one_ipsec $gateway $kind $z1
|
|
;;
|
|
ipip|IPIP)
|
|
setup_one_other IPIP $gateway 4
|
|
;;
|
|
gre|GRE)
|
|
setup_one_other GRE $gateway 47
|
|
;;
|
|
6to4|6TO4)
|
|
setup_one_other 6to4 $gateway 41
|
|
;;
|
|
pptpclient|PPTPCLIENT)
|
|
setup_pptp_client $gateway
|
|
;;
|
|
pptpserver|PPTPSERVER)
|
|
setup_pptp_server $gateway
|
|
;;
|
|
openvpn|OPENVPN|openvpn:*|OPENVPN:*)
|
|
setup_one_openvpn $gateway $kind
|
|
;;
|
|
generic:*|GENERIC:*)
|
|
setup_one_generic $gateway $kind $z1
|
|
;;
|
|
*)
|
|
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
|
|
}
|
|
|
|
#
|
|
# Process the ipsec information in the zones file
|
|
#
|
|
setup_ipsec() {
|
|
local zone using_ipsec=
|
|
#
|
|
# Add a --set-mss rule to the passed chain
|
|
#
|
|
set_mss1() # $1 = chain, $2 = MSS
|
|
{
|
|
eval local policy=\$${1}_policy
|
|
|
|
if [ "$policy" != NONE ]; then
|
|
case $COMMAND in
|
|
start|restart)
|
|
ensurechain $1
|
|
run_iptables -I $1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss $2
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
#
|
|
# Set up rules to set MSS to and/or from zone "$zone"
|
|
#
|
|
set_mss() # $1 = MSS value, $2 = _in, _out or ""
|
|
{
|
|
if [ $COMMAND != check ]; then
|
|
for z in $ZONES; do
|
|
case $2 in
|
|
_in)
|
|
set_mss1 ${zone}2${z} $1
|
|
;;
|
|
_out)
|
|
set_mss1 ${z}2${zone} $1
|
|
;;
|
|
*)
|
|
set_mss1 ${z}2${zone} $1
|
|
set_mss1 ${zone}2${z} $1
|
|
;;
|
|
esac
|
|
done
|
|
fi
|
|
}
|
|
|
|
do_options() # $1 = _in, _out or "" - $2 = option list
|
|
{
|
|
local option opts newoptions= val
|
|
|
|
[ x${2} = x- ] && return
|
|
|
|
opts=$(separate_list $2)
|
|
|
|
for option in $opts; do
|
|
val=${option#*=}
|
|
|
|
case $option in
|
|
mss=[0-9]*) set_mss $val $1 ;;
|
|
strict) newoptions="$newoptions --strict" ;;
|
|
next) newoptions="$newoptions --next" ;;
|
|
reqid=*) newoptions="$newoptions --reqid $val" ;;
|
|
spi=*) newoptions="$newoptions --spi $val" ;;
|
|
proto=*) newoptions="$newoptions --proto $val" ;;
|
|
mode=*) newoptions="$newoptions --mode $val" ;;
|
|
tunnel-src=*) newoptions="$newoptions --tunnel-src $val" ;;
|
|
tunnel-dst=*) newoptions="$newoptions --tunnel-dst $val" ;;
|
|
reqid!=*) newoptions="$newoptions ! --reqid $val" ;;
|
|
spi!=*) newoptions="$newoptions ! --spi $val" ;;
|
|
proto!=*) newoptions="$newoptions ! --proto $val" ;;
|
|
mode!=*) newoptions="$newoptions ! --mode $val" ;;
|
|
tunnel-src!=*) newoptions="$newoptions ! --tunnel-src $val" ;;
|
|
tunnel-dst!=*) newoptions="$newoptions ! --tunnel-dst $val" ;;
|
|
*) fatal_error "Invalid option \"$option\" for zone $zone" ;;
|
|
esac
|
|
done
|
|
|
|
if [ -n "$newoptions" ]; then
|
|
[ -n "$POLICY_MATCH" ] || fatal_error "Your kernel and/or iptables does not support policy match"
|
|
eval ${zone}_is_complex=Yes
|
|
eval ${zone}_ipsec${1}_options=\"${newoptions# }\"
|
|
fi
|
|
}
|
|
|
|
case $IPSECFILE in
|
|
zones)
|
|
f=zones
|
|
progress_message "Setting up IPSEC..."
|
|
;;
|
|
*)
|
|
f=$IPSECFILE
|
|
strip_file $f
|
|
progress_message "Processing $f..."
|
|
using_ipsec=Yes
|
|
;;
|
|
esac
|
|
|
|
while read zone ipsec options in_options out_options mss; do
|
|
expandv zone ipsec options in_options out_options mss
|
|
|
|
if [ -n "$using_ipsec" ]; then
|
|
validate_zone1 $zone || fatal_error "Unknown zone: $zone"
|
|
fi
|
|
|
|
if [ -n "$ipsec" ]; then
|
|
case $ipsec in
|
|
-|No|no)
|
|
;;
|
|
Yes|yes)
|
|
[ -n "$POLICY_MATCH" ] || fatal_error "Your kernel and/or iptables does not support policy match"
|
|
eval ${zone}_is_ipsec=Yes
|
|
eval ${zone}_is_complex=Yes
|
|
;;
|
|
*)
|
|
fatal_error "Invalid IPSEC column value: $ipsec"
|
|
;;
|
|
esac
|
|
|
|
do_options "" $options
|
|
do_options "_in" $in_options
|
|
do_options "_out" $out_options
|
|
fi
|
|
|
|
done < $TMP_DIR/$f
|
|
}
|
|
|
|
##
|
|
# Setup Proxy ARP
|
|
#
|
|
setup_proxy_arp() {
|
|
|
|
local setlist= resetlist=
|
|
|
|
print_error() {
|
|
error_message "Invalid value for HAVEROUTE - ($haveroute)"
|
|
error_message "Entry \"$address $interface $external $haveroute\" ignored"
|
|
}
|
|
|
|
print_error1() {
|
|
error_message "Invalid value for PERSISTENT - ($persistent)"
|
|
error_message "Entry \"$address $interface $external $haveroute $persistent\" ignored"
|
|
}
|
|
|
|
print_warning() {
|
|
error_message "PERSISTENT setting ignored - ($persistent)"
|
|
error_message "Entry \"$address $interface $external $haveroute $persistent\""
|
|
}
|
|
|
|
setup_one_proxy_arp() {
|
|
|
|
case $haveroute in
|
|
[Nn][Oo])
|
|
haveroute=
|
|
;;
|
|
[Yy][Ee][Ss])
|
|
;;
|
|
*)
|
|
if [ -n "$haveroute" ]; then
|
|
print_error
|
|
return
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
case $persistent in
|
|
[Nn][Oo])
|
|
persistent=
|
|
;;
|
|
[Yy][Ee][Ss])
|
|
[ -z "$haveroute" ] || print_warning
|
|
;;
|
|
*)
|
|
if [ -n "$persistent" ]; then
|
|
print_error1
|
|
return
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [ -z "$haveroute" ]; then
|
|
ensure_and_save_command ip route replace $address dev $interface
|
|
[ -n "$persistent" ] && haveroute=yes
|
|
fi
|
|
|
|
ensure_and_save_command arp -i $external -Ds $address $external pub
|
|
|
|
echo $address $interface $external $haveroute >> /var/lib/shorewall/proxyarp
|
|
|
|
progress_message " Host $address connected to $interface added to ARP on $external"
|
|
}
|
|
|
|
> /var/lib/shorewall/proxyarp
|
|
|
|
save_progress_message "Restoring Proxy ARP..."
|
|
|
|
while read address interface external haveroute persistent; do
|
|
expandv address interface external haveroute persistent
|
|
list_search $interface $setlist || setlist="$setlist $interface"
|
|
list_search $external $resetlist || list_search $external $setlist || resetlist="$resetlist $external"
|
|
setup_one_proxy_arp
|
|
done < $TMP_DIR/proxyarp
|
|
|
|
for interface in $resetlist; do
|
|
list_search $interface $setlist || \
|
|
run_and_save_command "echo 0 > /proc/sys/net/ipv4/conf/$interface/proxy_arp"
|
|
done
|
|
|
|
for interface in $setlist; do
|
|
run_and_save_command "echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp"
|
|
done
|
|
|
|
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
|
|
progress_message " Enabled proxy ARP on $interface"
|
|
save_command "echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp"
|
|
else
|
|
error_message "WARNING: Unable to enable proxy ARP on $interface"
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# Set up MAC Verification
|
|
#
|
|
setup_mac_lists() {
|
|
local interface
|
|
local mac
|
|
local addresses
|
|
local address
|
|
local chain
|
|
local chain1
|
|
local macpart
|
|
local blob
|
|
local hosts
|
|
local ipsec
|
|
local policy=
|
|
#
|
|
# Generate the list of interfaces having MAC verification
|
|
#
|
|
maclist_interfaces=
|
|
|
|
for hosts in $maclist_hosts; do
|
|
hosts=${hosts#*^}
|
|
interface=${hosts%%:*}
|
|
if ! list_search $interface $maclist_interfaces; then\
|
|
if [ -z "$maclist_interfaces" ]; then
|
|
maclist_interfaces=$interface
|
|
else
|
|
maclist_interfaces="$maclist_interfaces $interface"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
progress_message "Setting up MAC Verification on $maclist_interfaces..."
|
|
#
|
|
# Create chains.
|
|
#
|
|
for interface in $maclist_interfaces; do
|
|
chain=$(mac_chain $interface)
|
|
createchain $chain no
|
|
|
|
if [ -n "$MACLIST_TTL" ]; then
|
|
chain1=$(macrecent_target $interface)
|
|
createchain $chain1 no
|
|
run_iptables -A $chain -m recent --rcheck --seconds $MACLIST_TTL --name $chain -j RETURN
|
|
run_iptables -A $chain -j $chain1
|
|
run_iptables -A $chain -m recent --update --name $chain -j RETURN
|
|
run_iptables -A $chain -m recent --set --name $chain
|
|
fi
|
|
done
|
|
#
|
|
# Process the maclist file producing the verification rules
|
|
#
|
|
while read interface mac addresses; do
|
|
expandv interface mac addresses
|
|
|
|
physdev_part=
|
|
|
|
if [ -n "$BRIDGING" ]; then
|
|
case $interface in
|
|
*:*)
|
|
physdev_part="-m physdev --physdev-in ${interface#*:}"
|
|
interface=${interface%:*}
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
[ -n "$MACLIST_TTL" ] && chain=$(macrecent_target $interface) || chain=$(mac_chain $interface)
|
|
|
|
if ! havechain $chain ; then
|
|
fatal_error "No hosts on $interface have the maclist option specified"
|
|
fi
|
|
|
|
macpart=$(mac_match $mac)
|
|
|
|
if [ -z "$addresses" ]; then
|
|
run_iptables -A $chain $macpart $physdev_part -j RETURN
|
|
else
|
|
for address in $(separate_list $addresses) ; do
|
|
run_iptables2 -A $chain $macpart -s $address $physdev_part -j RETURN
|
|
done
|
|
fi
|
|
done < $TMP_DIR/maclist
|
|
#
|
|
# Must take care of our own broadcasts and multicasts then terminate the verification
|
|
# chains
|
|
#
|
|
for interface in $maclist_interfaces; do
|
|
[ -n "$MACLIST_TTL" ] && chain=$(macrecent_target $interface) || chain=$(mac_chain $interface)
|
|
|
|
blob=$(ip link show $interface 2> /dev/null)
|
|
|
|
[ -z "$blob" ] && \
|
|
fatal_error "Interface $interface must be up before Shorewall can start"
|
|
|
|
ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet //; s/brd //; s/scope.*//;' | while read address broadcast; do
|
|
address=${address%/*}
|
|
if [ -n "$broadcast" ]; then
|
|
run_iptables -A $chain -s $address -d $broadcast -j RETURN
|
|
fi
|
|
|
|
run_iptables -A $chain -s $address -d 255.255.255.255 -j RETURN
|
|
run_iptables -A $chain -s $address -d 224.0.0.0/4 -j RETURN
|
|
done
|
|
|
|
if [ -n "$MACLIST_LOG_LEVEL" ]; then
|
|
log_rule_limit $MACLIST_LOG_LEVEL $chain $(mac_chain $interface) $MACLIST_DISPOSITION "$LOGLIMIT" "" -A
|
|
fi
|
|
|
|
run_iptables -A $chain -j $maclist_target
|
|
done
|
|
#
|
|
# Generate jumps from the input and forward chains
|
|
#
|
|
for hosts in $maclist_hosts; do
|
|
ipsec=${hosts%^*}
|
|
hosts=${hosts#*^}
|
|
[ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
|
|
interface=${hosts%%:*}
|
|
hosts=${hosts#*:}
|
|
for chain in $(first_chains $interface) ; do
|
|
run_iptables -A $chain $(match_source_hosts $hosts) -m state --state NEW \
|
|
$policy -j $(mac_chain $interface)
|
|
done
|
|
done
|
|
}
|
|
|
|
#
|
|
# Set up SYN flood protection
|
|
#
|
|
setup_syn_flood_chain ()
|
|
# $1 = policy chain
|
|
# $2 = synparams
|
|
# $3 = loglevel
|
|
{
|
|
local chain=@$1
|
|
local limit=$2
|
|
local limit_burst=
|
|
|
|
case $limit in
|
|
*:*)
|
|
limit_burst="--limit-burst ${limit#*:}"
|
|
limit=${limit%:*}
|
|
;;
|
|
esac
|
|
|
|
run_iptables -N $chain
|
|
run_iptables -A $chain -m limit --limit $limit $limit_burst -j RETURN
|
|
[ -n "$3" ] && \
|
|
log_rule_limit $3 $chain $chain DROP "-m limit --limit 5/min --limit-burst 5" "" ""
|
|
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 -p tcp --syn -j @$2
|
|
progress_message " Enabled SYN flood protection"
|
|
}
|
|
|
|
#
|
|
# Delete existing Proxy ARP
|
|
#
|
|
delete_proxy_arp() {
|
|
if [ -f /var/lib/shorewall/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 < /var/lib/shorewall/proxyarp
|
|
|
|
rm -f /var/lib/shorewall/proxyarp
|
|
fi
|
|
|
|
[ -d /var/lib/shorewall ] && touch /var/lib/shorewall/proxyarp
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
[ -f $f/proxy_arp ] && echo 0 > $f/proxy_arp
|
|
done
|
|
}
|
|
|
|
#
|
|
# Setup Static Network Address Translation (NAT)
|
|
#
|
|
setup_nat() {
|
|
local external= interface= internal= allints= localnat= policyin= policyout=
|
|
|
|
validate_one() #1 = Variable Name, $2 = Column name, $3 = value
|
|
{
|
|
case $3 in
|
|
Yes|yes)
|
|
;;
|
|
No|no)
|
|
eval ${1}=
|
|
;;
|
|
*)
|
|
[ -n "$3" ] && \
|
|
fatal_error "Invalid value ($3) for $2 in entry \"$external $interface $internal $allints $localnat\""
|
|
;;
|
|
esac
|
|
}
|
|
|
|
do_one_nat() {
|
|
local add_ip_aliases=$ADD_IP_ALIASES iface=${interface%:*}
|
|
|
|
if [ -n "$add_ip_aliases" ]; then
|
|
case $interface in
|
|
*:)
|
|
interface=${interface%:}
|
|
add_ip_aliases=
|
|
;;
|
|
*)
|
|
[ -n "$RETAIN_ALIASES" ] || run_and_save_command qt ip addr del $external dev $iface
|
|
;;
|
|
esac
|
|
else
|
|
interface=${interface%:}
|
|
fi
|
|
|
|
validate_one allints "ALL INTERFACES" $allints
|
|
validate_one localnat "LOCAL" $localnat
|
|
|
|
if [ -n "$allints" ]; then
|
|
addnatrule nat_in -d $external $policyin -j DNAT --to-destination $internal
|
|
addnatrule nat_out -s $internal $policyout -j SNAT --to-source $external
|
|
else
|
|
addnatrule $(input_chain $iface) -d $external $policyin -j DNAT --to-destination $internal
|
|
addnatrule $(output_chain $iface) -s $internal $policyout -j SNAT --to-source $external
|
|
fi
|
|
|
|
[ -n "$localnat" ] && \
|
|
run_iptables2 -t nat -A OUTPUT -d $external $policyout -j DNAT --to-destination $internal
|
|
|
|
if [ -n "$add_ip_aliases" ]; then
|
|
list_search $external $aliases_to_add || \
|
|
aliases_to_add="$aliases_to_add $external $interface"
|
|
fi
|
|
}
|
|
#
|
|
# At this point, we're just interested in the network translation
|
|
#
|
|
> /var/lib/shorewall/nat
|
|
|
|
if [ -n "$POLICY_MATCH" ]; then
|
|
policyin="-m policy --pol none --dir in"
|
|
policyout="-m policy --pol none --dir out"
|
|
fi
|
|
|
|
[ -n "$RETAIN_ALIASES" ] || save_progress_message "Restoring one-to-one NAT..."
|
|
|
|
while read external interface internal allints localnat; do
|
|
expandv external interface internal allints localnat
|
|
|
|
do_one_nat
|
|
|
|
progress_message " 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 /var/lib/shorewall/nat ]; then
|
|
while read external interface; do
|
|
qt ip addr del $external dev $interface
|
|
done < /var/lib/shorewall/nat
|
|
|
|
rm -f {/var/lib/shorewall}/nat
|
|
fi
|
|
|
|
[ -d /var/lib/shorewall ] && touch /var/lib/shorewall/nat
|
|
}
|
|
|
|
#
|
|
# Setup Network Mapping (NETMAP)
|
|
#
|
|
setup_netmap() {
|
|
|
|
while read type net1 interface net2 ; do
|
|
expandv type net1 interface net2
|
|
|
|
list_search $interface $ALL_INTERFACES || \
|
|
fatal_error "Unknown interface $interface in entry \"$type $net1 $interface $net2\""
|
|
|
|
case $type in
|
|
DNAT)
|
|
addnatrule $(input_chain $interface) -d $net1 -j NETMAP --to $net2
|
|
;;
|
|
SNAT)
|
|
addnatrule $(output_chain $interface) -s $net1 -j NETMAP --to $net2
|
|
;;
|
|
*)
|
|
fatal_error "Invalid type $type in entry \"$type $net1 $interface $net2\""
|
|
;;
|
|
esac
|
|
|
|
progress_message " Network $net1 on $interface mapped to $net2 ($type)"
|
|
|
|
done < $TMP_DIR/netmap
|
|
}
|
|
|
|
#
|
|
# Setup ECN disabling rules
|
|
#
|
|
setup_ecn() # $1 = file name
|
|
{
|
|
local interfaces=""
|
|
local hosts=
|
|
local h
|
|
|
|
strip_file ecn $1
|
|
|
|
echo "Processing $1..."
|
|
|
|
while read interface host; do
|
|
expandv interface host
|
|
list_search $interface $ALL_INTERFACES || \
|
|
startup_error "Unknown interface $interface"
|
|
list_search $interface $interfaces || \
|
|
interfaces="$interfaces $interface"
|
|
[ "x$host" = "x-" ] && host=
|
|
for h in $(separate_list ${host:-0.0.0.0/0}); do
|
|
hosts="$hosts $interface:$h"
|
|
done
|
|
done < $TMP_DIR/ecn
|
|
|
|
if [ -n "$interfaces" ]; then
|
|
progress_message "Setting up ECN control on${interfaces}..."
|
|
|
|
for interface in $interfaces; do
|
|
chain=$(ecn_chain $interface)
|
|
if mangle_chain_exists $chain; then
|
|
flushmangle $chain
|
|
else
|
|
run_iptables -t mangle -N $chain
|
|
run_iptables -t mangle -A POSTROUTING -p tcp -o $interface -j $chain
|
|
run_iptables -t mangle -A OUTPUT -p tcp -o $interface -j $chain
|
|
fi
|
|
done
|
|
|
|
for host in $hosts; do
|
|
interface=${host%:*}
|
|
h=${host#*:}
|
|
run_iptables -t mangle -A $(ecn_chain $interface) -p tcp $(dest_ip_range $h) -j ECN --ecn-tcp-remove
|
|
progress_message " ECN Disabled to $h through $interface"
|
|
done
|
|
fi
|
|
}
|
|
|
|
|
|
#
|
|
# Process a TC Rule - $MARKING_CHAIN is assumed to contain the name of the
|
|
# default marking chain
|
|
#
|
|
process_tc_rule()
|
|
{
|
|
chain=$MARKING_CHAIN target="MARK --set-mark" marktest=
|
|
|
|
verify_designator() {
|
|
[ "$chain" = tcout ] && \
|
|
fatal_error "Chain designator not allowed when source is \$FW; rule \"$rule\""
|
|
chain=$1
|
|
mark="${mark%:*}"
|
|
}
|
|
|
|
add_a_tc_rule() {
|
|
r=
|
|
|
|
if [ "x$source" != "x-" ]; then
|
|
case $source in
|
|
$FW:*)
|
|
chain=tcout
|
|
r="$(source_ip_range ${source#*:}) "
|
|
;;
|
|
*.*.*|+*|!+*)
|
|
r="$(source_ip_range $source) "
|
|
;;
|
|
~*)
|
|
r="$(mac_match $source) "
|
|
;;
|
|
$FW)
|
|
chain=tcout
|
|
;;
|
|
*)
|
|
verify_interface $source || fatal_error "Unknown interface $source in rule \"$rule\""
|
|
r="$(match_source_dev) $source "
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
if [ "x${user:--}" != "x-" ]; then
|
|
|
|
[ "$chain" != tcout ] && \
|
|
fatal_error "Invalid use of a user/group: rule \"$rule\""
|
|
|
|
r="$r-m owner"
|
|
|
|
case "$user" in
|
|
*+*)
|
|
r="$r --cmd-owner ${user#*+} "
|
|
user=${user%+*}
|
|
;;
|
|
esac
|
|
|
|
case "$user" in
|
|
*:*)
|
|
temp="${user%:*}"
|
|
[ -n "$temp" ] && r="$r --uid-owner $temp "
|
|
temp="${user#*:}"
|
|
[ -n "$temp" ] && r="$r --gid-owner $temp "
|
|
;;
|
|
*)
|
|
[ -n "$user" ] && r="$r --uid-owner $user "
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
[ -n "$marktest" ] && r="${r}-m ${marktest}--mark $testval "
|
|
|
|
if [ "x$dest" != "x-" ]; then
|
|
case $dest in
|
|
*.*.*|+*|!+*)
|
|
r="${r}$(dest_ip_range $dest) "
|
|
;;
|
|
*)
|
|
[ "$chain" = tcpre ] && fatal_error "Destination interface is not allowed in the PREROUTING chain"
|
|
verify_interface $dest || fatal_error "Unknown interface $dest in rule \"$rule\""
|
|
r="${r}$(match_dest_dev $dest) "
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
if [ "x$proto" = xipp2p ]; then
|
|
[ "x$port" = "x-" ] && port="ipp2p"
|
|
r="${r}-p tcp -m ipp2p --${port} "
|
|
else
|
|
[ "x$proto" = "x-" ] && proto=all
|
|
[ "x$proto" = "x" ] && proto=all
|
|
[ "$proto" = "all" ] || r="${r}-p $proto "
|
|
[ "x$port" = "x-" ] || r="${r}--dport $port "
|
|
fi
|
|
|
|
[ "x$sport" = "x-" ] || r="${r}--sport $sport "
|
|
|
|
case $chain in
|
|
tcpost)
|
|
run_iptables2 -t mangle -A tcpost $r -j CLASSIFY --set-class $mark
|
|
;;
|
|
*)
|
|
run_iptables2 -t mangle -A $chain $r -j $target $mark
|
|
;;
|
|
esac
|
|
|
|
}
|
|
|
|
if [ "$mark" != "${mark%:*}" ]; then
|
|
case "${mark#*:}" in
|
|
p|P)
|
|
verify_designator tcpre
|
|
;;
|
|
cp|CP)
|
|
verify_designator tcpre
|
|
target="CONNMARK --set-mark"
|
|
;;
|
|
f|F)
|
|
verify_designator tcfor
|
|
;;
|
|
cf|CF)
|
|
verify_designator tcfor
|
|
target="CONNMARK --set-mark"
|
|
;;
|
|
c|C)
|
|
target="CONNMARK --set-mark"
|
|
mark=${mark%:*}
|
|
;;
|
|
*)
|
|
chain=tcpost
|
|
;;
|
|
esac
|
|
|
|
fi
|
|
|
|
case $mark in
|
|
SAVE)
|
|
target="CONNMARK --save-mark --mask 255"
|
|
mark=
|
|
;;
|
|
SAVE/*)
|
|
target="CONNMARK --save-mark --mask"
|
|
mark=${mark#*/}
|
|
verify_mark $mark
|
|
;;
|
|
RESTORE)
|
|
target="CONNMARK --restore-mark --mask 255"
|
|
mark=
|
|
;;
|
|
RESTORE/*)
|
|
target="CONNMARK --restore-mark --mask"
|
|
mark=${mark#*/}
|
|
verify_mark $mark
|
|
;;
|
|
CONTINUE)
|
|
target=RETURN
|
|
mark=
|
|
;;
|
|
*)
|
|
if [ "$chain" != tcpost ]; then
|
|
verify_mark $mark
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
case $testval in
|
|
-)
|
|
;;
|
|
!*:C)
|
|
marktest="connmark ! "
|
|
testval=${testval%:*}
|
|
testval=${testval#!}
|
|
;;
|
|
*:C)
|
|
marktest="connmark "
|
|
testval=${testval%:*}
|
|
;;
|
|
!*)
|
|
marktest="mark ! "
|
|
testval=${testval#!}
|
|
;;
|
|
*)
|
|
[ -n "$testval" ] && marktest="mark "
|
|
;;
|
|
esac
|
|
|
|
if [ -n "$marktest" ] ; then
|
|
case $testval in
|
|
*/*)
|
|
verify_mark ${testval%/*}
|
|
verify_mark ${testval#*/}
|
|
;;
|
|
*)
|
|
verify_mark $testval
|
|
testval=$testval/255
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
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
|
|
|
|
progress_message " TC Rule \"$rule\" added"
|
|
}
|
|
|
|
#
|
|
# Setup queuing and classes
|
|
#
|
|
setup_tc1() {
|
|
#
|
|
# Create the TC mangle chains
|
|
#
|
|
|
|
run_iptables -t mangle -N tcpre
|
|
|
|
for interface in $ROUTEMARK_INTERFACES; do
|
|
eval mark=\$$(chain_base $interface)_routemark
|
|
run_iptables -t mangle -A tcpre -m connmark --mark $mark -j RETURN
|
|
done
|
|
|
|
run_iptables -t mangle -N tcfor
|
|
run_iptables -t mangle -N tcout
|
|
run_iptables -t mangle -N tcpost
|
|
#
|
|
# Process the TC Rules File
|
|
#
|
|
strip_file tcrules
|
|
|
|
while read mark sources dests proto ports sports user testval; do
|
|
expandv mark sources dests proto ports sports user testval
|
|
rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval")
|
|
process_tc_rule
|
|
done < $TMP_DIR/tcrules
|
|
#
|
|
# Link to the TC mangle chains from the main chains
|
|
#
|
|
|
|
run_iptables -t mangle -A FORWARD -j tcfor
|
|
run_iptables -t mangle -A PREROUTING -j tcpre
|
|
run_iptables -t mangle -A OUTPUT -j tcout
|
|
run_iptables -t mangle -A POSTROUTING -j tcpost
|
|
|
|
f=$(find_file tcstart)
|
|
|
|
if [ -f $f ]; then
|
|
|
|
run_user_exit tcstart
|
|
|
|
save_progress_message "Restoring Traffic Control..."
|
|
save_command . $(find_file tcstart)
|
|
fi
|
|
}
|
|
|
|
setup_tc() {
|
|
|
|
echo "Setting up Traffic Control Rules..."
|
|
|
|
setup_tc1
|
|
}
|
|
|
|
#
|
|
# Clear Traffic Shaping
|
|
#
|
|
delete_tc()
|
|
{
|
|
|
|
clear_one_tc() {
|
|
run_and_save_command "tc qdisc del dev $1 root 2> /dev/null"
|
|
run_and_save_command "tc qdisc del dev $1 ingress 2> /dev/null"
|
|
|
|
}
|
|
|
|
save_progress_message "Clearing Traffic Control/QOS"
|
|
|
|
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
|
|
}
|
|
|
|
delete_tc1()
|
|
{
|
|
|
|
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
|
|
}
|
|
|
|
#
|
|
# Process a record from the accounting file
|
|
#
|
|
process_accounting_rule() {
|
|
rule=
|
|
rule2=
|
|
jumpchain=
|
|
user1=
|
|
|
|
accounting_error() {
|
|
error_message "WARNING: Invalid Accounting rule" $action $chain $source $dest $proto $port $sport $user
|
|
}
|
|
|
|
accounting_interface_error() {
|
|
error_message "WARNING: Unknown interface $1 in " $action $chain $source $dest $proto $port $sport $user
|
|
}
|
|
|
|
accounting_interface_verify() {
|
|
verify_interface $1 || accounting_interface_error $1
|
|
}
|
|
|
|
jump_to_chain() {
|
|
if ! havechain $jumpchain; then
|
|
if ! createchain2 $jumpchain No; then
|
|
accounting_error
|
|
return 2
|
|
fi
|
|
fi
|
|
|
|
rule="$rule -j $jumpchain"
|
|
}
|
|
|
|
|
|
case $source in
|
|
*:*)
|
|
accounting_interface_verify ${source%:*}
|
|
rule="$(source_ip_range ${source#*:}) $(match_source_dev ${source%:*})"
|
|
;;
|
|
*.*.*.*|+*|!+*)
|
|
rule="$(source_ip_range $source)"
|
|
;;
|
|
-|all|any)
|
|
;;
|
|
*)
|
|
if [ -n "$source" ]; then
|
|
accounting_interface_verify $source
|
|
rule="$(match_source_dev $source)"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
[ -n "$dest" ] && case $dest in
|
|
*:*)
|
|
accounting_interface_verify ${dest%:*}
|
|
rule="$rule $(dest_ip_range ${dest#*:}) $(match_dest_dev ${dest%:*})"
|
|
;;
|
|
*.*.*.*|+*|!*)
|
|
rule="$rule $(dest_ip_range $dest)"
|
|
;;
|
|
-|all|any)
|
|
;;
|
|
*)
|
|
accounting_interface_verify $dest
|
|
rule="$rule $(match_dest_dev $dest)"
|
|
;;
|
|
esac
|
|
|
|
[ -n "$proto" ] && case $proto in
|
|
-|any|all)
|
|
;;
|
|
ipp2p)
|
|
rule="$rule -p tcp -m ipp2p --${port:-ipp2p}"
|
|
port=
|
|
;;
|
|
*)
|
|
rule="$rule -p $proto"
|
|
;;
|
|
esac
|
|
|
|
[ -n "$port" ] && case $port in
|
|
-|any|all)
|
|
;;
|
|
*)
|
|
rule="$rule --dport $port"
|
|
;;
|
|
esac
|
|
|
|
[ -n "$sport" ] && case $sport in
|
|
-|any|all)
|
|
;;
|
|
*)
|
|
rule="$rule --sport $sport"
|
|
;;
|
|
esac
|
|
|
|
[ -n "$user" ] && case $user in
|
|
-|any|all)
|
|
;;
|
|
*)
|
|
[ "$chain" != OUTPUT ] && \
|
|
fatal_error "Invalid use of a user/group: chain is not OUTPUT but $chain"
|
|
rule="$rule -m owner"
|
|
user1="$user"
|
|
|
|
case "$user" in
|
|
!*+*)
|
|
if [ -n "${user#*+}" ]; then
|
|
rule="$rule ! --cmd-owner ${user#*+} "
|
|
fi
|
|
user1=${user%+*}
|
|
;;
|
|
*+*)
|
|
if [ -n "${user#*+}" ]; then
|
|
rule="$rule --cmd-owner ${user#*+} "
|
|
fi
|
|
user1=${user%+*}
|
|
;;
|
|
esac
|
|
|
|
case "$user1" in
|
|
!*:*)
|
|
if [ "$user1" != "!:" ]; then
|
|
temp="${user1#!}"
|
|
temp="${temp%:*}"
|
|
[ -n "$temp" ] && rule="$rule ! --uid-owner $temp "
|
|
temp="${user1#*:}"
|
|
[ -n "$temp" ] && rule="$rule ! --gid-owner $temp "
|
|
fi
|
|
;;
|
|
*:*)
|
|
if [ "$user1" != ":" ]; then
|
|
temp="${user1%:*}"
|
|
[ -n "$temp" ] && rule="$rule --uid-owner $temp "
|
|
temp="${user1#*:}"
|
|
[ -n "$temp" ] && rule="$rule --gid-owner $temp "
|
|
fi
|
|
;;
|
|
!*)
|
|
[ "$user1" != "!" ] && rule="$rule ! --uid-owner ${user1#!} "
|
|
;;
|
|
*)
|
|
[ -n "$user1" ] && rule="$rule --uid-owner $user1 "
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
|
|
case $action in
|
|
COUNT)
|
|
;;
|
|
DONE)
|
|
rule="$rule -j RETURN"
|
|
;;
|
|
*:COUNT)
|
|
rule2="$rule"
|
|
jumpchain=${action%:*}
|
|
jump_to_chain || return
|
|
;;
|
|
JUMP:*)
|
|
jumpchain=${action#*:}
|
|
jump_to_chain || return
|
|
;;
|
|
*)
|
|
jumpchain=$action
|
|
jump_to_chain || return
|
|
;;
|
|
esac
|
|
|
|
[ "x$chain" = "x-" ] && chain=accounting
|
|
[ -z "$chain" ] && chain=accounting
|
|
|
|
ensurechain1 $chain
|
|
|
|
if $IPTABLES -A $chain $(fix_bang $rule) ; then
|
|
[ -n "$rule2" ] && run_iptables2 -A $jumpchain $rule2
|
|
progress_message " Accounting rule" $action $chain $source $dest $proto $port $sport $user Added
|
|
else
|
|
accounting_error
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Set up Accounting
|
|
#
|
|
setup_accounting() # $1 = Name of accounting file
|
|
{
|
|
|
|
echo "Setting up Accounting..."
|
|
|
|
strip_file accounting $1
|
|
|
|
while read action chain source dest proto port sport user ; do
|
|
expandv action chain source dest proto port sport user
|
|
process_accounting_rule
|
|
done < $TMP_DIR/accounting
|
|
|
|
if havechain accounting; then
|
|
for chain in INPUT FORWARD OUTPUT; do
|
|
run_iptables -A $chain -j accounting
|
|
done
|
|
fi
|
|
|
|
}
|
|
|
|
#
|
|
# Check the configuration
|
|
#
|
|
check_config() {
|
|
|
|
disclaimer() {
|
|
echo
|
|
echo "Notice: The 'check' command is provided to catch"
|
|
echo " obvious errors in a Shorewall configuration."
|
|
echo " It is not designed to catch all possible errors"
|
|
echo " so please don't submit problem reports about"
|
|
echo " error conditions that 'check' doesn't find"
|
|
echo
|
|
}
|
|
|
|
|
|
report_capabilities
|
|
|
|
echo "Verifying Configuration..."
|
|
|
|
verify_os_version
|
|
|
|
if [ -n "$BRIDGING" ]; then
|
|
[ -n "$PHYSDEV_MATCH" ] || startup_error "BRIDGING=Yes requires Physdev Match support in your Kernel and iptables"
|
|
fi
|
|
|
|
[ "$MACLIST_TTL" = "0" ] && MACLIST_TTL=
|
|
|
|
if [ -n "$MACLIST_TTL" -a -z "$RECENT_MATCH" ]; then
|
|
startup_error "MACLIST_TTL requires the Recent Match capability which is not present in your Kernel and/or iptables"
|
|
fi
|
|
|
|
echo "Determining Zones..."
|
|
|
|
determine_zones
|
|
|
|
display_list "Zones:" $ZONES
|
|
|
|
setup_ipsec
|
|
|
|
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 policy file..."
|
|
|
|
validate_policy
|
|
|
|
validate_blacklist
|
|
|
|
echo "Pre-validating Actions..."
|
|
|
|
process_actions1
|
|
|
|
echo "Validating rules file..."
|
|
|
|
rules=$(find_file rules)
|
|
strip_file rules $rules
|
|
process_rules
|
|
|
|
echo "Validating Actions..."
|
|
|
|
process_actions2
|
|
process_actions3
|
|
|
|
rm -rf $TMP_DIR
|
|
[ -n "$RESTOREBASE" ] && rm -f $RESTOREBASE
|
|
|
|
echo "Configuration Validated"
|
|
|
|
disclaimer
|
|
|
|
}
|
|
|
|
#
|
|
# Refresh queuing and classes
|
|
#
|
|
refresh_tc() {
|
|
|
|
echo "Refreshing Traffic Control Rules..."
|
|
|
|
[ -n "$CLEAR_TC" ] && delete_tc1
|
|
|
|
[ -n "$MARK_IN_FORWARD_CHAIN" ] && chain=tcfor || chain=tcpre
|
|
|
|
if mangle_chain_exists $chain; then
|
|
#
|
|
# Flush the TC mangle chains
|
|
#
|
|
run_iptables -t mangle -F $chain
|
|
|
|
run_iptables -t mangle -F 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
|
|
|
|
run_user_exit tcstart
|
|
else
|
|
setup_tc1
|
|
fi
|
|
|
|
}
|
|
|
|
#
|
|
# Add one Filter Rule from an action -- Helper function for the action file processor
|
|
#
|
|
# The caller has established the following variables:
|
|
# COMMAND = current command. If 'check', we're executing a 'check'
|
|
# which only goes through the motions.
|
|
# 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
|
|
# action = The chain for this rule
|
|
# ratelimit = Optional rate limiting clause
|
|
# userandgroup = owner match clause
|
|
# logtag = Log tag
|
|
#
|
|
add_an_action()
|
|
{
|
|
do_ports() {
|
|
if [ -n "$port" ]; then
|
|
dports="--dport"
|
|
if [ -n "$multioption" -a "$port" != "${port%,*}" ]; then
|
|
multiport="$multioption"
|
|
dports="--dports"
|
|
fi
|
|
dports="$dports $port"
|
|
fi
|
|
|
|
if [ -n "$cport" ]; then
|
|
sports="--sport"
|
|
if [ -n "$multioption" -a "$cport" != "${cport%,*}" ]; then
|
|
multiport="$multioption"
|
|
sports="--sports"
|
|
fi
|
|
sports="$sports $cport"
|
|
fi
|
|
}
|
|
|
|
interface_error()
|
|
{
|
|
fatal_error "Unknown interface $1 in rule: \"$rule\""
|
|
}
|
|
|
|
action_interface_verify()
|
|
{
|
|
verify_interface $1 || interface_error $1
|
|
}
|
|
|
|
# Set source variables. The 'cli' variable will hold the client match predicate(s).
|
|
|
|
cli=
|
|
|
|
case "$client" in
|
|
-)
|
|
;;
|
|
*:*)
|
|
action_interface_verify ${client%:*}
|
|
cli="$(match_source_dev ${client%:*}) $(source_ip_range ${client#*:})"
|
|
;;
|
|
*.*.*|+*|!+*)
|
|
cli="$(source_ip_range $client)"
|
|
;;
|
|
~*)
|
|
cli=$(mac_match $client)
|
|
;;
|
|
*)
|
|
if [ -n "$client" ]; then
|
|
action_interface_verify $client
|
|
cli="$(match_source_dev $client)"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
# Set destination variables - 'serv' and 'dest_interface' hold the server match predicate(s).
|
|
|
|
dest_interface=
|
|
serv=
|
|
|
|
case "$server" in
|
|
-)
|
|
;;
|
|
*.*.*|+*|!+*)
|
|
serv=$server
|
|
;;
|
|
~*)
|
|
fatal_error "Rule \"$rule\" - Destination may not be specified by MAC Address"
|
|
;;
|
|
*)
|
|
if [ -n "$server" ]; then
|
|
action_interface_verify $server
|
|
dest_interface="$(match_dest_dev $server)"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
# Setup protocol and port variables
|
|
|
|
sports=
|
|
dports=
|
|
proto=$protocol
|
|
servport=$serverport
|
|
multiport=
|
|
|
|
[ x$port = x- ] && port=
|
|
[ x$cport = x- ] && cport=
|
|
|
|
case $proto in
|
|
tcp|TCP|6)
|
|
do_ports
|
|
[ "$target" = QUEUE ] && proto="$proto --syn"
|
|
;;
|
|
udp|UDP|17)
|
|
do_ports
|
|
;;
|
|
icmp|ICMP|1)
|
|
[ -n "$port" ] && dports="--icmp-type $port"
|
|
;;
|
|
*)
|
|
[ -n "$port" ] && \
|
|
fatal_error "Port number not allowed with protocol \"$proto\"; rule: \"$rule\""
|
|
;;
|
|
esac
|
|
|
|
proto="${proto:+-p $proto}"
|
|
|
|
# Some misc. setup
|
|
|
|
case "$logtarget" in
|
|
LOG)
|
|
[ -z "$loglevel" ] && fatal_error "LOG requires log level"
|
|
;;
|
|
esac
|
|
|
|
if [ $COMMAND != check ]; then
|
|
if [ -n "${serv}" ]; then
|
|
for serv1 in $(separate_list $serv); do
|
|
for srv in $(firewall_ip_range $serv1); do
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $action $logtarget "$ratelimit" "$logtag" -A $userandgroup \
|
|
$(fix_bang $proto $sports $multiport $cli $(dest_ip_range $srv) $dports)
|
|
fi
|
|
|
|
run_iptables2 -A $chain $proto $multiport $cli $sports \
|
|
$(dest_ip_range $srv) $dports $ratelimit $userandgroup -j $target
|
|
done
|
|
done
|
|
else
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $action $logtarget "$ratelimit" "$logtag" -A $userandgroup \
|
|
$(fix_bang $proto $sports $multiport $cli $dest_interface $dports)
|
|
fi
|
|
|
|
run_iptables2 -A $chain $proto $multiport $cli $dest_interface $sports \
|
|
$dports $ratelimit $userandgroup -j $target
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Process a record from an action file for the 'start', 'restart' or 'check' commands
|
|
#
|
|
process_action() # $1 = chain (Chain to add the rules to)
|
|
# $2 = action (The action name for logging purposes)
|
|
# $3 = target (The (possibly modified) contents of the TARGET column)
|
|
# $4 = clients
|
|
# $5 = servers
|
|
# $6 = protocol
|
|
# $7 = ports
|
|
# $8 = cports
|
|
# $9 = ratelimit
|
|
# $10 = userspec
|
|
{
|
|
local chain="$1"
|
|
local action="$2"
|
|
local target="$3"
|
|
local clients="$4"
|
|
local servers="$5"
|
|
local protocol="$6"
|
|
local ports="$7"
|
|
local cports="$8"
|
|
local ratelimit="$9"
|
|
local userspec="${10}"
|
|
local userandgroup=
|
|
local logtag=
|
|
|
|
if [ -n "$ratelimit" ]; then
|
|
case $ratelimit in
|
|
-)
|
|
ratelimit=
|
|
;;
|
|
*:*)
|
|
ratelimit="-m limit --limit ${ratelimit%:*} --limit-burst ${ratelimit#*:}"
|
|
;;
|
|
*)
|
|
ratelimit="-m limit --limit $ratelimit"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
[ "x$userspec" = "x-" ] && userspec=
|
|
|
|
if [ -n "$userspec" ]; then
|
|
userandgroup="-m owner"
|
|
|
|
case "$userspec" in
|
|
!*+*)
|
|
if [ -n "${userspec#*+}" ]; then
|
|
userandgroup="$userandgroup ! --cmd-owner ${userspec#*+}"
|
|
fi
|
|
userspec=${userspec%+*}
|
|
;;
|
|
*+*)
|
|
if [ -n "${userspec#*+}" ]; then
|
|
userandgroup="$userandgroup --cmd-owner ${userspec#*+}"
|
|
fi
|
|
userspec=${userspec%+*}
|
|
;;
|
|
esac
|
|
|
|
case "$userspec" in
|
|
!*:*)
|
|
if [ "$userspec" != "!:" ]; then
|
|
temp="${userspec#!}"
|
|
temp="${temp%:*}"
|
|
[ -n "$temp" ] && userandgroup="$userandgroup ! --uid-owner $temp"
|
|
temp="${userspec#*:}"
|
|
[ -n "$temp" ] && userandgroup="$userandgroup ! --gid-owner $temp"
|
|
fi
|
|
;;
|
|
*:*)
|
|
if [ "$userspec" != ":" ]; then
|
|
temp="${userspec%:*}"
|
|
[ -n "$temp" ] && userandgroup="$userandgroup --uid-owner $temp"
|
|
temp="${userspec#*:}"
|
|
[ -n "$temp" ] && userandgroup="$userandgroup --gid-owner $temp"
|
|
fi
|
|
;;
|
|
!*)
|
|
[ "$userspec" != "!" ] && userandgroup="$userandgroup ! --uid-owner ${userspec#!}"
|
|
;;
|
|
*)
|
|
[ -n "$userspec" ] && userandgroup="$userandgroup --uid-owner $userspec"
|
|
;;
|
|
esac
|
|
|
|
[ "$userandgroup" = "-m owner" ] && userandgroup=
|
|
fi
|
|
|
|
# Isolate log level
|
|
|
|
if [ "$target" = "${target%:*}" ]; then
|
|
loglevel=
|
|
else
|
|
loglevel="${target#*:}"
|
|
target="${target%%:*}"
|
|
expandv loglevel
|
|
if [ "$loglevel" != "${loglevel%:*}" ]; then
|
|
logtag="${loglevel#*:}"
|
|
loglevel="${loglevel%:*}"
|
|
expandv logtag
|
|
fi
|
|
|
|
case $loglevel in
|
|
none*)
|
|
loglevel=
|
|
logtag=
|
|
[ $target = LOG ] && return
|
|
;;
|
|
esac
|
|
|
|
loglevel=${loglevel%\!}
|
|
fi
|
|
|
|
logtarget="$target"
|
|
|
|
case $target in
|
|
REJECT)
|
|
target=reject
|
|
;;
|
|
CONTINUE)
|
|
target=RETURN
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
|
|
# Generate Netfilter rule(s)
|
|
|
|
[ "x$protocol" = "x-" ] && protocol=all || protocol=${protocol:=all}
|
|
|
|
if [ -n "$XMULTIPORT" ] && \
|
|
! list_search $protocol "icmp" "ICMP" "1" && \
|
|
[ $(( $(list_count $ports) + $(list_count1 $(split $ports ) ) )) -le 16 -a \
|
|
$(( $(list_count $cports) + $(list_count1 $(split $cports ) ) )) -le 16 ]
|
|
then
|
|
#
|
|
# Extended MULTIPORT is enabled, and less than
|
|
# 16 ports are listed (port ranges count as two ports) - use multiport match.
|
|
#
|
|
multioption="-m multiport"
|
|
for client in $(separate_list ${clients:=-}); do
|
|
for server in $(separate_list ${servers:=-}); do
|
|
#
|
|
# add_an_action() modifies these so we must set their values each time
|
|
#
|
|
port=${ports:=-}
|
|
cport=${cports:=-}
|
|
add_an_action
|
|
done
|
|
done
|
|
elif [ -n "$MULTIPORT" ] && \
|
|
! list_search $protocol "icmp" "ICMP" "1" && \
|
|
[ "$ports" = "${ports%:*}" -a \
|
|
"$cports" = "${cports%:*}" -a \
|
|
$(list_count $ports) -le 15 -a \
|
|
$(list_count $cports) -le 15 ]
|
|
then
|
|
#
|
|
# MULTIPORT is enabled, there are no port ranges in the rule and less than
|
|
# 16 ports are listed - use multiport match.
|
|
#
|
|
multioption="-m multiport"
|
|
for client in $(separate_list ${clients:=-}); do
|
|
for server in $(separate_list ${servers:=-}); do
|
|
#
|
|
# add_an_action() modifies these so we must set their values each time
|
|
#
|
|
port=${ports:=-}
|
|
cport=${cports:=-}
|
|
add_an_action
|
|
done
|
|
done
|
|
else
|
|
#
|
|
# MULTIPORT is disabled or the rule isn't compatible with multiport match
|
|
#
|
|
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_an_action
|
|
done
|
|
done
|
|
done
|
|
done
|
|
fi
|
|
#
|
|
# Report Result
|
|
#
|
|
if [ $COMMAND = check ]; then
|
|
progress_message " Rule \"$rule\" checked."
|
|
else
|
|
progress_message " Rule \"$rule\" added."
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Create and record a log action chain -- Log action chains have names
|
|
# that are formed from the action name by prepending a "%" and appending
|
|
# a 1- or 2-digit sequence number. In the functions that follow,
|
|
# the CHAIN, LEVEL and TAG variable serves as arguments to the user's
|
|
# exit. We call the exit corresponding to the name of the action but we
|
|
# set CHAIN to the name of the iptables chain where rules are to be added.
|
|
# Similarly, LEVEL and TAG contain the log level and log tag respectively.
|
|
#
|
|
# For each <action>, we maintain two variables:
|
|
#
|
|
# <action>_actchain - The action chain number.
|
|
# <action>_chains - List of ( level[:tag] , chainname ) pairs
|
|
#
|
|
# The maximum length of a chain name is 30 characters -- since the log
|
|
# action chain name is 2-3 characters longer than the base chain name,
|
|
# this function truncates the original chain name where necessary before
|
|
# it adds the leading "%" and trailing sequence number.
|
|
|
|
createlogactionchain() # $1 = Action Name, $2 = Log Level [: Log Tag ]
|
|
{
|
|
local actchain= action=$1 level=$2
|
|
|
|
eval actchain=\${${action}_actchain}
|
|
|
|
case ${#action} in
|
|
29|30)
|
|
CHAIN=$(echo $action | truncate 28) # %...n makes 30
|
|
;;
|
|
*)
|
|
CHAIN=${action}
|
|
;;
|
|
esac
|
|
|
|
[ "$COMMAND" != check ] && \
|
|
while havechain %${CHAIN}${actchain}; do
|
|
actchain=$(($actchain + 1))
|
|
[ $actchain -eq 10 -a ${#CHAIN} -eq 28 ] && CHAIN=$(echo $CHAIN | truncate 27) # %...nn makes 30
|
|
done
|
|
|
|
CHAIN=%${CHAIN}${actchain}
|
|
|
|
eval ${action}_actchain=$(($actchain + 1))
|
|
|
|
if [ $COMMAND != check ]; then
|
|
createchain $CHAIN No
|
|
LEVEL=${level%:*}
|
|
if [ "$LEVEL" != "$level" ]; then
|
|
TAG=${level#*:}
|
|
else
|
|
TAG=
|
|
fi
|
|
run_user_exit $1
|
|
fi
|
|
|
|
eval ${action}_chains=\"\$${action}_chains $level $CHAIN\"
|
|
|
|
}
|
|
|
|
#
|
|
# Create an action chain and run it's associated user exit
|
|
#
|
|
|
|
createactionchain() # $1 = Action, including log level and tag if any
|
|
{
|
|
case $1 in
|
|
*:*:*)
|
|
set -- $(split $1)
|
|
createlogactionchain $1 $2:$3
|
|
;;
|
|
*:*)
|
|
set -- $(split $1)
|
|
createlogactionchain $1 $2
|
|
;;
|
|
*)
|
|
CHAIN=$1
|
|
if [ $COMMAND != check ]; then
|
|
LEVEL=
|
|
TAG=
|
|
createchain $CHAIN no
|
|
run_user_exit $CHAIN
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
#
|
|
# Find the chain that handles the passed action. If the chain cannot be found,
|
|
# a fatal error is generated and the function does not return.
|
|
#
|
|
find_logactionchain() # $1 = Action, including log level and tag if any
|
|
{
|
|
local fullaction=$1 action=${1%%:*} level= chains=
|
|
|
|
case $fullaction in
|
|
*:*)
|
|
level=${fullaction#*:}
|
|
;;
|
|
*)
|
|
if [ $COMMAND != check ]; then
|
|
havechain $action || fatal_error "Fatal error in find_logactionchain"
|
|
fi
|
|
|
|
echo $action
|
|
return
|
|
;;
|
|
esac
|
|
|
|
eval chains="\$${action}_chains"
|
|
|
|
set -- $chains
|
|
|
|
while [ $# -gt 0 ]; do
|
|
[ "$1" = "$level" ] && { echo $2 ; return ; }
|
|
shift;shift
|
|
done
|
|
|
|
fatal_error "Fatal error in find_logactionchain"
|
|
|
|
}
|
|
|
|
#
|
|
# This function determines the logging for a subordinate action or a rule within a subordinate action
|
|
#
|
|
merge_levels() # $1=level at which superior action is called, $2=level at which the subordinate rule is called
|
|
{
|
|
local superior=$1 subordinate=$2
|
|
|
|
set -- $(split $1)
|
|
|
|
case $superior in
|
|
*:*:*)
|
|
case $2 in
|
|
'none!')
|
|
echo ${subordinate%%:*}:'none!'
|
|
return
|
|
;;
|
|
*'!')
|
|
echo ${subordinate%%:*}:$2:$3
|
|
return
|
|
;;
|
|
*)
|
|
case $subordinate in
|
|
*:*)
|
|
echo $subordinate
|
|
return
|
|
;;
|
|
*)
|
|
echo ${subordinate%%:*}:$2:$3
|
|
return
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
*:*)
|
|
case $2 in
|
|
'none!')
|
|
echo ${subordinate%%:*}:'none!'
|
|
return
|
|
;;
|
|
*'!')
|
|
echo ${subordinate%%:*}:$2
|
|
return
|
|
;;
|
|
*)
|
|
case $subordinate in
|
|
*:*)
|
|
echo $subordinate
|
|
return
|
|
;;
|
|
*)
|
|
echo ${subordinate%%:*}:$2
|
|
return
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
echo $subordinate
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# This function substitutes the second argument for the first part of the first argument up to the first colon (":")
|
|
#
|
|
# Example:
|
|
#
|
|
# substitute_action DNAT PARAM:info:FTP
|
|
#
|
|
# produces "DNAT:info:FTP"
|
|
#
|
|
substitute_action() # $1 = parameter, $2 = action
|
|
{
|
|
local logpart=${2%%:*}
|
|
|
|
case $2 in
|
|
*:*)
|
|
echo $1:${logpart%/}
|
|
;;
|
|
*)
|
|
echo $1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
#
|
|
# This function maps old action names into their new macro equivalents
|
|
#
|
|
map_old_action() # $1 = Potential Old Action
|
|
{
|
|
local macro= aktion
|
|
|
|
if [ -n "$MAPOLDACTIONS" ]; then
|
|
case $1 in
|
|
*/*)
|
|
echo $1
|
|
return
|
|
;;
|
|
*)
|
|
if [ -f $(find_file $1) ]; then
|
|
echo $1
|
|
return
|
|
fi
|
|
|
|
case $1 in
|
|
Allow*)
|
|
macro=${1#*w}
|
|
aktion=ACCEPT
|
|
;;
|
|
Drop*)
|
|
macro=${1#*p}
|
|
aktion=DROP
|
|
;;
|
|
Reject*)
|
|
macro=${1#*t}
|
|
aktion=REJECT
|
|
;;
|
|
*)
|
|
echo $1
|
|
return
|
|
;;
|
|
esac
|
|
esac
|
|
|
|
if [ -f $(find_file macro.$macro) ]; then
|
|
echo $macro/$aktion
|
|
fi
|
|
fi
|
|
|
|
echo $1
|
|
}
|
|
|
|
#
|
|
# The next three functions implement the three phases of action processing.
|
|
#
|
|
# The first phase (process_actions1) occurs before the rules file is processed. /usr/share/shorewall/actions.std
|
|
# and /etc/shorewall/actions are scanned (in that order) and for each action:
|
|
#
|
|
# a) The related action definition file is located and scanned.
|
|
# b) Forward and unresolved action references are trapped as errors.
|
|
# c) A dependency graph is created. For each <action>, the variable 'requiredby_<action>' lists the
|
|
# action[:level[:tag]] of each action invoked by <action>.
|
|
# d) All actions are listed in the global variable ACTIONS.
|
|
# e) Common actions are recorded (in variables of the name <policy>_common) and are added to the global
|
|
# USEDACTIONS
|
|
#
|
|
# As the rules file is scanned, each action[:level[:tag]] is merged onto the USEDACTIONS list. When an <action>
|
|
# is merged onto this list, its action chain is created. Where logging is specified, a chain with the name
|
|
# %<action>n is used where the <action> name is truncated on the right where necessary to ensure that the total
|
|
# length of the chain name does not exceed 30 characters.
|
|
#
|
|
# The second phase (process_actions2) occurs after the rules file is scanned. The transitive closure of
|
|
# USEDACTIONS is generated; again, as new actions are merged onto this list, their action chains are created.
|
|
#
|
|
# The final phase (process_actions3) is to traverse the USEDACTIONS list populating each chain appropriately
|
|
# by reading the action definition files and creating rules. Note that a given action definition file is
|
|
# processed once for each unique [:level[:tag]] applied to an invocation of the action.
|
|
#
|
|
process_actions1() {
|
|
|
|
ACTIONS="dropBcast allowBcast dropNonSyn dropNotSyn rejNotSyn dropInvalid allowInvalid allowinUPnP allowoutUPnP forwardUPnP"
|
|
|
|
USEDACTIONS=
|
|
|
|
strip_file actions
|
|
|
|
strip_file actions.std /usr/share/shorewall/actions.std
|
|
|
|
for inputfile in actions.std actions; do
|
|
while read xaction rest; do
|
|
[ "x$rest" = x ] || fatal_error "Invalid Action: $xaction $rest"
|
|
|
|
case $xaction in
|
|
*:*)
|
|
temp=${xaction#*:}
|
|
[ ${#temp} -le 30 ] || fatal_error "Action Name Longer than 30 Characters: $temp"
|
|
xaction=${xaction%:*}
|
|
case $temp in
|
|
ACCEPT|REJECT|DROP|QUEUE)
|
|
eval ${temp}_common=$xaction
|
|
if [ -n "$xaction" ] && ! list_search $xaction $USEDACTIONS; then
|
|
USEDACTIONS="$USEDACTIONS $xaction"
|
|
fi
|
|
;;
|
|
*)
|
|
startup_error "Common Actions are only allowed for ACCEPT, DROP, REJECT and QUEUE"
|
|
;;
|
|
esac
|
|
esac
|
|
|
|
[ -z "$xaction" ] && continue
|
|
|
|
[ "$xaction" = "$(chain_base $xaction)" ] || startup_error "Invalid Action Name: $xaction"
|
|
|
|
if ! list_search $xaction $ACTIONS; then
|
|
f=action.$xaction
|
|
fn=$(find_file $f)
|
|
|
|
eval requiredby_${action}=
|
|
|
|
if [ -f $fn ]; then
|
|
echo " Pre-processing $fn..."
|
|
strip_file $f $fn
|
|
while read xtarget xclients xservers xprotocol xports xcports xratelimit $xuserspec; do
|
|
expandv xtarget
|
|
temp="${xtarget%%:*}"
|
|
case "$temp" in
|
|
ACCEPT|DROP|REJECT|LOG|QUEUE|CONTINUE)
|
|
;;
|
|
*)
|
|
if list_search $temp $ACTIONS; then
|
|
eval requiredby=\"\$requiredby_${xaction}\"
|
|
list_search $xtarget $requiredby || eval requiredby_${xaction}=\"$requiredby $xtarget\"
|
|
else
|
|
temp=$(map_old_action $temp)
|
|
|
|
case $temp in
|
|
*/*)
|
|
param=${temp#*/}
|
|
case $param in
|
|
ACCEPT|DROP|REJECT|LOG|QUEUE|CONTINUE)
|
|
;;
|
|
*)
|
|
rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec"
|
|
startup_error "Invalid Macro Parameter in rule \"$rule\""
|
|
;;
|
|
esac
|
|
temp=${temp%%/*}
|
|
;;
|
|
esac
|
|
|
|
f1=macro.${temp}
|
|
fn=$(find_file $f1)
|
|
|
|
if [ ! -f $TMP_DIR/$f1 ]; then
|
|
if [ -f $fn ]; then
|
|
strip_file $f1 $fn
|
|
else
|
|
rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec"
|
|
startup_error "Invalid TARGET in rule \"$rule\""
|
|
fi
|
|
|
|
progress_message " ..Expanding Macro $fn..."
|
|
|
|
while read mtarget mclients mservers mprotocol mports mcports mratelimit muserspec; do
|
|
expandv mtarget
|
|
temp="${mtarget%%:*}"
|
|
case "$temp" in
|
|
ACCEPT|DROP|REJECT|LOG|QUEUE|CONTINUE|PARAM)
|
|
;;
|
|
*)
|
|
rule="$mtarget $mclients $mservers $mprotocol $mports $mcports $mratelimit $muserspec"
|
|
startup_error "Invalid TARGET in rule \"$rule\""
|
|
esac
|
|
done < $TMP_DIR/$f1
|
|
|
|
progress_message " ..End Macro"
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
esac
|
|
done < $TMP_DIR/$f
|
|
else
|
|
startup_error "Missing Action File: $f"
|
|
fi
|
|
|
|
ACTIONS="$ACTIONS $xaction"
|
|
fi
|
|
done < $TMP_DIR/$inputfile
|
|
done
|
|
}
|
|
|
|
process_actions2() {
|
|
|
|
local interfaces="$(find_interfaces_by_option upnp)"
|
|
|
|
if [ -n "$interfaces" ]; then
|
|
if ! list_search forwardUPnP $USEDACTIONS; then
|
|
error_message "WARNING:Missing forwardUPnP rule (required by 'upnp' interface option on $interfaces)"
|
|
USEDACTIONS="$USEDACTIONS forwardUPnP"
|
|
fi
|
|
fi
|
|
|
|
progress_message " Generating Transitive Closure of Used-action List..."
|
|
|
|
changed=Yes
|
|
|
|
while [ -n "$changed" ]; do
|
|
changed=
|
|
for xaction in $USEDACTIONS; do
|
|
|
|
eval required=\"\$requiredby_${xaction%%:*}\"
|
|
|
|
for xaction1 in $required; do
|
|
#
|
|
# Generate the action that will be passed to process_action by merging the
|
|
# logging specified when the action was invoked with the logging in the
|
|
# invocation of the subordinate action (usually no logging)
|
|
#
|
|
xaction2=$(merge_levels $xaction $xaction1)
|
|
|
|
if ! list_search $xaction2 $USEDACTIONS; then
|
|
#
|
|
# We haven't seen this one before -- create and record a chain to handle it
|
|
#
|
|
USEDACTIONS="$USEDACTIONS $xaction2"
|
|
createactionchain $xaction2
|
|
changed=Yes
|
|
fi
|
|
done
|
|
done
|
|
done
|
|
}
|
|
|
|
process_actions3() {
|
|
|
|
for xaction in $USEDACTIONS; do
|
|
#
|
|
# Find the chain associated with this action:level:tag
|
|
#
|
|
xchain=$(find_logactionchain $xaction)
|
|
#
|
|
# Split the action:level:tag
|
|
#
|
|
set -- $(split $xaction)
|
|
|
|
xaction1=$1
|
|
xlevel=$2
|
|
xtag=$3
|
|
#
|
|
# Handle Builtin actions
|
|
#
|
|
case $xaction1 in
|
|
dropBcast)
|
|
if [ "$COMMAND" != check ]; then
|
|
if [ -n "$PKTTYPE" ]; then
|
|
case $xlevel in
|
|
none'!')
|
|
;;
|
|
*)
|
|
if [ -n "$xlevel" ]; then
|
|
log_rule_limit ${xlevel%\!} $xchain dropBcast DROP "" "$xtag" -A -m pkttype --pkt-type broadcast
|
|
log_rule_limit ${xlevel%\!} $xchain dropBcast DROP "" "$xtag" -A -m pkttype --pkt-type multicast
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
run_iptables -A dropBcast -m pkttype --pkt-type broadcast -j DROP
|
|
run_iptables -A dropBcast -m pkttype --pkt-type multicast -j DROP
|
|
else
|
|
for address in $(find_broadcasts) 255.255.255.255 224.0.0.0/4 ; do
|
|
case $xlevel in
|
|
none*)
|
|
;;
|
|
*)
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain dropBcast DROP "" "$xtag" -A -d $address
|
|
;;
|
|
esac
|
|
|
|
run_iptables -A $xchain -d $address -j DROP
|
|
done
|
|
fi
|
|
fi
|
|
;;
|
|
allowBcast)
|
|
if [ "$COMMAND" != check ]; then
|
|
if [ -n "$PKTTYPE" ]; then
|
|
case $xlevel in
|
|
none'!')
|
|
;;
|
|
*)
|
|
if [ -n "$xlevel" ]; then
|
|
log_rule_limit ${xlevel%\!} $xchain allowBcast ACCEPT "" "$xtag" -A -m pkttype --pkt-type broadcast
|
|
log_rule_limit ${xlevel%\!} $xchain allowBcast ACCEPT "" "$xtag" -A -m pkttype --pkt-type multicast
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
run_iptables -A allowBcast -m pkttype --pkt-type broadcast -j ACCEPT
|
|
run_iptables -A allowBcast -m pkttype --pkt-type multicast -j ACCEPT
|
|
else
|
|
for address in $(find_broadcasts) 255.255.255.255 224.0.0.0/4 ; do
|
|
case $xlevel in
|
|
none*)
|
|
;;
|
|
*)
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain allowBcast ACCEPT "" "$xtag" -A -d $address
|
|
;;
|
|
esac
|
|
|
|
run_iptables -A $xchain -d $address -j ACCEPT
|
|
done
|
|
fi
|
|
fi
|
|
;;
|
|
dropNonSyn)
|
|
error_message "WARNING: \"dropNonSyn\" has been replaced by \"dropNotSyn\""
|
|
|
|
if [ "$COMMAND" != check ]; then
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain dropNonSyn DROP "" "$xtag" -A -p tcp ! --syn
|
|
run_iptables -A $xchain -p tcp ! --syn -j DROP
|
|
fi
|
|
;;
|
|
dropNotSyn)
|
|
if [ "$COMMAND" != check ]; then
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain dropNotSyn DROP "" "$xtag" -A -p tcp ! --syn
|
|
run_iptables -A $xchain -p tcp ! --syn -j DROP
|
|
fi
|
|
;;
|
|
rejNotSyn)
|
|
if [ "$COMMAND" != check ]; then
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain rejNotSyn REJECT "" "$xtag" -A -p tcp ! --syn
|
|
run_iptables -A $xchain -p tcp ! --syn -j REJECT --reject-with tcp-reset
|
|
fi
|
|
;;
|
|
dropInvalid)
|
|
if [ "$COMMAND" != check ]; then
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain dropInvalid DROP "" "$xtag" -A -m state --state INVALID
|
|
run_iptables -A $xchain -m state --state INVALID -j DROP
|
|
fi
|
|
;;
|
|
allowInvalid)
|
|
if [ "$COMMAND" != check ]; then
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain allowInvalid ACCEPT "" "$xtag" -A -m state --state INVALID
|
|
run_iptables -A $xchain -m state --state INVALID -j ACCEPT
|
|
fi
|
|
;;
|
|
forwardUPnP)
|
|
;;
|
|
allowinUPnP)
|
|
if [ "$COMMAND" != check ]; then
|
|
if [ -n "$xlevel" ]; then
|
|
log_rule_limit ${xlevel%\!} $xchain allowinUPnP ACCEPT "" "$xtag" -A -p udp --dport 1900
|
|
log_rule_limit ${xlevel%\!} $xchain allowinUPnP ACCEPT "" "$xtag" -A -p tcp --dport 49152
|
|
fi
|
|
|
|
run_iptables -A $xchain -p udp --dport 1900 -j ACCEPT
|
|
run_iptables -A $xchain -p tcp --dport 49152 -j ACCEPT
|
|
fi
|
|
;;
|
|
allowoutUPnP)
|
|
if [ "$COMMAND" != check ]; then
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain allowoutUPnP ACCEPT "" "$xtag" -A -m owner --owner-cmd upnpd
|
|
run_iptables -A $xchain -m owner --cmd-owner upnpd -j ACCEPT
|
|
fi
|
|
;;
|
|
*)
|
|
#
|
|
# Not a builtin
|
|
#
|
|
f=action.$xaction1
|
|
|
|
echo "Processing $(find_file $f) for Chain $xchain..."
|
|
|
|
while read xtarget xclients xservers xprotocol xports xcports xratelimit xuserspec; do
|
|
expandv xtarget
|
|
#
|
|
# Generate the target:level:tag to pass to process_action()
|
|
#
|
|
xaction2=$(merge_levels $xaction $xtarget)
|
|
|
|
is_macro=
|
|
param=
|
|
|
|
xtarget1=${xaction2%%:*}
|
|
|
|
case $xtarget1 in
|
|
ACCEPT|DROP|REJECT|LOG|QUEUE|CONTINUE)
|
|
#
|
|
# Builtin target -- Nothing to do
|
|
#
|
|
;;
|
|
*)
|
|
if list_search $xtarget1 $ACTIONS ; then
|
|
#
|
|
# An Action -- Replace the target from the file
|
|
# -- with the one generated above
|
|
xtarget=$xaction2
|
|
#
|
|
# And locate the chain for that action:level:tag
|
|
#
|
|
xaction2=$(find_logactionchain $xtarget)
|
|
else
|
|
is_macro=yes
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
expandv xclients xservers xprotocol xports xcports xratelimit xuserspec
|
|
|
|
if [ -n "$is_macro" ]; then
|
|
|
|
xtarget1=$(map_old_action $xtarget1)
|
|
|
|
case $xtarget1 in
|
|
*/*)
|
|
param=${xtarget1#*/}
|
|
xtarget1=${xtarget1%%/*}
|
|
;;
|
|
esac
|
|
|
|
progress_message "..Expanding Macro $(find_file macro.$xtarget1)..."
|
|
while read mtarget mclients mservers mprotocol mports mcports mratelimit muserspec; do
|
|
expandv mtarget mclients mservers mprotocol mports mcports mratelimit muserspec
|
|
|
|
mtarget=$(merge_levels $xaction2 $mtarget)
|
|
|
|
case $mtarget in
|
|
PARAM|PARAM:*)
|
|
[ -n "$param" ] && mtarget=$(substitute_action $param $mtarget) || fatal_error "PARAM requires that a parameter be supplied in macro invocation"
|
|
;;
|
|
esac
|
|
|
|
if [ -n "$mclients" ]; then
|
|
case $mclients in
|
|
-)
|
|
mclients=${xclients}
|
|
;;
|
|
*)
|
|
mclients=${mclients}:${xclients}
|
|
;;
|
|
esac
|
|
else
|
|
mclients=${xclients}
|
|
fi
|
|
|
|
if [ -n "$mservers" ]; then
|
|
case $mservers in
|
|
-)
|
|
mservers=${xservers}
|
|
;;
|
|
*)
|
|
mservers=${mservers}:${xservers}
|
|
;;
|
|
esac
|
|
else
|
|
mservers=${xserverss}
|
|
fi
|
|
|
|
[ -n "$xprotocol" ] && [ "x${xprotocol}" != x- ] && mprotocol=$xprotocol
|
|
[ -n "$xports" ] && [ "x${xports}" != x- ] && mports=$xports
|
|
[ -n "$xcports" ] && [ "x${xcports}" != x- ] && mcports=$xcports
|
|
[ -n "$xratelimit" ] && [ "x${xratelimit}" != x- ] && mratelimit=$xratelimit
|
|
[ -n "$xuserspec" ] && [ "x${xuserspec}" != x- ] && muserspec=$xuserspec
|
|
|
|
rule="$mtarget ${mclients:=-} ${mservers:=-} ${mprotocol:=-} ${mports:=-} ${mcports:=-} ${mratelimit:-} ${muserspec:=-}"
|
|
process_action $xchain $xaction1 $mtarget $mclients $mservers $mprotocol $mports $mcports $mratelimit $muserspec
|
|
done < $TMP_DIR/macro.$xtarget1
|
|
progress_message "..End Macro"
|
|
else
|
|
rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec"
|
|
process_action $xchain $xaction1 $xaction2 $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec
|
|
fi
|
|
done < $TMP_DIR/$f
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
#
|
|
# Add a NAT rule - Helper function for the rules file processor
|
|
#
|
|
# The caller has established the following variables:
|
|
# COMMAND = The current command -- if 'check', we just go through
|
|
# the motions.
|
|
# 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
|
|
# ratelimit = Optional rate limiting clause
|
|
# userandgroup = -m owner match to limit the rule to a particular user and/or group
|
|
# logtag = Log tag
|
|
#
|
|
add_nat_rule() {
|
|
local chain
|
|
local excludedests=
|
|
|
|
# Be sure we can NAT
|
|
|
|
if [ -z "$NAT_ENABLED" ]; then
|
|
fatal_error "Rule \"$rule\" requires NAT which is disabled"
|
|
fi
|
|
|
|
# Parse SNAT address if any
|
|
|
|
if [ "$addr" != "${addr%:*}" ]; then
|
|
fatal_error "SNAT may no longer be specified in a DNAT rule; use /etc/shorewall/masq instead"
|
|
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=${addr:+$addr,}$(find_first_interface_address $interface)
|
|
done
|
|
fi
|
|
;;
|
|
!*)
|
|
if [ $(list_count $addr) -gt 1 ]; then
|
|
excludedests="$(separate_list ${addr#\!})"
|
|
addr=
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
addr=${addr:-0.0.0.0/0}
|
|
|
|
# Select target
|
|
|
|
if [ "$logtarget" = SAME ]; then
|
|
[ -n "$servport" ] && fatal_error "Port mapping not allowed in SAME rules"
|
|
serv1=
|
|
for srv in $(separate_list $serv); do
|
|
serv1="$serv1 --to ${srv}"
|
|
done
|
|
target1="SAME $serv1"
|
|
elif [ -n "$serv" ]; then
|
|
servport="${servport:+:$servport}"
|
|
serv1=
|
|
for srv in $(separate_list $serv); do
|
|
serv1="$serv1 --to-destination ${srv}${servport}"
|
|
done
|
|
target1="DNAT $serv1"
|
|
else
|
|
target1="REDIRECT --to-port $servport"
|
|
fi
|
|
|
|
if [ $source = $FW ]; then
|
|
[ -n "$excludezones" ] && fatal_error "Invalid Source in rule \"$rule\""
|
|
fi
|
|
|
|
# Generate nat table rules
|
|
|
|
if [ $COMMAND != check ]; then
|
|
if [ "$source" = "$FW" ]; then
|
|
if [ -n "$excludedests" ]; then
|
|
chain=nonat${nonat_seq}
|
|
nonat_seq=$(($nonat_seq + 1))
|
|
createnatchain $chain
|
|
|
|
for adr in $(separate_list $addr); do
|
|
run_iptables2 -t nat -A OUTPUT $cli $proto $userandgroup $multiport $sports $dports $(dest_ip_range $adr) -j $chain
|
|
done
|
|
|
|
for adr in $excludedests; do
|
|
addnatrule $chain $(dest_ip_range $adr) -j RETURN
|
|
done
|
|
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule $loglevel $chain $logtarget -t nat
|
|
fi
|
|
|
|
addnatrule $chain $ratelimit $proto -j $target1 # Protocol is necessary for port redirection
|
|
else
|
|
for adr in $(separate_list $addr); do
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel OUTPUT OUTPUT $logtarget "$ratelimit" "$logtag" -A -t nat \
|
|
$(fix_bang $proto $cli $sports $userandgroup $(dest_ip_range $adr) $multiport $dports)
|
|
fi
|
|
|
|
run_iptables2 -t nat -A OUTPUT $ratelimit $proto $sports $userandgroup $(dest_ip_range $adr) $multiport $dports -j $target1
|
|
done
|
|
fi
|
|
else
|
|
chain=$(dnat_chain $source)
|
|
|
|
if [ -n "${excludezones}${excludedests}" ]; then
|
|
chain=nonat${nonat_seq}
|
|
nonat_seq=$(($nonat_seq + 1))
|
|
createnatchain $chain
|
|
|
|
for adr in $(separate_list $addr); do
|
|
addnatrule $(dnat_chain $source) $cli $proto $multiport $sports $dports $(dest_ip_range $adr) -j $chain
|
|
done
|
|
|
|
for z in $(separate_list $excludezones); do
|
|
eval hosts=\$${z}_hosts
|
|
for host in $hosts; do
|
|
addnatrule $chain $(match_source_hosts ${host#*:}) -j RETURN
|
|
done
|
|
done
|
|
|
|
for adr in $excludedests; do
|
|
addnatrule $chain $(dest_ip_range $adr) -j RETURN
|
|
done
|
|
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A -t nat
|
|
fi
|
|
|
|
addnatrule $chain $ratelimit $proto -j $target1 # Protocol is necessary for port redirection
|
|
else
|
|
for adr in $(separate_list $addr); do
|
|
if [ -n "$loglevel" ]; then
|
|
ensurenatchain $chain
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A -t nat \
|
|
$(fix_bang $proto $cli $sports $(dest_ip_range $adr) $multiport $dports)
|
|
fi
|
|
|
|
addnatrule $chain $proto $ratelimit $cli $sports \
|
|
-d $adr $multiport $dports -j $target1
|
|
done
|
|
fi
|
|
fi
|
|
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
|
|
|
|
[ "x$addr" = "x0.0.0.0/0" ] && addr=
|
|
ratelimit=
|
|
}
|
|
|
|
#
|
|
# Add one Filter Rule -- Helper function for the rules file processor
|
|
#
|
|
# The caller has established the following variables:
|
|
# COMMAND = current command. If 'check', we're executing a 'check'
|
|
# which only goes through the motions.
|
|
# 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
|
|
# ratelimit = Optional rate limiting clause
|
|
# userandgroup= -m owner clause
|
|
# userspec = User name
|
|
# logtag = Log tag
|
|
#
|
|
add_a_rule()
|
|
{
|
|
local natrule=
|
|
|
|
do_ports() {
|
|
if [ -n "$port" ]; then
|
|
dports="--dport"
|
|
if [ -n "$multioption" -a "$port" != "${port%,*}" ]; then
|
|
multiport="$multioption"
|
|
dports="--dports"
|
|
fi
|
|
dports="$dports $port"
|
|
fi
|
|
|
|
if [ -n "$cport" ]; then
|
|
sports="--sport"
|
|
if [ -n "$multioption" -a "$cport" != "${cport%,*}" ]; then
|
|
multiport="$multioption"
|
|
sports="--sports"
|
|
fi
|
|
sports="$sports $cport"
|
|
fi
|
|
}
|
|
|
|
interface_error()
|
|
{
|
|
fatal_error "Unknown interface $1 in rule: \"$rule\""
|
|
}
|
|
|
|
rule_interface_verify()
|
|
{
|
|
verify_interface $1 || interface_error $1
|
|
}
|
|
|
|
# Set source variables. The 'cli' variable will hold the client match predicate(s).
|
|
|
|
cli=
|
|
|
|
case "$client" in
|
|
-)
|
|
;;
|
|
*:*)
|
|
rule_interface_verify ${client%:*}
|
|
cli="$(match_source_dev ${client%:*}) $(source_ip_range ${client#*:})"
|
|
;;
|
|
*.*.*|+*)
|
|
cli="$(source_ip_range $client)"
|
|
;;
|
|
~*)
|
|
cli=$(mac_match $client)
|
|
;;
|
|
*)
|
|
if [ -n "$client" ]; then
|
|
rule_interface_verify $client
|
|
cli="$(match_source_dev $client)"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
# Set destination variables - 'serv' and 'dest_interface' hold the server match predicate(s).
|
|
|
|
dest_interface=
|
|
serv=
|
|
|
|
case "$server" in
|
|
-)
|
|
;;
|
|
*.*.*|+*)
|
|
serv=$server
|
|
;;
|
|
~*)
|
|
fatal_error "Rule \"$rule\" - Destination may not be specified by MAC Address"
|
|
;;
|
|
*)
|
|
if [ -n "$server" ]; then
|
|
[ -n "$nonat" ] && fatal_error "Destination interface not allowed with $logtarget"
|
|
rule_interface_verify $server
|
|
dest_interface="$(match_dest_dev $server)"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
# Setup protocol and port variables
|
|
|
|
sports=
|
|
dports=
|
|
proto=$protocol
|
|
addr=$address
|
|
servport=$serverport
|
|
multiport=
|
|
|
|
[ x$port = x- ] && port=
|
|
[ x$cport = x- ] && cport=
|
|
|
|
case $proto in
|
|
tcp|TCP|6)
|
|
do_ports
|
|
[ "$target" = QUEUE ] && proto="$proto --syn"
|
|
;;
|
|
udp|UDP|17)
|
|
do_ports
|
|
;;
|
|
icmp|ICMP|1)
|
|
[ -n "$port" ] && dports="--icmp-type $port"
|
|
;;
|
|
all|ALL)
|
|
[ -n "$port" ] && \
|
|
fatal_error "Port number not allowed with protocol \"all\"; rule: \"$rule\""
|
|
proto=
|
|
;;
|
|
*)
|
|
[ -n "$port" ] && \
|
|
fatal_error "Port number not allowed with protocol \"$proto\"; rule: \"$rule\""
|
|
;;
|
|
esac
|
|
|
|
proto="${proto:+-p $proto}"
|
|
|
|
# Some misc. setup
|
|
|
|
case "$logtarget" in
|
|
ACCEPT|DROP|REJECT|CONTINUE)
|
|
if [ -z "$proto" -a -z "$cli" -a -z "$serv" -a -z "$servport" -a -z "$userandgroup" ] ; then
|
|
error_message "Warning -- Rule \"$rule\" is a POLICY"
|
|
error_message " -- and should be moved to the policy file"
|
|
fi
|
|
;;
|
|
REDIRECT)
|
|
[ -n "$serv" ] && \
|
|
fatal_error "REDIRECT rules cannot specify a server IP; rule: \"$rule\""
|
|
servport=${servport:=$port}
|
|
natrule=Yes
|
|
;;
|
|
DNAT|SAME)
|
|
[ -n "$serv" ] || \
|
|
fatal_error "$logtarget rules require a server address; rule: \"$rule\""
|
|
natrule=Yes
|
|
;;
|
|
LOG)
|
|
[ -z "$loglevel" ] && \
|
|
fatal_error "LOG requires log level"
|
|
;;
|
|
esac
|
|
|
|
if [ -n "${serv}${servport}" ]; then
|
|
if [ $COMMAND != check ]; then
|
|
|
|
# A specific server or server port given
|
|
|
|
if [ -n "$natrule" ]; then
|
|
add_nat_rule
|
|
elif [ -n "$servport" -a "$servport" != "$port" ]; then
|
|
fatal_error "Only DNAT, SAME and REDIRECT rules may specify destination port mapping; rule \"$rule\""
|
|
fi
|
|
|
|
if [ -z "$dnat_only" ]; then
|
|
if [ -n "$serv" ]; then
|
|
for serv1 in $(separate_list $serv); do
|
|
for srv in $(firewall_ip_range $serv1); do
|
|
if [ -n "$addr" -a -n "$CONNTRACK_MATCH" ]; then
|
|
for adr in $(separate_list $addr); do
|
|
if [ -n "$loglevel" -a -z "$natrule" ]; then
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A -m conntrack --ctorigdst $adr \
|
|
$userandgroup $(fix_bang $proto $sports $multiport $cli $(dest_ip_range $srv) $dports)
|
|
fi
|
|
|
|
run_iptables2 -A $chain $proto $ratelimit $multiport $cli $sports \
|
|
$(dest_ip_range $srv) $dports -m conntrack --ctorigdst $adr $userandgroup -j $target
|
|
done
|
|
else
|
|
if [ -n "$loglevel" -a -z "$natrule" ]; then
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A $userandgroup \
|
|
$(fix_bang $proto $sports $multiport $cli $(dest_ip_range $srv) $dports)
|
|
fi
|
|
|
|
if [ -n "$nonat" ]; then
|
|
addnatrule $(dnat_chain $source) $proto $multiport \
|
|
$cli $sports $(dest_ip_range $srv) $dports $ratelimit $userandgroup -j RETURN
|
|
fi
|
|
|
|
if [ "$logtarget" != NONAT ]; then
|
|
run_iptables2 -A $chain $proto $multiport $cli $sports \
|
|
$(dest_ip_range $srv) $dports $ratelimit $userandgroup -j $target
|
|
fi
|
|
fi
|
|
done
|
|
done
|
|
else
|
|
if [ -n "$loglevel" -a -z "$natrule" ]; then
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A $userandgroup \
|
|
$(fix_bang $proto $sports $multiport $cli $dports)
|
|
fi
|
|
|
|
[ -n "$nonat" ] && \
|
|
addnatrule $(dnat_chain $source) $proto $multiport \
|
|
$cli $sports $dports $ratelimit $userandgroup -j RETURN
|
|
|
|
[ "$logtarget" != NONAT ] && \
|
|
run_iptables2 -A $chain $proto $multiport $cli $sports \
|
|
$dports $ratelimit $userandgroup -j $target
|
|
fi
|
|
fi
|
|
fi
|
|
else
|
|
|
|
# Destination is a simple zone
|
|
|
|
if [ $COMMAND != check ]; then
|
|
if [ -n "$addr" ]; then
|
|
for adr in $(separate_list $addr); do
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A $userandgroup \
|
|
$(fix_bang $proto $multiport $cli $dest_interface $sports $dports -m conntrack --ctorigdst $adr)
|
|
fi
|
|
|
|
if [ "$logtarget" != LOG ]; then
|
|
if [ -n "$nonat" ]; then
|
|
addnatrule $(dnat_chain $source) $proto $multiport \
|
|
$cli $sports $dports $ratelimit $userandgroup -m conntrack --ctorigdst $adr -j RETURN
|
|
fi
|
|
|
|
if [ "$logtarget" != NONAT ]; then
|
|
run_iptables2 -A $chain $proto $multiport $cli $dest_interface \
|
|
$sports $dports $ratelimit $userandgroup -m conntrack --ctorigdst $adr -j $target
|
|
fi
|
|
fi
|
|
done
|
|
else
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A $userandgroup \
|
|
$(fix_bang $proto $multiport $cli $dest_interface $sports $dports)
|
|
fi
|
|
|
|
if [ "$logtarget" != LOG ]; then
|
|
if [ -n "$nonat" ]; then
|
|
addnatrule $(dnat_chain $source) $proto $multiport \
|
|
$cli $sports $dports $ratelimit $userandgroup -j RETURN
|
|
fi
|
|
|
|
if [ "$logtarget" != NONAT ]; then
|
|
run_iptables2 -A $chain $proto $multiport $cli $dest_interface \
|
|
$sports $dports $ratelimit $userandgroup -j $target
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Process a record from the rules file for the 'start', 'restart' or 'check' commands
|
|
#
|
|
process_rule() # $1 = target
|
|
# $2 = clients
|
|
# $3 = servers
|
|
# $4 = protocol
|
|
# $5 = ports
|
|
# $6 = cports
|
|
# $7 = address
|
|
# $8 = ratelimit
|
|
# $9 = userspec
|
|
{
|
|
local target="$1"
|
|
local clients="$2"
|
|
local servers="$3"
|
|
local protocol="$4"
|
|
local ports="$5"
|
|
local cports="$6"
|
|
local address="$7"
|
|
local ratelimit="$8"
|
|
local userspec="$9"
|
|
local userandgroup=
|
|
local logtag=
|
|
local nonat=
|
|
|
|
# Function Body - isolate rate limit
|
|
|
|
[ "x$ratelimit" = "x-" ] && ratelimit=
|
|
|
|
if [ -n "$ratelimit" ]; then
|
|
case $ratelimit in
|
|
*:*)
|
|
ratelimit="-m limit --limit ${ratelimit%:*} --limit-burst ${ratelimit#*:}"
|
|
;;
|
|
*)
|
|
ratelimit="-m limit --limit $ratelimit"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# Isolate log level
|
|
|
|
if [ "$target" = "${target%:*}" ]; then
|
|
loglevel=
|
|
else
|
|
loglevel="${target#*:}"
|
|
target="${target%%:*}"
|
|
expandv loglevel
|
|
if [ "$loglevel" != "${loglevel%:*}" ]; then
|
|
logtag="${loglevel#*:}"
|
|
loglevel="${loglevel%:*}"
|
|
expandv logtag
|
|
fi
|
|
|
|
case $loglevel in
|
|
none*)
|
|
loglevel=
|
|
logtag=
|
|
[ $target = LOG ] && return
|
|
;;
|
|
esac
|
|
|
|
loglevel=${loglevel%\!}
|
|
fi
|
|
#
|
|
# Save the original target in 'logtarget' for logging rules
|
|
#
|
|
logtarget=${target%-}
|
|
#
|
|
# Targets ending in "-" only apply to the nat table
|
|
#
|
|
[ $target = $logtarget ] && dnat_only= || dnat_only=Yes
|
|
|
|
# Tranform the rule:
|
|
#
|
|
# - parse the user specification
|
|
# - set 'target' to the filter table target.
|
|
# - make $FW the destination for REDIRECT
|
|
# - remove '-' suffix from logtargets while setting 'dnat_only'
|
|
# - clear 'address' if it has been set to '-'
|
|
|
|
[ "x$userspec" = x- ] && userspec=
|
|
[ "x$address" = "x-" ] && address=
|
|
|
|
if [ -n "$userspec" ]; then
|
|
|
|
userandgroup="-m owner"
|
|
|
|
case "$userspec" in
|
|
!*+*)
|
|
if [ -n "${userspec#*+}" ]; then
|
|
userandgroup="$userandgroup ! --cmd-owner ${userspec#*+}"
|
|
fi
|
|
userspec=${userspec%+*}
|
|
;;
|
|
*+*)
|
|
if [ -n "${userspec#*+}" ]; then
|
|
userandgroup="$userandgroup --cmd-owner ${userspec#*+}"
|
|
fi
|
|
userspec=${userspec%+*}
|
|
;;
|
|
esac
|
|
|
|
case "$userspec" in
|
|
!*:*)
|
|
if [ "$userspec" != "!:" ]; then
|
|
temp="${userspec#!}"
|
|
temp="${temp%:*}"
|
|
[ -n "$temp" ] && userandgroup="$userandgroup ! --uid-owner $temp"
|
|
temp="${userspec#*:}"
|
|
[ -n "$temp" ] && userandgroup="$userandgroup ! --gid-owner $temp"
|
|
fi
|
|
;;
|
|
*:*)
|
|
if [ "$userspec" != ":" ]; then
|
|
temp="${userspec%:*}"
|
|
[ -n "$temp" ] && userandgroup="$userandgroup --uid-owner $temp"
|
|
temp="${userspec#*:}"
|
|
[ -n "$temp" ] && userandgroup="$userandgroup --gid-owner $temp"
|
|
fi
|
|
;;
|
|
!*)
|
|
[ "$userspec" != "!" ] && userandgroup="$userandgroup ! --uid-owner ${userspec#!}"
|
|
;;
|
|
*)
|
|
[ -n "$userspec" ] && userandgroup="$userandgroup --uid-owner $userspec"
|
|
;;
|
|
esac
|
|
|
|
[ "$userandgroup" = "-m owner" ] && userandgroup=
|
|
fi
|
|
|
|
case $target in
|
|
ACCEPT+|NONAT)
|
|
nonat=Yes
|
|
target=ACCEPT
|
|
;;
|
|
ACCEPT|LOG)
|
|
;;
|
|
DROP)
|
|
[ -n "$ratelimit" ] && fatal_error "Rate Limiting not available with DROP"
|
|
;;
|
|
REJECT)
|
|
target=reject
|
|
;;
|
|
CONTINUE)
|
|
target=RETURN
|
|
;;
|
|
DNAT*|SAME*)
|
|
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 "Empty source zone or qualifier: rule \"$rule\""
|
|
if [ $(list_count $clients) -gt 1 ]; then
|
|
case $clients in
|
|
!*)
|
|
fatal_error "Exclude lists not supported in the SOURCE column"
|
|
;;
|
|
esac
|
|
fi
|
|
fi
|
|
|
|
if [ "$clientzone" = "${clientzone%!*}" ]; then
|
|
excludezones=
|
|
else
|
|
excludezones="${clientzone#*!}"
|
|
clientzone="${clientzone%!*}"
|
|
|
|
case $logtarget in
|
|
DNAT|REDIRECT|SAME)
|
|
;;
|
|
*)
|
|
fatal_error "Exclude list only allowed with DNAT, SAME or REDIRECT"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
validate_zone $clientzone || fatal_error "Undefined Client Zone in rule \"$rule\""
|
|
|
|
# Parse and validate destination
|
|
|
|
source=$clientzone
|
|
|
|
if [ $source = $FW ]; then
|
|
source_hosts=
|
|
elif [ -n "$userspec" ]; then
|
|
fatal_error "Invalid use of a user-qualification: rule \"$rule\""
|
|
else
|
|
eval source_hosts=\"\$${source}_hosts\"
|
|
fi
|
|
|
|
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 "Empty destination zone or server port: rule \"$rule\""
|
|
if [ $(list_count $servers) -gt 1 ]; then
|
|
case $servers in
|
|
!*)
|
|
fatal_error "Exclude lists not supported in the DEST column"
|
|
;;
|
|
esac
|
|
fi
|
|
else
|
|
serverport=
|
|
[ -z "$serverzone" -o -z "$servers" ] && \
|
|
fatal_error "Empty destination zone or qualifier: rule \"$rule\""
|
|
fi
|
|
fi
|
|
|
|
if ! validate_zone $serverzone; then
|
|
fatal_error "Undefined Server Zone in rule \"$rule\""
|
|
fi
|
|
|
|
dest=$serverzone
|
|
|
|
# Ensure that this rule doesn't apply to a NONE policy pair of zones
|
|
|
|
chain=${source}2${dest}
|
|
|
|
eval policy=\$${chain}_policy
|
|
|
|
[ -z "$policy" ] && \
|
|
fatal_error "No policy defined from zone $source to zone $dest"
|
|
|
|
[ $policy = NONE ] && \
|
|
fatal_error "Rules may not override a NONE policy: rule \"$rule\""
|
|
|
|
# Create the canonical chain if it doesn't already exist
|
|
|
|
[ $COMMAND = check ] || ensurechain $chain
|
|
|
|
# Generate Netfilter rule(s)
|
|
|
|
protocol=${protocol:=all}
|
|
|
|
case $logtarget in
|
|
DNAT*|SAME)
|
|
if [ -n "$XMULTIPORT" ] && \
|
|
! list_search $protocol "icmp" "ICMP" "1" && \
|
|
[ $(( $(list_count $ports) + $(list_count1 $(split $ports ) ) )) -le 16 -a \
|
|
$(( $(list_count $cports) + $(list_count1 $(split $cports ) ) )) -le 16 ]
|
|
then
|
|
#
|
|
# Extended MULTIPORT is enabled, and less than
|
|
# 16 ports are listed (port ranges count as two ports) - use multiport match.
|
|
#
|
|
multioption="-m multiport"
|
|
for client in $(separate_list ${clients:=-}); do
|
|
#
|
|
# add_a_rule() modifies these so we must set their values each time
|
|
#
|
|
server=${servers:=-}
|
|
port=${ports:=-}
|
|
cport=${cports:=-}
|
|
add_a_rule
|
|
done
|
|
elif [ -n "$MULTIPORT" ] && \
|
|
! list_search $protocol "icmp" "ICMP" "1" && \
|
|
[ "$ports" = "${ports%:*}" -a \
|
|
"$cports" = "${cports%:*}" -a \
|
|
$(list_count $ports) -le 15 -a \
|
|
$(list_count $cports) -le 15 ]
|
|
then
|
|
#
|
|
# MULTIPORT is enabled, there are no port ranges in the rule and less than
|
|
# 16 ports are listed - use multiport match.
|
|
#
|
|
multioption="-m multiport"
|
|
for client in $(separate_list ${clients:=-}); do
|
|
#
|
|
# add_a_rule() modifies these so we must set their values each time
|
|
#
|
|
server=${servers:=-}
|
|
port=${ports:=-}
|
|
cport=${cports:=-}
|
|
add_a_rule
|
|
done
|
|
else
|
|
#
|
|
# MULTIPORT is disabled or the rule isn't compatible with multiport match
|
|
#
|
|
multioption=
|
|
for client in $(separate_list ${clients:=-}); do
|
|
for port in $(separate_list ${ports:=-}); do
|
|
for cport in $(separate_list ${cports:=-}); do
|
|
server=${servers:=-}
|
|
add_a_rule
|
|
done
|
|
done
|
|
done
|
|
fi
|
|
;;
|
|
*)
|
|
|
|
if [ -n "$XMULTIPORT" ] && \
|
|
! list_search $protocol "icmp" "ICMP" "1" && \
|
|
[ $(( $(list_count $ports) + $(list_count1 $(split $ports ) ) )) -le 16 -a \
|
|
$(( $(list_count $cports) + $(list_count1 $(split $cports ) ) )) -le 16 ]
|
|
then
|
|
#
|
|
# Extended MULTIPORT is enabled, and less than
|
|
# 16 ports are listed (port ranges count as two ports) - use multiport match.
|
|
#
|
|
multioption="-m multiport"
|
|
for client in $(separate_list ${clients:=-}); do
|
|
for server in $(separate_list ${servers:=-}); do
|
|
#
|
|
# add_a_rule() modifies these so we must set their values each time
|
|
#
|
|
port=${ports:=-}
|
|
cport=${cports:=-}
|
|
add_a_rule
|
|
done
|
|
done
|
|
elif [ -n "$MULTIPORT" ] && \
|
|
! list_search $protocol "icmp" "ICMP" "1" && \
|
|
[ "$ports" = "${ports%:*}" -a \
|
|
"$cports" = "${cports%:*}" -a \
|
|
$(list_count $ports) -le 15 -a \
|
|
$(list_count $cports) -le 15 ]
|
|
then
|
|
#
|
|
# MULTIPORT is enabled, there are no port ranges in the rule and less than
|
|
# 16 ports are listed - use multiport match.
|
|
#
|
|
multioption="-m multiport"
|
|
for client in $(separate_list ${clients:=-}); do
|
|
for server in $(separate_list ${servers:=-}); do
|
|
#
|
|
# add_a_rule() modifies these so we must set their values each time
|
|
#
|
|
port=${ports:=-}
|
|
cport=${cports:=-}
|
|
add_a_rule
|
|
done
|
|
done
|
|
else
|
|
#
|
|
# MULTIPORT is disabled or the rule isn't compatible with multiport match
|
|
#
|
|
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
|
|
;;
|
|
esac
|
|
#
|
|
# Report Result
|
|
#
|
|
if [ $COMMAND = check ]; then
|
|
progress_message " Rule \"$rule\" checked."
|
|
else
|
|
progress_message " Rule \"$rule\" added."
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Process a macro invocation in the rules file
|
|
#
|
|
|
|
process_macro() # $1 = target
|
|
# $2 = param
|
|
# $2 = clients
|
|
# $3 = servers
|
|
# $4 = protocol
|
|
# $5 = ports
|
|
# $6 = cports
|
|
# $7 = address
|
|
# $8 = ratelimit
|
|
# $9 = userspec
|
|
{
|
|
local itarget="$1"
|
|
local param="$2"
|
|
local iclients="$3"
|
|
local iservers="$4"
|
|
local iprotocol="$5"
|
|
local iports="$6"
|
|
local icports="$7"
|
|
local iaddress="$8"
|
|
local iratelimit="$9"
|
|
local iuserspec="${10}"
|
|
|
|
progress_message "..Expanding Macro $(find_file macro.${itarget%%:*})..."
|
|
|
|
while read mtarget mclients mservers mprotocol mports mcports mratelimit muserspec; do
|
|
expandv mtarget mclients mservers mprotocol mports mcports mratelimit muserspec
|
|
|
|
mtarget=$(merge_levels $itarget $mtarget)
|
|
|
|
case $mtarget in
|
|
PARAM|PARAM:*)
|
|
[ -n "$param" ] && mtarget=$(substitute_action $param $mtarget) || fatal_error "PARAM requires that a parameter be supplied in macro invocation"
|
|
;;
|
|
esac
|
|
|
|
if [ -n "$mclients" ]; then
|
|
case $mclients in
|
|
-)
|
|
mclients=${iclients}
|
|
;;
|
|
*)
|
|
mclients=${mclients}:${iclients}
|
|
;;
|
|
esac
|
|
else
|
|
mclients=${iclients}
|
|
fi
|
|
|
|
if [ -n "$mservers" ]; then
|
|
case $mservers in
|
|
-)
|
|
mservers=${iservers}
|
|
;;
|
|
*)
|
|
mservers=${mservers}:${iservers}
|
|
;;
|
|
esac
|
|
else
|
|
mservers=${iserverss}
|
|
fi
|
|
|
|
[ -n "$iprotocol" ] && [ "x${iprotocol}" != x- ] && mprotocol=$iprotocol
|
|
[ -n "$iports" ] && [ "x${iports}" != x- ] && mports=$iports
|
|
[ -n "$icports" ] && [ "x${icports}" != x- ] && mcports=$icports
|
|
[ -n "$iratelimit" ] && [ "x${iratelimit}" != x- ] && mratelimit=$iratelimit
|
|
[ -n "$iuserspec" ] && [ "x${iuserspec}" != x- ] && muserspec=$iuserspec
|
|
|
|
rule="$mtarget ${mclients=-} ${mservers:=-} ${mprotocol:=-} ${mports:=-} ${mcports:=-} ${xaddress:=-} ${mratelimit:=-} ${muserspec:=-}"
|
|
process_rule $mtarget $mclients $mservers $mprotocol $mports $mcports ${iaddress:=-} $mratelimit $muserspec
|
|
|
|
done < $TMP_DIR/macro.${itarget%%:*}
|
|
|
|
progress_message "..End Macro"
|
|
|
|
}
|
|
|
|
#
|
|
# Process the rules file for the 'start', 'restart' or 'check' command.
|
|
#
|
|
process_rules() # $1 = "Yes" if the target is a macro.
|
|
{
|
|
#
|
|
# Process a rule where the source or destination is "all"
|
|
#
|
|
process_wildcard_rule() {
|
|
local yclients yservers ysourcezone ydestzone ypolicy
|
|
|
|
for yclients in $xclients; do
|
|
for yservers in $xservers; do
|
|
ysourcezone=${yclients%%:*}
|
|
ydestzone=${yservers%%:*}
|
|
if [ "${ysourcezone}" != "${ydestzone}" ] ; then
|
|
eval ypolicy=\$${ysourcezone}2${ydestzone}_policy
|
|
if [ "$ypolicy" != NONE ] ; then
|
|
if [ "$1" = Yes ]; then
|
|
process_macro $xtarget "$xparam" $yclients $yservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec
|
|
else
|
|
rule="$xtarget $yclients $yservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec"
|
|
process_rule $xtarget $yclients $yservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
|
|
do_it() # $1 = "Yes" if the target is a macro.
|
|
{
|
|
expandv xprotocol xports xcports xaddress xratelimit xuserspec
|
|
|
|
if [ "x$xclients" = xall ]; then
|
|
xclients="$ZONES $FW"
|
|
if [ "x$xservers" = xall ]; then
|
|
xservers="$ZONES $FW"
|
|
fi
|
|
process_wildcard_rule $1
|
|
return
|
|
fi
|
|
|
|
if [ "x$xservers" = xall ]; then
|
|
xservers="$ZONES $FW"
|
|
process_wildcard_rule $1
|
|
return
|
|
fi
|
|
|
|
if [ "$1" = Yes ]; then
|
|
process_macro $xtarget "$xparam" $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec
|
|
else
|
|
rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec"
|
|
process_rule $xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec
|
|
fi
|
|
}
|
|
|
|
while read xtarget xclients xservers xprotocol xports xcports xaddress xratelimit xuserspec; do
|
|
expandv xtarget xclients xservers
|
|
|
|
if [ "x$xclients" = xnone -o "x$servers" = xnone ]; then
|
|
rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec"
|
|
progress_message " Rule \"$rule\" ignored."
|
|
continue
|
|
fi
|
|
|
|
case "${xtarget%%:*}" in
|
|
ACCEPT|ACCEPT+|NONAT|DROP|REJECT|DNAT|DNAT-|REDIRECT|REDIRECT-|LOG|CONTINUE|QUEUE|SAME|SAME-)
|
|
do_it No
|
|
;;
|
|
*)
|
|
if list_search ${xtarget%%:*} $ACTIONS; then
|
|
if ! list_search $xtarget $USEDACTIONS; then
|
|
createactionchain $xtarget
|
|
USEDACTIONS="$USEDACTIONS $xtarget"
|
|
fi
|
|
|
|
xtarget=$(find_logactionchain $xtarget)
|
|
do_it No
|
|
else
|
|
xtarget1=$(map_old_action ${xtarget%%:*})
|
|
|
|
case $xtarget1 in
|
|
*/*)
|
|
xparam=${xtarget1#*/}
|
|
xtarget1=${xtarget1%%/*}
|
|
xtarget=$(substitute_action $xtarget1 $xtarget)
|
|
;;
|
|
*)
|
|
xparam=
|
|
;;
|
|
esac
|
|
|
|
f=macro.$xtarget1
|
|
|
|
if [ -f $TMP_DIR/$f ]; then
|
|
do_it Yes
|
|
else
|
|
fn=$(find_file $f)
|
|
|
|
if [ -f $fn ]; then
|
|
strip_file $f $fn
|
|
do_it Yes
|
|
else
|
|
rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec"
|
|
fatal_error "Invalid Action in rule \"$rule\""
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
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
|
|
*.*.*|+*|!+*)
|
|
#
|
|
# IP Address or networks
|
|
#
|
|
src="$(source_ip_range $src)"
|
|
;;
|
|
~*)
|
|
src=$(mac_match $src)
|
|
;;
|
|
*)
|
|
#
|
|
# Assume that this is a device name
|
|
#
|
|
if ! verify_interface $src ; then
|
|
error_message "WARNING: Unknown Interface in rule \"$rule\" ignored"
|
|
return
|
|
fi
|
|
|
|
src="$(match_source_dev $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
|
|
*.*.*|+*|!+*)
|
|
#
|
|
# IP Address or networks
|
|
#
|
|
;;
|
|
*)
|
|
#
|
|
# 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="$(dest_ip_range $dest)"
|
|
|
|
case $srczone in
|
|
$FW)
|
|
run_iptables2 -t mangle -A outtos \
|
|
$protocol $dest $dports $sports $tos
|
|
;;
|
|
all|ALL)
|
|
run_iptables2 -t mangle -A outtos \
|
|
$protocol $dest $dports $sports $tos
|
|
run_iptables2 -t mangle -A pretos \
|
|
$protocol $dest $dports $sports $tos
|
|
;;
|
|
*)
|
|
if [ -n "$src" ]; then
|
|
run_iptables2 -t mangle -A pretos $src \
|
|
$protocol $dest $dports $sports $tos
|
|
else
|
|
eval interfaces=\$${srczone}_interfaces
|
|
|
|
for interface in $interfaces; do
|
|
run_iptables2 -t mangle -A pretos -i $interface \
|
|
$protocol $dest $dports $sports $tos
|
|
done
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
progress_message " Rule \"$rule\" added."
|
|
}
|
|
|
|
#
|
|
# Process the tos file
|
|
#
|
|
process_tos() # $1 = name of tos file
|
|
{
|
|
echo "Processing $1..."
|
|
|
|
strip_file tos $1
|
|
|
|
if [ -s $TMP_DIR/tos ] ; then
|
|
run_iptables -t mangle -N pretos
|
|
run_iptables -t mangle -N outtos
|
|
|
|
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
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Display elements of a list with leading white space
|
|
#
|
|
display_list() # $1 = List Title, rest of $* = list to display
|
|
{
|
|
[ $# -gt 1 ] && echo " $*"
|
|
}
|
|
|
|
policy_rules() # $1 = chain to add rules to
|
|
# $2 = policy
|
|
# $3 = E/R Policy
|
|
# $4 = loglevel
|
|
{
|
|
local target="$2"
|
|
|
|
case $3 in
|
|
QUEUE)
|
|
run_iptables -I $1 -m state --state RELATED -j ACCEPT
|
|
run_iptables -I $1 -m state --state ESTABLISHED -j QUEUE
|
|
;;
|
|
ACCEPT)
|
|
run_iptables -I $1 -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
;;
|
|
esac
|
|
|
|
case "$target" in
|
|
ACCEPT)
|
|
[ -n "$ACCEPT_common" ] && run_iptables -A $1 -j $ACCEPT_common
|
|
;;
|
|
DROP)
|
|
[ -n "$DROP_common" ] && run_iptables -A $1 -j $DROP_common
|
|
;;
|
|
REJECT)
|
|
[ -n "$REJECT_common" ] && run_iptables -A $1 -j $REJECT_common
|
|
target=reject
|
|
;;
|
|
QUEUE)
|
|
[ -n "$QUEUE_common" ] && run_iptables -A $1 -j $QUEUE_common
|
|
;;
|
|
CONTINUE)
|
|
target=
|
|
;;
|
|
*)
|
|
fatal_error "Invalid policy ($policy) for $1"
|
|
;;
|
|
esac
|
|
|
|
if [ $# -eq 4 -a "x${4}" != "x-" ]; then
|
|
log_rule $4 $1 $2
|
|
fi
|
|
|
|
[ -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() {
|
|
#
|
|
# Insert a rule of ESTABLISHED,RELATED packets at the head of the
|
|
# canonical 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
|
|
#
|
|
case $epolicy in
|
|
QUEUE)
|
|
run_iptables -I $chain -m state --state RELATED -j ACCEPT
|
|
run_iptables -I $chain -m state --state ESTABLISHED -j QUEUE
|
|
;;
|
|
ACCEPT)
|
|
run_iptables -I $chain -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
;;
|
|
esac
|
|
|
|
run_iptables -A $chain -j $chain1
|
|
chain=$chain1
|
|
}
|
|
|
|
apply_default()
|
|
{
|
|
#
|
|
# Generate policy file column values from the policy chain
|
|
#
|
|
eval policy=\$${chain1}_policy
|
|
eval loglevel=\$${chain1}_loglevel
|
|
eval synparams=\$${chain1}_synparams
|
|
eval epolicy=\$${chain1}_epolicy
|
|
#
|
|
# Add the appropriate rules to the canonical chain ($chain) to enforce
|
|
# the specified policy
|
|
|
|
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 $epolicy $loglevel
|
|
else
|
|
#
|
|
# The policy chain is different from the canonical chain -- approach
|
|
# depends on the policy
|
|
#
|
|
case $policy in
|
|
ACCEPT|QUEUE)
|
|
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 $epolicy $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 $epolicy $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
|
|
|
|
progress_message " Policy $policy for $1 to $2 using chain $chain"
|
|
}
|
|
|
|
eval chain1=\$${1}2${2}_policychain
|
|
|
|
if [ -n "$chain1" ]; then
|
|
apply_default $1 $2
|
|
else
|
|
fatal_error "No default policy for zone $1 to zone $2"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# 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=
|
|
local policychain=
|
|
|
|
run_user_exit $1
|
|
|
|
eval policychain=\$${2}2${3}_policychain
|
|
|
|
if [ -n "$policychain" ]; then
|
|
eval policy=\$${policychain}_policy
|
|
eval loglevel=\$${policychain}_loglevel
|
|
eval
|
|
|
|
policy_rules $1 $policy NONE $loglevel
|
|
else
|
|
policy_rules $1 DROP NONE INFO
|
|
fi
|
|
}
|
|
|
|
#
|
|
# 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} local policy
|
|
|
|
havechain $chain && { echo $chain; return; }
|
|
|
|
[ "$1" = "$2" ] && { echo ACCEPT; return; }
|
|
|
|
eval chain=\$${chain}_policychain
|
|
|
|
eval policy=\$${chain}_policy
|
|
|
|
if [ "$policy" != CONTINUE ] ; then
|
|
[ -n "$chain" ] && { echo $chain; return; }
|
|
fatal_error "No policy defined for zone $1 to zone $2"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# echo the list of networks routed out of a given interface
|
|
#
|
|
get_routed_networks() # $1 = interface name
|
|
{
|
|
local address
|
|
local rest
|
|
|
|
ip route show dev $1 2> /dev/null |
|
|
while read address rest; do
|
|
if [ "x$address" = xdefault ]; then
|
|
error_message "WARNING: default route ignored on interface $1"
|
|
else
|
|
[ "$address" = "${address%/*}" ] && address="${address}/32"
|
|
echo $address
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# Set up Routing
|
|
#
|
|
setup_routes()
|
|
{
|
|
|
|
run_iptables -t mangle -A PREROUTING -m connmark ! --mark 0 -j CONNMARK --restore-mark
|
|
run_iptables -t mangle -N routemark
|
|
|
|
for interface in $ROUTEMARK_INTERFACES ; do
|
|
|
|
iface=$(chain_base $interface)
|
|
eval mark_value=\$${iface}_routemark
|
|
|
|
run_iptables -t mangle -A PREROUTING -i $interface -m mark --mark 0 -j routemark
|
|
run_iptables -t mangle -A routemark -i $interface -j MARK --set-mark $mark_value
|
|
|
|
done
|
|
|
|
run_iptables -t mangle -A routemark -m mark ! --mark 0 -j CONNMARK --save-mark --mask 255
|
|
|
|
}
|
|
|
|
#
|
|
# Set up Source NAT (including masquerading)
|
|
#
|
|
setup_masq()
|
|
{
|
|
do_ipsec_options() {
|
|
local options="$(separate_list $ipsec)" option
|
|
policy="-m policy --pol ipsec --dir out"
|
|
|
|
for option in $options; do
|
|
case $option in
|
|
[Yy]es) ;;
|
|
strict) policy="$policy --strict" ;;
|
|
next) policy="$policy --next" ;;
|
|
reqid=*) policy="$policy --reqid ${option#*=}" ;;
|
|
spi=*) policy="$policy --spi ${option#*=}" ;;
|
|
proto=*) policy="$policy --proto ${option#*=}" ;;
|
|
mode=*) policy="$policy --mode ${option#*=}" ;;
|
|
tunnel-src=*) policy="$policy --tunnel-src ${option#*=}" ;;
|
|
tunnel-dst=*) policy="$policy --tunnel-dst ${option#*=}" ;;
|
|
reqid!=*) policy="$policy ! --reqid ${option#*=}" ;;
|
|
spi!=*) policy="$policy ! --spi ${option#*=}" ;;
|
|
proto!=*) policy="$policy ! --proto ${option#*=}" ;;
|
|
mode!=*) policy="$policy ! --mode ${option#*=}" ;;
|
|
tunnel-src!=*) policy="$policy ! --tunnel-src ${option#*=}" ;;
|
|
tunnel-dst!=*) policy="$policy ! --tunnel-dst ${option#*=}" ;;
|
|
*) fatal_error "Invalid IPSEC option \"$option\"" ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
setup_one() {
|
|
local add_snat_aliases=$ADD_SNAT_ALIASES pre_nat= policy= destnets=
|
|
|
|
[ "x$ipsec" = x- ] && ipsec=
|
|
|
|
case $ipsec in
|
|
Yes|yes)
|
|
[ -n "$POLICY_MATCH" ] || \
|
|
fatal_error "IPSEC=Yes requires policy match support in your kernel and iptables"
|
|
policy="-m policy --pol ipsec --dir out"
|
|
;;
|
|
No|no)
|
|
[ -n "$POLICY_MATCH" ] || \
|
|
fatal_error "IPSEC=No requires policy match support in your kernel and iptables"
|
|
policy="-m policy --pol none --dir out"
|
|
;;
|
|
*)
|
|
if [ -n "$ipsec" ]; then
|
|
do_ipsec_options
|
|
elif [ -n "$POLICY_MATCH" ]; then
|
|
policy="-m policy --pol none --dir out"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
case $fullinterface in
|
|
+*)
|
|
pre_nat=Yes
|
|
fullinterface=${fullinterface#+}
|
|
;;
|
|
esac
|
|
|
|
case $fullinterface in
|
|
*::*)
|
|
add_snat_aliases=
|
|
destnets="${fullinterface##*:}"
|
|
fullinterface="${fullinterface%:*}"
|
|
;;
|
|
*:*:*)
|
|
# Both alias name and networks
|
|
destnets="${fullinterface##*:}"
|
|
fullinterface="${fullinterface%:*}"
|
|
;;
|
|
*:)
|
|
add_snat_aliases=
|
|
fullinterface=${fullinterface%:}
|
|
;;
|
|
*:*)
|
|
# Alias name OR networks
|
|
case ${fullinterface#*:} in
|
|
*.*)
|
|
# It's a networks
|
|
destnets="${fullinterface#*:}"
|
|
fullinterface="${fullinterface%:*}"
|
|
;;
|
|
*)
|
|
#it's an alias name
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
|
|
interface=${fullinterface%:*}
|
|
|
|
if ! list_search $interface $ALL_INTERFACES; then
|
|
fatal_error "Unknown interface $interface"
|
|
fi
|
|
|
|
if [ "$networks" = "${networks%!*}" ]; then
|
|
nomasq=
|
|
else
|
|
nomasq="${networks#*!}"
|
|
networks="${networks%!*}"
|
|
fi
|
|
|
|
source="${networks:=0.0.0.0/0}"
|
|
|
|
case $source in
|
|
*.*.*|+*|!+*)
|
|
;;
|
|
*)
|
|
networks=$(get_routed_networks $networks)
|
|
[ -z "$networks" ] && fatal_error "Unable to determine the routes through interface \"$source\""
|
|
networks="$networks"
|
|
;;
|
|
esac
|
|
|
|
[ "x$addresses" = x- ] && addresses=
|
|
|
|
if [ -n "$addresses" -a -n "$add_snat_aliases" ]; then
|
|
for address in $(separate_list $addresses); do
|
|
address=${address%:)}
|
|
if [ -n "$address" ]; then
|
|
for addr in $(ip_range_explicit ${address%:*}) ; do
|
|
if ! list_search $addr $aliases_to_add; then
|
|
[ -n "$RETAIN_ALIASES" ] || save_command qt ip addr del $addr dev $interface
|
|
aliases_to_add="$aliases_to_add $addr $fullinterface"
|
|
case $fullinterface in
|
|
*:*)
|
|
fullinterface=${fullinterface%:*}:$((${fullinterface#*:} + 1 ))
|
|
;;
|
|
esac
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
fi
|
|
|
|
[ "x$proto" = x- ] && proto=
|
|
[ "x$ports" = x- ] && ports=
|
|
|
|
if [ -n "$proto" ]; then
|
|
|
|
displayproto="($proto)"
|
|
|
|
case $proto in
|
|
tcp|TCP|udp|UDP|6|17)
|
|
if [ -n "$ports" ]; then
|
|
displayproto="($proto $ports)"
|
|
|
|
listcount=$(list_count $ports)
|
|
|
|
if [ $listcount -gt 1 ]; then
|
|
case $ports in
|
|
*:*)
|
|
if [ -n "$XMULTIPORT" ]; then
|
|
if [ $(($listcount + $(list_count1 $(split $ports) ) )) -le 16 ]; then
|
|
ports="-m multiport --dports $ports"
|
|
else
|
|
fatal_error "More than 15 entries in port list ($ports)"
|
|
fi
|
|
else
|
|
fatal_error "Port Range not allowed in list ($ports)"
|
|
fi
|
|
;;
|
|
*)
|
|
if [ -n "$MULTIPORT" ]; then
|
|
[ $listcount -le 15 ] || fatal_error "More than 15 entries in port list ($ports)"
|
|
ports="-m multiport --dports $ports"
|
|
else
|
|
fatal_error "Port Ranges require multiport match support in your kernel ($ports)"
|
|
fi
|
|
;;
|
|
esac
|
|
else
|
|
ports="--dport $ports"
|
|
fi
|
|
fi
|
|
;;
|
|
*)
|
|
[ -n "$ports" ] && fatal_error "Ports only allowed with UDP or TCP ($ports)"
|
|
;;
|
|
esac
|
|
|
|
proto="-p $proto"
|
|
else
|
|
displayproto="(all)"
|
|
[ -n "$ports" ] && fatal_error "Ports only allowed with UDP or TCP ($ports)"
|
|
fi
|
|
|
|
destination=${destnets:=0.0.0.0/0}
|
|
|
|
[ -z "$pre_nat" ] && chain=$(masq_chain $interface) || chain=$(snat_chain $interface)
|
|
|
|
case $destnets in
|
|
!*)
|
|
newchain=masq${masq_seq}
|
|
createnatchain $newchain
|
|
destnets=${destnets#!}
|
|
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $newchain $(dest_ip_range $destnet) -j RETURN
|
|
done
|
|
|
|
if [ -n "$networks" ]; then
|
|
for s in $networks; do
|
|
addnatrule $chain $(source_ip_range $s) $proto $ports $policy -j $newchain
|
|
done
|
|
networks=
|
|
else
|
|
addnatrule $chain -j $newchain
|
|
fi
|
|
|
|
masq_seq=$(($masq_seq + 1))
|
|
chain=$newchain
|
|
destnets=0.0.0.0/0
|
|
proto=
|
|
ports=
|
|
policy=
|
|
|
|
if [ -n "$nomasq" ]; then
|
|
for addr in $(separate_list $nomasq); do
|
|
addnatrule $chain $(source_ip_range $addr) -j RETURN
|
|
done
|
|
source="$source except $nomasq"
|
|
fi
|
|
;;
|
|
*)
|
|
if [ -n "$nomasq" ]; then
|
|
newchain=masq${masq_seq}
|
|
createnatchain $newchain
|
|
|
|
if [ -n "$networks" ]; then
|
|
for s in $networks; do
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $chain $(both_ip_ranges $s $destnet) $proto $ports $policy -j $newchain
|
|
done
|
|
done
|
|
else
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $chain $(dest_ip_range $destnet) $proto $ports $policy -j $newchain
|
|
done
|
|
fi
|
|
|
|
masq_seq=$(($masq_seq + 1))
|
|
chain=$newchain
|
|
networks=
|
|
destnets=0.0.0.0/0
|
|
proto=
|
|
ports=
|
|
policy=
|
|
|
|
for addr in $(separate_list $nomasq); do
|
|
addnatrule $chain $(source_ip_range $addr) -j RETURN
|
|
done
|
|
|
|
source="$source except $nomasq"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
addrlist=
|
|
target=MASQUERADE
|
|
|
|
if [ -n "$addresses" ]; then
|
|
case "$addresses" in
|
|
SAME:nodst:*)
|
|
target="SAME --nodst"
|
|
addresses=${addresses#SAME:nodst:}
|
|
for address in $(separate_list $addresses); do
|
|
addrlist="$addrlist --to $address";
|
|
done
|
|
;;
|
|
SAME:*)
|
|
target="SAME"
|
|
addresses=${addresses#SAME:}
|
|
for address in $(separate_list $addresses); do
|
|
addrlist="$addrlist --to $address";
|
|
done
|
|
;;
|
|
*)
|
|
for address in $(separate_list $addresses); do
|
|
case $address in
|
|
*.*.*.*)
|
|
target=SNAT
|
|
addrlist="$addrlist --to-source $address"
|
|
;;
|
|
*)
|
|
addrlist="$addrlist --to-ports ${address#:}"
|
|
;;
|
|
esac
|
|
done
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
if [ -n "$networks" ]; then
|
|
for network in $networks; do
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $chain $(both_ip_ranges $network $destnet) $proto $ports $policy -j $target $addrlist
|
|
done
|
|
|
|
if [ -n "$addresses" ]; then
|
|
progress_message " To $destination $displayproto from $network through ${interface} using $addresses"
|
|
else
|
|
progress_message " To $destination $displayproto from $network through ${interface}"
|
|
fi
|
|
done
|
|
else
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $chain $(dest_ip_range $destnet) $proto $ports $policy -j $target $addrlist
|
|
done
|
|
|
|
if [ -n "$addresses" ]; then
|
|
progress_message " To $destination $displayproto from $source through ${interface} using $addresses"
|
|
else
|
|
progress_message " To $destination $displayproto from $source through ${interface}"
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
strip_file masq $1
|
|
|
|
if [ -n "$NAT_ENABLED" ]; then
|
|
echo "Masqueraded Networks and Hosts:"
|
|
[ -n "$RETAIN_ALIASES" ] || save_progress_message "Restoring Masquerading/SNAT..."
|
|
fi
|
|
|
|
while read fullinterface networks addresses proto ports ipsec; do
|
|
expandv fullinterface networks addresses proto ports ipsec
|
|
[ -n "$NAT_ENABLED" ] && setup_one || \
|
|
error_message "WARNING: NAT disabled; masq rule ignored"
|
|
done < $TMP_DIR/masq
|
|
}
|
|
|
|
#
|
|
# Add a record to the blacklst chain
|
|
#
|
|
# $source = address match
|
|
# $proto = protocol selector
|
|
# $dport = destination port selector
|
|
#
|
|
add_blacklist_rule() {
|
|
if [ "$COMMAND" != check ]; then
|
|
if [ -n "$BLACKLIST_LOGLEVEL" ]; then
|
|
log_rule $BLACKLIST_LOGLEVEL blacklst $BLACKLIST_DISPOSITION $(fix_bang $source $proto $dport)
|
|
fi
|
|
|
|
run_iptables2 -A blacklst $source $proto $dport -j $disposition
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Process a record from the blacklist file
|
|
#
|
|
# $networks = address/networks
|
|
# $protocol = Protocol Number/Name
|
|
# $port = Port Number/Name
|
|
#
|
|
process_blacklist_rec() {
|
|
local source
|
|
local addr
|
|
local proto
|
|
local dport
|
|
local temp
|
|
local setname
|
|
|
|
for addr in $(separate_list $networks); do
|
|
case $addr in
|
|
~*)
|
|
addr=$(echo $addr | sed 's/~//;s/-/:/g')
|
|
source="--match mac --mac-source $addr"
|
|
;;
|
|
*)
|
|
source="$(source_ip_range $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
|
|
|
|
if [ "$COMMAND" = check ]; then
|
|
progress_message " $addr" Verified
|
|
else
|
|
progress_message " $addr added to Black List"
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# Setup the Black List
|
|
#
|
|
setup_blacklist() {
|
|
local hosts="$(find_hosts_by_option blacklist)"
|
|
local f=$(find_file blacklist)
|
|
local disposition=$BLACKLIST_DISPOSITION
|
|
local ipsec policy
|
|
|
|
if [ -n "$hosts" -a -f $f ]; then
|
|
echo "Setting up Blacklisting..."
|
|
|
|
strip_file blacklist $f
|
|
|
|
createchain blacklst no
|
|
|
|
[ -n "$BLACKLISTNEWONLY" ] && state="-m state --state NEW,INVALID" || state=
|
|
|
|
for host in $hosts; do
|
|
ipsec=${host%^*}
|
|
host=${host#*^}
|
|
[ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
|
|
interface=${host%%:*}
|
|
network=${host#*:}
|
|
|
|
for chain in $(first_chains $interface); do
|
|
run_iptables -A $chain $state $(match_source_hosts $network) $policy -j blacklst
|
|
done
|
|
|
|
[ $network = 0/0.0.0.0 ] && network= || network=":$network"
|
|
|
|
progress_message " Blacklisting enabled on ${interface}${network}"
|
|
done
|
|
|
|
[ "$disposition" = REJECT ] && disposition=reject
|
|
|
|
if [ -z "$DELAYBLACKLISTLOAD" ]; then
|
|
while read networks protocol ports; do
|
|
expandv networks protocol ports
|
|
process_blacklist_rec
|
|
done < $TMP_DIR/blacklist
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Refresh the Black List
|
|
#
|
|
refresh_blacklist() {
|
|
local f=$(find_file blacklist)
|
|
local disposition=$BLACKLIST_DISPOSITION
|
|
|
|
if qt $IPTABLES -L blacklst -n ; then
|
|
echo "Loading Black List..."
|
|
|
|
strip_file blacklist $f
|
|
|
|
[ "$disposition" = REJECT ] && disposition=reject
|
|
|
|
run_iptables -F blacklst
|
|
|
|
while read networks protocol ports; do
|
|
expandv networks protocol ports
|
|
process_blacklist_rec
|
|
done < $TMP_DIR/blacklist
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Verify the Black List
|
|
#
|
|
validate_blacklist() {
|
|
local f=$(find_file blacklist)
|
|
local disposition=$BLACKLIST_DISPOSITION
|
|
|
|
echo "Checking Black List..."
|
|
|
|
strip_file blacklist $f
|
|
|
|
[ "$disposition" = REJECT ] && disposition=reject
|
|
|
|
while read networks protocol ports; do
|
|
expandv networks protocol ports
|
|
process_blacklist_rec
|
|
done < $TMP_DIR/blacklist
|
|
}
|
|
|
|
#
|
|
# Verify that kernel has netfilter support
|
|
#
|
|
verify_os_version() {
|
|
|
|
osversion=$(uname -r)
|
|
|
|
case $osversion in
|
|
2.4.*|2.5.*|2.6.*)
|
|
;;
|
|
*)
|
|
startup_error "Shorewall version $version does not work with kernel version $osversion"
|
|
;;
|
|
esac
|
|
|
|
[ $COMMAND = start -a -n "$(lsmod 2> /dev/null | grep '^ipchains')" ] && \
|
|
startup_error "Shorewall can't start with the ipchains kernel module loaded - see FAQ #8"
|
|
}
|
|
|
|
#
|
|
# Add IP Aliases
|
|
#
|
|
add_ip_aliases()
|
|
{
|
|
local addresses external interface inet cidr rest val
|
|
|
|
address_details()
|
|
{
|
|
#
|
|
# 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 an existing address in the same networks
|
|
#
|
|
# Get all of the lines that contain inet addresses with broadcast
|
|
#
|
|
ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | while read inet cidr rest ; do
|
|
case $cidr in
|
|
*/*)
|
|
if in_network $external $cidr; then
|
|
echo "/${cidr#*/} brd $(broadcastaddress $cidr)"
|
|
break
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
do_one()
|
|
{
|
|
val=$(address_details)
|
|
|
|
if [ -n "$RETAIN_ALIASES" ]; then
|
|
run_ip addr add ${external}${val} dev $interface $label
|
|
save_command qt ip addr add ${external}${val} dev $interface $label
|
|
else
|
|
ensure_and_save_command ip addr add ${external}${val} dev $interface $label
|
|
fi
|
|
|
|
echo "$external $interface" >> /var/lib/shorewall/nat
|
|
[ -n "$label" ] && label="with $label"
|
|
progress_message " IP Address $external added to interface $interface $label"
|
|
}
|
|
|
|
set -- $aliases_to_add
|
|
|
|
save_progress_message "Restoring IP Addresses..."
|
|
|
|
while [ $# -gt 0 ]; do
|
|
external=$1
|
|
interface=$2
|
|
label=
|
|
|
|
if [ "$interface" != "${interface%:*}" ]; then
|
|
label="${interface#*:}"
|
|
interface="${interface%:*}"
|
|
label="label $interface:$label"
|
|
fi
|
|
|
|
shift;shift
|
|
|
|
list_search $external $(find_interface_addresses $interface) || do_one
|
|
done
|
|
}
|
|
|
|
#
|
|
# Load kernel modules required for Shorewall
|
|
#
|
|
load_kernel_modules()
|
|
{
|
|
save_modules_dir=$MODULESDIR
|
|
|
|
[ -z "$MODULESDIR" ] && \
|
|
MODULESDIR=/lib/modules/$(uname -r)/kernel/net/ipv4/netfilter
|
|
|
|
modules=$(find_file modules)
|
|
|
|
if [ -f $modules -a -d $MODULESDIR ]; then
|
|
progress_message "Loading Modules..."
|
|
. $modules
|
|
fi
|
|
|
|
MODULESDIR=$save_modules_dir
|
|
}
|
|
|
|
save_load_kernel_modules()
|
|
{
|
|
|
|
modules=$(find_file modules)
|
|
|
|
save_progress_message "Loading kernel modules..."
|
|
save_command "reload_kernel_modules <<__EOF__"
|
|
|
|
while read command; do
|
|
case "$command" in
|
|
loadmodule*)
|
|
save_command $command
|
|
;;
|
|
esac
|
|
done < $modules
|
|
|
|
save_command __EOF__
|
|
save_command ""
|
|
|
|
}
|
|
|
|
# Verify that the 'ip' program is installed
|
|
|
|
verify_ip() {
|
|
qt ip link ls ||\
|
|
startup_error "Shorewall $version requires the iproute package ('ip' utility)"
|
|
}
|
|
|
|
#
|
|
# Determine which optional facilities are supported by iptables/netfilter
|
|
#
|
|
determine_capabilities() {
|
|
qt $IPTABLES -t nat -L -n && NAT_ENABLED=Yes || NAT_ENABLED=
|
|
qt $IPTABLES -t mangle -L -n && MANGLE_ENABLED=Yes || MANGLE_ENABLED=
|
|
|
|
CONNTRACK_MATCH=
|
|
MULTIPORT=
|
|
XMULTIPORT=
|
|
POLICY_MATCH=
|
|
PHYSDEV_MATCH=
|
|
IPRANGE_MATCH=
|
|
RECENT_MATCH=
|
|
OWNER_MATCH=
|
|
IPSET_MATCH=
|
|
ROUTE_TARGET=
|
|
XMARK=
|
|
CONNMARK=
|
|
CONNMARK_MATCH=
|
|
|
|
qt $IPTABLES -N fooX1234
|
|
qt $IPTABLES -A fooX1234 -m conntrack --ctorigdst 192.168.1.1 -j ACCEPT && CONNTRACK_MATCH=Yes
|
|
qt $IPTABLES -A fooX1234 -p tcp -m multiport --dports 21,22 -j ACCEPT && MULTIPORT=Yes
|
|
qt $IPTABLES -A fooX1234 -p tcp -m multiport --dports 21:22 -j ACCEPT && XMULTIPORT=Yes
|
|
qt $IPTABLES -A fooX1234 -m policy --pol ipsec --dir in -j ACCEPT && POLICY_MATCH=Yes
|
|
qt $IPTABLES -A fooX1234 -m physdev --physdev-in eth0 -j ACCEPT && PHYSDEV_MATCH=Yes
|
|
qt $IPTABLES -A fooX1234 -m iprange --src-range 192.168.1.5-192.168.1.124 -j ACCEPT && IPRANGE_MATCH=Yes
|
|
qt $IPTABLES -A fooX1234 -m recent --update -j ACCEPT && RECENT_MATCH=Yes
|
|
qt $IPTABLES -A fooX1234 -m owner --cmd-owner foo -j ACCEPT && OWNER_MATCH=Yes
|
|
qt $IPTABLES -A fooX1234 -m connmark --mark 2 -j ACCEPT && CONNMARK_MATCH=Yes
|
|
|
|
qt $IPTABLES -t mangle -N fooX1234
|
|
qt $IPTABLES -t mangle -A fooX1234 -j ROUTE --oif eth0 && ROUTE_TARGET=Yes
|
|
qt $IPTABLES -t mangle -A fooX1234 -j MARK --or-mark 2 && XMARK=Yes
|
|
qt $IPTABLES -t mangle -A fooX1234 -j CONNMARK --save-mark && CONNMARK=Yes
|
|
qt $IPTABLES -t mangle -F fooX1234
|
|
qt $IPTABLES -t mangle -X fooX1234
|
|
|
|
|
|
if qt which ipset; then
|
|
qt ipset -X fooX1234 # Just in case something went wrong the last time
|
|
|
|
if qt ipset -N fooX1234 iphash ; then
|
|
if qt $IPTABLES -A fooX1234 -m set --set fooX1234 src -j ACCEPT; then
|
|
qt $IPTABLES -D fooX1234 -m set --set fooX1234 src -j ACCEPT
|
|
IPSET_MATCH=Yes
|
|
fi
|
|
qt ipset -X fooX1234
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$PKTTYPE" ]; then
|
|
qt $IPTABLES -A fooX1234 -m pkttype --pkt-type broadcast -j ACCEPT || PKTTYPE=
|
|
fi
|
|
|
|
qt $IPTABLES -F fooX1234
|
|
qt $IPTABLES -X fooX1234
|
|
}
|
|
|
|
report_capability() # $1 = Capability Description , $2 Capability Setting (if any)
|
|
{
|
|
local setting=
|
|
|
|
[ "x$2" = "xYes" ] && setting="Available" || setting="Not available"
|
|
|
|
echo " " $1: $setting
|
|
}
|
|
|
|
report_capabilities() {
|
|
echo "Shorewall has detected the following iptables/netfilter capabilities:"
|
|
report_capability "NAT" $NAT_ENABLED
|
|
report_capability "Packet Mangling" $MANGLE_ENABLED
|
|
report_capability "Multi-port Match" $MULTIPORT
|
|
[ -n "$MULTIPORT" ] && report_capability "Extended Multi-port Match" $XMULTIPORT
|
|
report_capability "Connection Tracking Match" $CONNTRACK_MATCH
|
|
report_capability "Packet Type Match" $PKTTYPE
|
|
report_capability "Policy Match" $POLICY_MATCH
|
|
report_capability "Physdev Match" $PHYSDEV_MATCH
|
|
report_capability "IP range Match" $IPRANGE_MATCH
|
|
report_capability "Recent Match" $RECENT_MATCH
|
|
report_capability "Owner Match" $OWNER_MATCH
|
|
report_capability "Ipset Match" $IPSET_MATCH
|
|
report_capability "ROUTE Target" $ROUTE_TARGET
|
|
report_capability "Extended MARK Target" $XMARK
|
|
report_capability "CONNMARK Target" $CONNMARK
|
|
report_capability "Connmark Match" $CONNMARK_MATCH
|
|
}
|
|
|
|
#
|
|
# 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 () {
|
|
|
|
report_capabilities
|
|
|
|
if [ -n "$BRIDGING" ]; then
|
|
[ -n "$PHYSDEV_MATCH" ] || startup_error "BRIDGING=Yes requires Physdev Match support in your Kernel and iptables"
|
|
fi
|
|
|
|
[ "$MACLIST_TTL" = "0" ] && MACLIST_TTL=
|
|
|
|
if [ -n "$MACLIST_TTL" -a -z "$RECENT_MATCH" ]; then
|
|
startup_error "MACLIST_TTL requires the Recent Match capability which is not present in your Kernel and/or iptables"
|
|
fi
|
|
|
|
[ -n "$RFC1918_STRICT" -a -z "$CONNTRACK_MATCH" ] && \
|
|
startup_error "RFC1918_STRICT=Yes requires Connection Tracking match"
|
|
|
|
echo "Determining Zones..."
|
|
|
|
determine_zones
|
|
|
|
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
|
|
|
|
run_user_exit init
|
|
|
|
#
|
|
# Some files might be large so strip them while the firewall is still running
|
|
# (restart command). This reduces the length of time that the firewall isn't
|
|
# accepting new connections.
|
|
#
|
|
|
|
strip_file rules
|
|
strip_file proxyarp
|
|
strip_file maclist
|
|
strip_file nat
|
|
strip_file netmap
|
|
|
|
echo "Pre-processing Actions..."
|
|
process_actions1
|
|
|
|
terminator=fatal_error
|
|
|
|
deletechain shorewall
|
|
|
|
[ -n "$NAT_ENABLED" ] && delete_nat
|
|
|
|
delete_proxy_arp
|
|
|
|
[ -n "$MANGLE_ENABLED" ] && \
|
|
run_iptables -t mangle -F && \
|
|
run_iptables -t mangle -X
|
|
|
|
[ -n "$CLEAR_TC" ] && delete_tc
|
|
|
|
echo "Deleting user chains..."
|
|
|
|
exists_INPUT=Yes
|
|
exists_OUTPUT=Yes
|
|
exists_FORWARD=Yes
|
|
|
|
process_criticalhosts
|
|
|
|
if [ -n "$CRITICALHOSTS" ]; then
|
|
|
|
setpolicy INPUT ACCEPT
|
|
setpolicy OUTPUT ACCEPT
|
|
setpolicy FORWARD DROP
|
|
|
|
deleteallchains
|
|
|
|
enable_critical_hosts
|
|
|
|
setpolicy INPUT DROP
|
|
setpolicy OUTPUT DROP
|
|
|
|
setcontinue FORWARD
|
|
setcontinue INPUT
|
|
setcontinue OUTPUT
|
|
else
|
|
|
|
setpolicy INPUT DROP
|
|
setpolicy OUTPUT DROP
|
|
setpolicy FORWARD DROP
|
|
|
|
deleteallchains
|
|
|
|
setcontinue FORWARD
|
|
setcontinue INPUT
|
|
setcontinue OUTPUT
|
|
fi
|
|
|
|
f=$(find_file ipsets)
|
|
|
|
if [ -f $f ]; then
|
|
echo "Processing $f ..."
|
|
ipset -U :all: :all:
|
|
run_ipset -F
|
|
run_ipset -X
|
|
run_ipset -R < $f
|
|
fi
|
|
|
|
run_user_exit continue
|
|
|
|
f=$(find_file routestopped)
|
|
|
|
echo "Processing $f ..."
|
|
|
|
strip_file routestopped $f
|
|
|
|
process_routestopped -A
|
|
|
|
[ -n "$DISABLE_IPV6" ] && disable_ipv6
|
|
|
|
#
|
|
# Enable the Loopback interface for now
|
|
#
|
|
run_iptables -A INPUT -i lo -j ACCEPT
|
|
run_iptables -A OUTPUT -o lo -j ACCEPT
|
|
|
|
|
|
#
|
|
# Allow DNS lookups during startup for FQDNs
|
|
#
|
|
|
|
for chain in INPUT OUTPUT FORWARD; do
|
|
run_iptables -A $chain -p udp --dport 53 -j ACCEPT
|
|
done
|
|
|
|
if [ -n "$CLAMPMSS" ]; then
|
|
case $CLAMPMSS in
|
|
Yes)
|
|
option="--clamp-mss-to-pmtu"
|
|
;;
|
|
*)
|
|
option="--set-mss $CLAMPMSS"
|
|
;;
|
|
esac
|
|
|
|
run_iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS $option
|
|
fi
|
|
|
|
accounting_file=$(find_file accounting)
|
|
|
|
[ -f $accounting_file ] && setup_accounting $accounting_file
|
|
|
|
if [ -z "$NEWNOTSYN" ]; then
|
|
createchain newnotsyn no
|
|
|
|
for host in $(find_hosts_by_option newnotsyn); do
|
|
ipsec=${host%^*}
|
|
host=${host#*^}
|
|
[ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
|
|
interface=${host%%:*}
|
|
network=${host#*:}
|
|
run_iptables -A newnotsyn -i $interface $(match_source_hosts $network) $policy -p tcp --tcp-flags ACK ACK -j ACCEPT
|
|
run_iptables -A newnotsyn -i $interface $(match_source_hosts $network) $policy -p tcp --tcp-flags RST RST -j ACCEPT
|
|
run_iptables -A newnotsyn -i $interface $(match_source_hosts $network) $policy -p tcp --tcp-flags FIN FIN -j ACCEPT
|
|
run_iptables -A newnotsyn -i $interface $(match_source_hosts $network) $policy -j RETURN
|
|
done
|
|
|
|
run_user_exit newnotsyn
|
|
|
|
if [ -n "$LOGNEWNOTSYN" ]; then
|
|
log_rule $LOGNEWNOTSYN newnotsyn DROP
|
|
fi
|
|
|
|
run_iptables -A newnotsyn -j DROP
|
|
fi
|
|
|
|
createchain reject no
|
|
createchain dynamic no
|
|
createchain smurfs no
|
|
|
|
if [ -f /var/lib/shorewall/save ]; then
|
|
echo "Restoring dynamic rules..."
|
|
|
|
if [ -f /var/lib/shorewall/save ]; then
|
|
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
|
|
fi
|
|
|
|
[ -n "$BLACKLISTNEWONLY" ] && state="-m state --state NEW,INVALID" || state=
|
|
|
|
echo "Creating Interface Chains..."
|
|
|
|
for interface in $ALL_INTERFACES; do
|
|
createchain $(forward_chain $interface) no
|
|
run_iptables -A $(forward_chain $interface) $state -j dynamic
|
|
createchain $(input_chain $interface) no
|
|
run_iptables -A $(input_chain $interface) $state -j dynamic
|
|
done
|
|
}
|
|
|
|
#
|
|
# Construct zone-independent rules
|
|
#
|
|
add_common_rules() {
|
|
local savelogparms="$LOGPARMS"
|
|
local broadcasts="$(find_broadcasts) 255.255.255.255 224.0.0.0/4"
|
|
|
|
drop_broadcasts() {
|
|
for address in $broadcasts ; do
|
|
run_iptables -A reject -d $address -j DROP
|
|
done
|
|
}
|
|
|
|
#
|
|
# Populate the smurf chain
|
|
#
|
|
for address in $broadcasts ; do
|
|
[ -n "$SMURF_LOG_LEVEL" ] && log_rule $SMURF_LOG_LEVEL smurfs DROP -s $address
|
|
run_iptables -A smurfs $(source_ip_range $address) -j DROP
|
|
done
|
|
#
|
|
# Reject Rules -- Don't respond to broadcasts with an ICMP
|
|
#
|
|
if [ -n "$PKTTYPE" ]; then
|
|
qt $IPTABLES -A reject -m pkttype --pkt-type broadcast -j DROP
|
|
if ! qt $IPTABLES -A reject -m pkttype --pkt-type multicast -j DROP; then
|
|
#
|
|
# No pkttype support -- do it the hard way
|
|
#
|
|
drop_broadcasts
|
|
fi
|
|
else
|
|
drop_broadcasts
|
|
fi
|
|
#
|
|
# Don't feed the smurfs
|
|
#
|
|
for address in $broadcasts ; do
|
|
run_iptables -A reject -s $address -j DROP
|
|
done
|
|
|
|
run_iptables -A reject -p tcp -j REJECT --reject-with tcp-reset
|
|
run_iptables -A reject -p udp -j REJECT
|
|
#
|
|
# Not all versions of iptables support these so don't complain if they don't work
|
|
#
|
|
qt $IPTABLES -A reject -p icmp -j REJECT --reject-with icmp-host-unreachable
|
|
if ! qt $IPTABLES -A reject -j REJECT --reject-with icmp-host-prohibited; then
|
|
#
|
|
# In case the above doesn't work
|
|
#
|
|
run_iptables -A reject -j REJECT
|
|
fi
|
|
|
|
#
|
|
# Create common action chains
|
|
#
|
|
for action in $USEDACTIONS; do
|
|
createactionchain $action
|
|
done
|
|
|
|
run_user_exit initdone
|
|
|
|
#
|
|
# Process Black List
|
|
#
|
|
setup_blacklist
|
|
|
|
#
|
|
# SMURFS
|
|
#
|
|
hosts=$(find_hosts_by_option nosmurfs)
|
|
|
|
if [ -n "$hosts" ]; then
|
|
|
|
echo "Adding Anti-smurf Rules"
|
|
|
|
for host in $hosts; do
|
|
ipsec=${host%^*}
|
|
host=${host#*^}
|
|
[ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
|
|
interface=${host%%:*}
|
|
network=${host#*:}
|
|
|
|
for chain in $(first_chains $interface); do
|
|
run_iptables -A $chain -m state --state NEW,INVALID $(match_source_hosts $network) $policy -j smurfs
|
|
done
|
|
done
|
|
fi
|
|
#
|
|
# DHCP
|
|
#
|
|
interfaces=$(find_interfaces_by_option dhcp)
|
|
|
|
if [ -n "$interfaces" ]; then
|
|
|
|
echo "Adding rules for DHCP"
|
|
|
|
for interface in $interfaces; do
|
|
if [ -n "$BRIDGING" ]; then
|
|
is_bridge=$( brctl show $interface 2> /dev/null | grep ^$interface[[:space:]] )
|
|
[ -n "$is_bridge" ] && \
|
|
$IPTABLES -A $(forward_chain $interface) -p udp -o $interface --dport 67:68 -j ACCEPT
|
|
fi
|
|
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
|
|
fi
|
|
#
|
|
# RFC 1918
|
|
#
|
|
hosts="$(find_hosts_by_option norfc1918)"
|
|
|
|
if [ -n "$hosts" ]; then
|
|
echo "Enabling RFC1918 Filtering"
|
|
|
|
strip_file rfc1918
|
|
|
|
createchain norfc1918 no
|
|
|
|
createchain rfc1918 no
|
|
|
|
log_rule $RFC1918_LOG_LEVEL rfc1918 DROP
|
|
|
|
run_iptables -A rfc1918 -j DROP
|
|
|
|
chain=norfc1918
|
|
|
|
if [ -n "$RFC1918_STRICT" ]; then
|
|
#
|
|
# We'll generate two chains - one for source and one for destination
|
|
#
|
|
chain=rfc1918d
|
|
createchain $chain no
|
|
elif [ -n "$MANGLE_ENABLED" -a -z "$CONNTRACK_MATCH" ]; then
|
|
#
|
|
# Mangling is enabled but conntrack match isn't available --
|
|
# 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 rfc1918
|
|
log_rule $RFC1918_LOG_LEVEL rfc1918 DROP -t mangle
|
|
run_iptables -t mangle -A rfc1918 -j DROP
|
|
fi
|
|
|
|
while read networks target; do
|
|
case $target in
|
|
logdrop)
|
|
target=rfc1918
|
|
s_target=rfc1918
|
|
;;
|
|
DROP)
|
|
s_target=DROP
|
|
;;
|
|
RETURN)
|
|
[ -n "$RFC1918_STRICT" ] && s_target=rfc1918d || s_target=RETURN
|
|
;;
|
|
*)
|
|
fatal_error "Invalid target ($target) for $networks"
|
|
;;
|
|
esac
|
|
|
|
for network in $(separate_list $networks); do
|
|
run_iptables2 -A norfc1918 $(source_ip_range $network) -j $s_target
|
|
|
|
if [ -n "$CONNTRACK_MATCH" ]; then
|
|
#
|
|
# We have connection tracking match -- match on the original destination
|
|
#
|
|
run_iptables2 -A $chain -m conntrack --ctorigdst $network -j $target
|
|
elif [ -n "$MANGLE_ENABLED" ]; then
|
|
#
|
|
# No connection tracking match but we have mangling -- add a rule to
|
|
# the mangle table
|
|
#
|
|
run_iptables2 -t mangle -A man1918 $(dest_ip_range $network) -j $target
|
|
fi
|
|
done
|
|
done < $TMP_DIR/rfc1918
|
|
|
|
[ -n "$RFC1918_STRICT" ] && run_iptables -A norfc1918 -j rfc1918d
|
|
|
|
for host in $hosts; do
|
|
ipsec=${host%^*}
|
|
host=${host#*^}
|
|
[ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
|
|
interface=${host%%:*}
|
|
networks=${host#*:}
|
|
|
|
for chain in $(first_chains $interface); do
|
|
run_iptables -A $chain -m state --state NEW $(match_source_hosts $networks) $policy -j norfc1918
|
|
done
|
|
|
|
[ -n "$MANGLE_ENABLED" -a -z "$CONNTRACK_MATCH" ] && \
|
|
run_iptables -t mangle -A PREROUTING -m state --state NEW -i $interface $(match_source_hosts $networks) -j man1918
|
|
done
|
|
fi
|
|
|
|
hosts=$(find_hosts_by_option tcpflags)
|
|
|
|
if [ -n "$hosts" ]; then
|
|
echo "Setting up TCP Flags checking..."
|
|
|
|
createchain tcpflags no
|
|
|
|
if [ -n "$TCP_FLAGS_LOG_LEVEL" ]; then
|
|
createchain logflags no
|
|
|
|
savelogparms="$LOGPARMS"
|
|
|
|
[ "$TCP_FLAGS_LOG_LEVEL" = ULOG ] || LOGPARMS="$LOGPARMS --log-ip-options"
|
|
|
|
log_rule $TCP_FLAGS_LOG_LEVEL logflags $TCP_FLAGS_DISPOSITION
|
|
|
|
LOGPARMS="$savelogparms"
|
|
|
|
case $TCP_FLAGS_DISPOSITION in
|
|
REJECT)
|
|
run_iptables -A logflags -j REJECT --reject-with tcp-reset
|
|
;;
|
|
*)
|
|
run_iptables -A logflags -j $TCP_FLAGS_DISPOSITION
|
|
;;
|
|
esac
|
|
|
|
disposition="-j logflags"
|
|
else
|
|
disposition="-j $TCP_FLAGS_DISPOSITION"
|
|
fi
|
|
|
|
run_iptables -A tcpflags -p tcp --tcp-flags ALL FIN,URG,PSH $disposition
|
|
run_iptables -A tcpflags -p tcp --tcp-flags ALL NONE $disposition
|
|
run_iptables -A tcpflags -p tcp --tcp-flags SYN,RST SYN,RST $disposition
|
|
run_iptables -A tcpflags -p tcp --tcp-flags SYN,FIN SYN,FIN $disposition
|
|
#
|
|
# There are a lot of probes to ports 80, 3128 and 8080 that use a source
|
|
# port of 0. This catches them even if they are directed at an IP that
|
|
# hosts a web server.
|
|
#
|
|
run_iptables -A tcpflags -p tcp --syn --sport 0 $disposition
|
|
|
|
for host in $hosts; do
|
|
ipsec=${host%^*}
|
|
host=${host#*^}
|
|
[ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy=
|
|
interface=${host%%:*}
|
|
network=${host#*:}
|
|
|
|
for chain in $(first_chains $interface); do
|
|
run_iptables -A $chain -p tcp $(match_source_hosts $network) $policy -j tcpflags
|
|
done
|
|
done
|
|
fi
|
|
#
|
|
# ARP Filtering
|
|
#
|
|
save_progress_message "Restoring ARP filtering..."
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
run_and_save_command "[ -f $f/arp_filter ] && echo 0 > $f/arp_filter"
|
|
run_and_save_command "[ -f $f/arp_filter ] && echo 0 > $f/arp_ignore"
|
|
done
|
|
|
|
interfaces=$(find_interfaces_by_option arp_filter)
|
|
interfaces1=$(find_interfaces_by_option1 arp_ignore)
|
|
|
|
if [ -n "${interfaces}${interfaces1}" ]; then
|
|
echo "Setting up ARP Filtering..."
|
|
|
|
for interface in $interfaces; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/arp_filter
|
|
if [ -f $file ]; then
|
|
run_and_save_command "echo 1 > $file"
|
|
else
|
|
error_message \
|
|
"WARNING: Cannot set ARP filtering on $interface"
|
|
fi
|
|
done
|
|
|
|
for interface in $interfaces1; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/arp_ignore
|
|
if [ -f $file ]; then
|
|
eval command="\"echo \$$(chain_base $interface)_arp_ignore > $file\""
|
|
run_and_save_command "$command"
|
|
else
|
|
error_message \
|
|
"WARNING: Cannot set ARP filtering on $interface"
|
|
fi
|
|
done
|
|
fi
|
|
#
|
|
# Route Filtering
|
|
#
|
|
interfaces="$(find_interfaces_by_option routefilter)"
|
|
|
|
if [ -n "$interfaces" -o -n "$ROUTE_FILTER" ]; then
|
|
echo "Setting up Kernel Route Filtering..."
|
|
|
|
save_progress_message "Restoring Route Filtering..."
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
run_and_save_command "[ -f $f/rp_filter ] && echo 0 > $f/rp_filter"
|
|
done
|
|
|
|
for interface in $interfaces; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/rp_filter
|
|
if [ -f $file ]; then
|
|
run_and_save_command "echo 1 > $file"
|
|
else
|
|
error_message \
|
|
"WARNING: Cannot set route filtering on $interface"
|
|
fi
|
|
done
|
|
|
|
run_and_save_command "echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter"
|
|
|
|
if [ -n "$ROUTE_FILTER" ]; then
|
|
run_and_save_command "echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter"
|
|
run_and_save_command "echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter"
|
|
fi
|
|
|
|
run_and_save_command ip route flush cache
|
|
fi
|
|
|
|
#
|
|
# Martian Logging
|
|
#
|
|
interfaces="$(find_interfaces_by_option logmartians)"
|
|
|
|
if [ -n "$interfaces" -o -n "$LOG_MARTIANS" ]; then
|
|
echo "Setting up Martian Logging..."
|
|
|
|
save_progress_message "Restoring Martian Logging..."
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
run_and_save_command "[ -f $f/log_martians ] && echo 0 > $f/log_martians"
|
|
done
|
|
|
|
for interface in $interfaces; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/log_martians
|
|
if [ -f $file ]; then
|
|
run_and_save_command "echo 1 > $file"
|
|
else
|
|
error_message \
|
|
"WARNING: Cannot set Martian logging on $interface"
|
|
fi
|
|
done
|
|
|
|
if [ -n "$LOG_MARTIANS" ]; then
|
|
run_and_save_command "echo 1 > /proc/sys/net/ipv4/conf/default/log_martians"
|
|
run_and_save_command "echo 1 > /proc/sys/net/ipv4/conf/all/log_martians"
|
|
fi
|
|
|
|
fi
|
|
|
|
#
|
|
# Source Routing
|
|
#
|
|
save_progress_message "Restoring Accept Source Routing..."
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
run_and_save_command "[ -f $f/accept_source_route ] && echo 0 > $f/accept_source_route"
|
|
done
|
|
|
|
interfaces=$(find_interfaces_by_option sourceroute)
|
|
|
|
if [ -n "$interfaces" ]; then
|
|
echo "Setting up Accept Source Routing..."
|
|
|
|
for interface in $interfaces; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/accept_source_route
|
|
if [ -f $file ]; then
|
|
run_and_save_command "echo 1 > $file"
|
|
else
|
|
error_message \
|
|
"WARNING: Cannot set Accept Source Routing on $interface"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ -n "$DYNAMIC_ZONES" ]; then
|
|
echo "Setting up Dynamic Zone Chains..."
|
|
|
|
for interface in $ALL_INTERFACES; do
|
|
for chain in $(dynamic_chains $interface); do
|
|
createchain $chain no
|
|
done
|
|
|
|
chain=$(dynamic_in $interface)
|
|
createnatchain $chain
|
|
|
|
run_iptables -A $(input_chain $interface) -j $chain
|
|
run_iptables -A $(forward_chain $interface) -j $(dynamic_fwd $interface)
|
|
run_iptables -A OUTPUT -o $interface -j $(dynamic_out $interface)
|
|
done
|
|
fi
|
|
#
|
|
# UPnP
|
|
#
|
|
interfaces=$(find_interfaces_by_option upnp)
|
|
|
|
if [ -n "$interfaces" ]; then
|
|
echo "Setting up UPnP..."
|
|
|
|
createnatchain UPnP
|
|
|
|
for interface in $interfaces; do
|
|
run_iptables -t nat -A PREROUTING -i $interface -j UPnP
|
|
done
|
|
fi
|
|
|
|
setup_forwarding
|
|
}
|
|
|
|
#
|
|
# 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
|
|
#
|
|
for chain in $all_policy_chains; do
|
|
eval policy=\$${chain}_policy
|
|
eval loglevel=\$${chain}_loglevel
|
|
eval synparams=\$${chain}_synparams
|
|
eval epolicy=\$${chain}_epolicy
|
|
|
|
[ -n "$synparams" ] && setup_syn_flood_chain $chain $synparams $loglevel
|
|
|
|
if havechain $chain; then
|
|
[ "$epolicy" = ACCEPT ] && ordinal=2 || ordinal=3
|
|
[ -n "$synparams" ] && \
|
|
run_iptables -I $chain $ordinal -p tcp --syn -j @$chain
|
|
elif [ "$policy" != CONTINUE ]; then
|
|
#
|
|
# The chain doesn't exist. Create the chain and add policy
|
|
# rules
|
|
#
|
|
createchain $chain yes
|
|
|
|
#
|
|
# If either client or server is 'all' then this MUST be
|
|
# a policy chain and we must apply the appropriate policy rules
|
|
#
|
|
# Otherwise, this is a canonical chain which will be handled in
|
|
# the for loop below
|
|
#
|
|
if [ -n "$synparams" ]; then
|
|
case $policy in
|
|
ACCEPT|CONTINUE|QUEUE)
|
|
run_iptables -I $chain -p tcp --syn -j @$chain
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
case $chain in
|
|
all2*|*2all)
|
|
policy_rules $chain $policy $epolicy $loglevel
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
done
|
|
|
|
#
|
|
# Add policy rules to canonical chains
|
|
#
|
|
for zone in $FW $ZONES; do
|
|
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
|
|
|
|
if havenatchain $destchain ; then
|
|
run_iptables2 -t nat -A $sourcechain $@ -j $destchain
|
|
else
|
|
[ -n "$BRIDGING" -a -f $TMP_DIR/physdev ] && -rm -f $TMP_DIR/physdev
|
|
[ -n "$IPRANGE_MATCH" -a -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Jump to a RULES chain from one of the builtin nat chains. These jumps are
|
|
# are inserted before jumps to one-to-one NAT chains.
|
|
#
|
|
addrulejump() # $1 = BUILTIN chain, $2 = user chain, $3 - * other arguments
|
|
{
|
|
local sourcechain=$1 destchain=$2
|
|
shift
|
|
shift
|
|
|
|
if havenatchain $destchain; then
|
|
eval run_iptables2 -t nat -I $sourcechain \
|
|
\$${sourcechain}_rule $@ -j $destchain
|
|
eval ${sourcechain}_rule=\$\(\(\$${sourcechain}_rule + 1\)\)
|
|
else
|
|
[ -n "$BRIDGING" -a -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
|
|
[ -n "$IPRANGE_MATCH" -a -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
|
|
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Add jumps to early SNAT chains
|
|
#
|
|
for interface in $ALL_INTERFACES; do
|
|
addnatjump POSTROUTING $(snat_chain $interface) -o $interface
|
|
done
|
|
#
|
|
# Add jumps for dynamic nat chains
|
|
#
|
|
[ -n "$DYNAMIC_ZONES" ] && for interface in $ALL_INTERFACES ; do
|
|
addrulejump PREROUTING $(dynamic_in $interface) -i $interface
|
|
done
|
|
#
|
|
# 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
|
|
|
|
> /var/lib/shorewall/chains
|
|
> /var/lib/shorewall/zones
|
|
#
|
|
# Create forwarding chains for complex zones and generate jumps for IPSEC source hosts to that chain.
|
|
#
|
|
for zone in $ZONES; do
|
|
if eval test -n \"\$${zone}_is_complex\" ; then
|
|
frwd_chain=${zone}_frwd
|
|
createchain $frwd_chain No
|
|
|
|
if [ -n "$POLICY_MATCH" ]; then
|
|
#
|
|
# Because policy match only matches an 'in' or an 'out' policy (but not both), we have to place the
|
|
# '--pol ipsec --dir in' rules at the front of the interface forwarding chains. Otherwise, decrypted packets
|
|
# can match '--pol none --dir out' rules and send the packets down the wrong rules chain.
|
|
#
|
|
eval is_ipsec=\$${zone}_is_ipsec
|
|
|
|
if [ -n "$is_ipsec" ]; then
|
|
eval source_hosts=\$${zone}_hosts
|
|
if [ -n "$DYNAMIC_ZONES" ]; then
|
|
createchain ${zone}_dyn No
|
|
run_iptables -A $frwd_chain -j ${zone}_dyn
|
|
fi
|
|
else
|
|
eval source_hosts=\$${zone}_ipsec_hosts
|
|
fi
|
|
|
|
for host in $source_hosts; do
|
|
interface=${host%%:*}
|
|
networks=${host#*:}
|
|
|
|
run_iptables2 -A $(forward_chain $interface) $(match_source_hosts $networks) $(match_ipsec_in $zone $host) -j $frwd_chain
|
|
done
|
|
fi
|
|
fi
|
|
done
|
|
|
|
for zone in $ZONES; do
|
|
eval source_hosts=\$${zone}_hosts
|
|
|
|
chain1=$(rules_chain $FW $zone)
|
|
chain2=$(rules_chain $zone $FW)
|
|
|
|
eval complex=\$${zone}_is_complex
|
|
|
|
[ -n "$complex" ] && frwd_chain=${zone}_frwd
|
|
|
|
echo $zone $source_hosts >> /var/lib/shorewall/zones
|
|
|
|
if [ -n "$DYNAMIC_ZONES" ]; then
|
|
echo "$FW $zone $chain1" >> /var/lib/shorewall/chains
|
|
echo "$zone $FW $chain2" >> /var/lib/shorewall/chains
|
|
fi
|
|
|
|
need_broadcast=
|
|
|
|
for host in $source_hosts; do
|
|
interface=${host%%:*}
|
|
networks=${host#*:}
|
|
|
|
[ -n "$chain1" ] && run_iptables2 -A OUTPUT -o $interface $(match_dest_hosts $networks) $(match_ipsec_out $zone $host) -j $chain1
|
|
|
|
#
|
|
# Add jumps from the builtin chains for DNAT and SNAT rules
|
|
#
|
|
addrulejump PREROUTING $(dnat_chain $zone) -i $interface $(match_source_hosts $networks) $(match_ipsec_in $zone $host)
|
|
addrulejump POSTROUTING $(snat_chain $zone) -o $interface $(match_dest_hosts $networks) $(match_ipsec_out $zone $host)
|
|
|
|
[ -n "$chain2" ] && run_iptables2 -A $(input_chain $interface) $(match_source_hosts $networks) $(match_ipsec_in $zone $host) -j $chain2
|
|
|
|
if [ -n "$complex" ] && ! is_ipsec_host $zone $host ; then
|
|
run_iptables2 -A $(forward_chain $interface) $(match_source_hosts $networks) $(match_ipsec_in $zone $host) -j $frwd_chain
|
|
fi
|
|
|
|
case $networks in
|
|
*.*.*.*|+*)
|
|
if [ "$networks" != 0.0.0.0/0 ]; then
|
|
if ! list_search $interface $need_broadcast ; then
|
|
interface_has_option $interface detectnets && need_broadcast="$need_broadcast $interface"
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -n "$chain1" ]; then
|
|
for interface in $need_broadcast ; do
|
|
run_iptables -A OUTPUT -o $interface -d 255.255.255.255 -j $chain1
|
|
run_iptables -A OUTPUT -o $interface -d 224.0.0.0/4 -j $chain1
|
|
done
|
|
fi
|
|
|
|
for zone1 in $ZONES; do
|
|
|
|
eval policy=\$${zone}2${zone1}_policy
|
|
|
|
[ "$policy" = NONE ] && continue
|
|
|
|
eval dest_hosts=\$${zone1}_hosts
|
|
|
|
chain="$(rules_chain $zone $zone1)"
|
|
|
|
[ -z "$chain" ] && continue # CONTINUE policy and there is no canonical chain.
|
|
|
|
[ -n "$DYNAMIC_ZONES" ] && echo "$zone $zone1 $chain" >> /var/lib/shorewall/chains
|
|
|
|
if [ $zone = $zone1 ]; then
|
|
#
|
|
# Try not to generate superfluous intra-zone rules
|
|
#
|
|
eval routeback=\"\$${zone}_routeback\"
|
|
eval interfaces=\"\$${zone}_interfaces\"
|
|
eval ports="\$${zone}_ports"
|
|
|
|
num_ifaces=$(list_count1 $interfaces)
|
|
#
|
|
# If the zone has a single interface then what matters is how many ports it has
|
|
#
|
|
[ $num_ifaces -eq 1 -a -n "$ports" ] && num_ifaces=$(list_count1 $ports)
|
|
#
|
|
# If we don't need to route back and if we have only one interface or one port to
|
|
# the zone then assume that hosts in the zone can communicate directly.
|
|
#
|
|
if [ $num_ifaces -lt 2 -a -z "$routeback" ] ; then
|
|
continue
|
|
fi
|
|
else
|
|
routeback=
|
|
num_ifaces=0
|
|
fi
|
|
|
|
if [ -n "$complex" ]; then
|
|
for host1 in $dest_hosts; do
|
|
interface1=${host1%%:*}
|
|
networks1=${host1#*:}
|
|
#
|
|
# Only generate an intrazone rule if the zone has more than one interface (port) or if
|
|
# routeback was specified for this host group
|
|
#
|
|
if [ $zone != $zone1 -o $num_ifaces -gt 1 ] || list_search $host1 $routeback ; then
|
|
run_iptables2 -A $frwd_chain -o $interface1 $(match_dest_hosts $networks1) $(match_ipsec_out $zone1 $host1) -j $chain
|
|
fi
|
|
done
|
|
else
|
|
for host in $source_hosts; do
|
|
interface=${host%%:*}
|
|
networks=${host#*:}
|
|
|
|
chain3=$(forward_chain $interface)
|
|
|
|
for host1 in $dest_hosts; do
|
|
interface1=${host1%%:*}
|
|
networks1=${host1#*:}
|
|
|
|
if [ "$host" != "$host1" ] || list_search $host $routeback; then
|
|
run_iptables2 -A $chain3 $(match_source_hosts $networks) -o $interface1 $(match_dest_hosts $networks1) $(match_ipsec_out $zone1 $host1) -j $chain
|
|
fi
|
|
done
|
|
done
|
|
fi
|
|
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
|
|
#
|
|
# Bridges under the 2.4 kernel have the wierd property that REJECTS have the physdev-in and physdev-out set to the input physdev.
|
|
# To accomodate this feature/bug, we effectively set 'routeback' on bridge ports.
|
|
#
|
|
eval ports=\$$(chain_base $interface)_ports
|
|
for port in $ports; do
|
|
run_iptables -A $(forward_chain $interface) -o $interface -m physdev --physdev-in $port --physdev-out $port -j ACCEPT
|
|
done
|
|
done
|
|
|
|
chain=${FW}2${FW}
|
|
|
|
if havechain $chain; then
|
|
#
|
|
# There is a fw->fw chain. Send loopback output through that chain
|
|
#
|
|
run_ip link ls | grep LOOPBACK | while read ordinal interface rest ; do
|
|
run_iptables -A OUTPUT -o ${interface%:*} -j $chain
|
|
done
|
|
#
|
|
# And delete the unconditional ACCEPT rule
|
|
#
|
|
run_iptables -D OUTPUT -o lo -j ACCEPT
|
|
fi
|
|
|
|
complete_standard_chain INPUT all $FW
|
|
complete_standard_chain OUTPUT $FW all
|
|
complete_standard_chain FORWARD all all
|
|
#
|
|
# Remove rules added to keep the firewall alive during [re]start"
|
|
#
|
|
disable_critical_hosts
|
|
|
|
for chain in INPUT OUTPUT FORWARD; do
|
|
run_iptables -D $chain -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
run_iptables -D $chain -p udp --dport 53 -j ACCEPT
|
|
done
|
|
|
|
process_routestopped -D
|
|
|
|
if [ -n "$LOGALLNEW" ]; then
|
|
for table in mangle nat filter; do
|
|
case $table in
|
|
mangle)
|
|
chains="PREROUTING INPUT FORWARD POSTROUTING"
|
|
;;
|
|
nat)
|
|
chains="PREROUTING POSTROUTING OUTPUT"
|
|
;;
|
|
*)
|
|
chains="INPUT FORWARD OUTPUT"
|
|
;;
|
|
esac
|
|
|
|
for chain in $chains; do
|
|
log_rule_limit $LOGALLNEW $chain $table $chain "" "" -I -m state --state NEW -t $table
|
|
done
|
|
done
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Check for disabled startup
|
|
#
|
|
check_disabled_startup() {
|
|
if [ -z "$STARTUP_ENABLED" ]; then
|
|
echo " Shorewall Startup is disabled -- to enable startup"
|
|
echo " after you have completed Shorewall configuration,"
|
|
echo " change the setting of STARTUP_ENABLED to Yes in"
|
|
echo " /etc/shorewall/shorewall.conf"
|
|
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
my_mutex_off
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Start/Restart the Firewall
|
|
#
|
|
define_firewall() # $1 = Command (Start or Restart)
|
|
{
|
|
check_disabled_startup
|
|
|
|
echo "${1}ing Shorewall..."
|
|
|
|
verify_os_version
|
|
verify_ip
|
|
|
|
[ -d /var/lib/shorewall ] || { mkdir -p /var/lib/shorewall ; chmod 700 /var/lib/shorewall; }
|
|
|
|
RESTOREBASE=$(mktempfile /var/lib/shorewall)
|
|
|
|
[ -n "$RESTOREBASE" ] || startup_error "Cannot create temporary file in /var/lib/shorewall"
|
|
|
|
echo '#bin/sh' >> $RESTOREBASE
|
|
save_command "#"
|
|
save_command "# Restore base file generated by Shorewall $version - $(date)"
|
|
save_command "#"
|
|
save_command ". /usr/share/shorewall/functions"
|
|
|
|
f=$(find_file params)
|
|
|
|
[ -f $f ] && \
|
|
save_command ". $f"
|
|
|
|
save_command "#"
|
|
save_command "MODULESDIR=\"$MODULESDIR\""
|
|
save_command "MODULE_SUFFIX=\"$MODULE_SUFFIX\""
|
|
|
|
save_load_kernel_modules
|
|
|
|
echo "Initializing..."; initialize_netfilter
|
|
|
|
echo "Configuring Proxy ARP"; setup_proxy_arp
|
|
#
|
|
# [re]-Establish routing
|
|
#
|
|
setup_providers $(find_file providers)
|
|
[ -n "$ROUTEMARK_INTERFACES" ] && setup_routes
|
|
|
|
|
|
echo "Setting up NAT..."; setup_nat
|
|
echo "Setting up NETMAP..."; setup_netmap
|
|
echo "Adding Common Rules"; add_common_rules
|
|
|
|
tunnels=$(find_file tunnels)
|
|
[ -f $tunnels ] && \
|
|
echo "Processing $tunnels..." && setup_tunnels $tunnels
|
|
|
|
setup_ipsec
|
|
|
|
maclist_hosts=$(find_hosts_by_option maclist)
|
|
[ -n "$maclist_hosts" ] && setup_mac_lists
|
|
|
|
echo "Processing $(find_file rules)..."; process_rules
|
|
echo "Processing Actions..."; process_actions2
|
|
process_actions3
|
|
echo "Processing $(find_file 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
|
|
|
|
ecn=$(find_file ecn)
|
|
[ -f $ecn ] && [ -n "$MANGLE_ENABLED" ] && setup_ecn $ecn
|
|
|
|
[ -n "$TC_ENABLED" ] && setup_tc
|
|
|
|
echo "Activating Rules..."; activate_rules
|
|
|
|
[ -n "$aliases_to_add" ] && \
|
|
echo "Adding IP Addresses..." && add_ip_aliases
|
|
|
|
for file in chains nat proxyarp zones; do
|
|
append_file $file
|
|
done
|
|
|
|
save_progress_message "Restoring Netfilter Configuration..."
|
|
|
|
save_command 'iptables-restore << __EOF__'
|
|
|
|
# 'shorewall save' appends the iptables-save output and '__EOF__'
|
|
|
|
mv -f $RESTOREBASE /var/lib/shorewall/restore-base-$$
|
|
|
|
> $RESTOREBASE
|
|
|
|
save_command "#"
|
|
save_command "# Restore tail file generated by Shorewall $version - $(date)"
|
|
save_command "#"
|
|
save_command "date > /var/lib/shorewall/restarted"
|
|
|
|
run_user_exit start
|
|
|
|
[ -n "$DELAYBLACKLISTLOAD" ] && refresh_blacklist
|
|
|
|
createchain shorewall no
|
|
|
|
date > /var/lib/shorewall/restarted
|
|
|
|
report "Shorewall ${1}ed"
|
|
|
|
run_user_exit started
|
|
|
|
rm -rf $TMP_DIR
|
|
|
|
mv -f /var/lib/shorewall/restore-base-$$ /var/lib/shorewall/restore-base
|
|
mv -f $RESTOREBASE /var/lib/shorewall/restore-tail
|
|
}
|
|
|
|
#
|
|
# Refresh the firewall
|
|
#
|
|
refresh_firewall()
|
|
{
|
|
echo "Refreshing Shorewall..."
|
|
|
|
echo "Determining Zones and Interfaces..."
|
|
|
|
determine_zones
|
|
|
|
validate_interfaces_file
|
|
|
|
determine_interfaces
|
|
|
|
run_user_exit refresh
|
|
|
|
#
|
|
# Blacklist
|
|
#
|
|
refresh_blacklist
|
|
|
|
ecn=$(find_file ecn)
|
|
|
|
[ -f $ecn ] && [ -n "$MANGLE_ENABLED" ] && setup_ecn $ecn
|
|
#
|
|
# Refresh Traffic Control
|
|
#
|
|
[ -n "$TC_ENABLED" ] && refresh_tc
|
|
|
|
report "Shorewall Refreshed"
|
|
|
|
rm -rf $TMP_DIR
|
|
}
|
|
|
|
#
|
|
# Add a host or networks to a zone
|
|
#
|
|
add_to_zone() # $1...${n-1} = <interface>[:<hosts>] $n = zone
|
|
{
|
|
local interface host zone z h z1 z2 chain
|
|
local dhcp_interfaces blacklist_interfaces maclist_interfaces
|
|
local tcpflags_interfaces newhostlist=
|
|
local rulenum source_chain dest_hosts iface hosts hostlist=
|
|
|
|
nat_chain_exists() # $1 = chain name
|
|
{
|
|
qt $IPTABLES -t nat -L $1 -n
|
|
}
|
|
|
|
do_iptables() # $@ = command
|
|
{
|
|
[ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
|
|
[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
|
|
|
|
if ! $IPTABLES $@ ; then
|
|
error_message "Can't add $newhost to zone $zone"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Load $zones
|
|
#
|
|
determine_zones
|
|
#
|
|
# Validate Interfaces File
|
|
#
|
|
validate_interfaces_file
|
|
#
|
|
# Validate Hosts File
|
|
#
|
|
validate_hosts_file
|
|
#
|
|
# Validate IPSec File
|
|
#
|
|
f=$(find_file ipsec)
|
|
|
|
[ -f $f ] && setup_ipsec $f
|
|
#
|
|
# Normalize host list
|
|
#
|
|
while [ $# -gt 1 ]; do
|
|
interface=${1%%:*}
|
|
host=${1#*:}
|
|
#
|
|
# Be sure that the interface was dynamic at last [re]start
|
|
#
|
|
if ! chain_exists $(input_chain $interface) ; then
|
|
startup_error "Unknown interface $interface"
|
|
fi
|
|
|
|
if ! chain_exists $(dynamic_in $interface) ; then
|
|
startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf"
|
|
fi
|
|
|
|
if [ -z "$host" ]; then
|
|
hostlist="$hostlist $interface:0.0.0.0/0"
|
|
else
|
|
for h in $(separate_list $host); do
|
|
hostlist="$hostlist $interface:$h"
|
|
done
|
|
fi
|
|
|
|
shift
|
|
done
|
|
#
|
|
# Validate Zone
|
|
#
|
|
zone=$1
|
|
|
|
validate_zone $zone || startup_error "Unknown zone: $zone"
|
|
|
|
[ "$zone" = $FW ] && startup_error "Can't add $1 to firewall zone"
|
|
|
|
#
|
|
# Be sure that Shorewall has been restarted using a DZ-aware version of the code
|
|
#
|
|
[ -f /var/lib/shorewall/chains ] || startup_error "/var/lib/shorewall/chains -- file not found"
|
|
[ -f /var/lib/shorewall/zones ] || startup_error "/var/lib/shorewall/zones -- file not found"
|
|
#
|
|
# Check for duplicates and create a new zone state file
|
|
#
|
|
> /var/lib/shorewall/zones_$$
|
|
|
|
while read z hosts; do
|
|
if [ "$z" = "$zone" ]; then
|
|
for h in $hostlist; do
|
|
list_search $h $hosts
|
|
if [ "$?" -gt 0 ]; then
|
|
newhostlist="$newhostlist $h"
|
|
else
|
|
error_message "$h already in zone $zone"
|
|
fi
|
|
done
|
|
|
|
[ -z "$hosts" ] && hosts=$newhostlist || hosts="$hosts $newhostlist"
|
|
fi
|
|
|
|
eval ${z}_hosts=\"$hosts\"
|
|
|
|
echo "$z $hosts" >> /var/lib/shorewall/zones_$$
|
|
done < /var/lib/shorewall/zones
|
|
|
|
mv -f /var/lib/shorewall/zones_$$ /var/lib/shorewall/zones
|
|
|
|
terminator=fatal_error
|
|
#
|
|
# Create a new Zone state file
|
|
#
|
|
for newhost in $newhostlist; do
|
|
#
|
|
# Isolate interface and host parts
|
|
#
|
|
interface=${newhost%%:*}
|
|
host=${newhost#*:}
|
|
#
|
|
# If the zone passed in the command has a dnat chain then insert a rule in
|
|
# the nat table PREROUTING chain to jump to that chain when the source
|
|
# matches the new host(s)#
|
|
#
|
|
chain=${zone}_dnat
|
|
|
|
if nat_chain_exists $chain; then
|
|
do_iptables -t nat -A $(dynamic_in $interface) $(source_ip_range $host) $(match_ipsec_in $zone $newhost) -j $chain
|
|
fi
|
|
#
|
|
# Insert new rules into the filter table for the passed interface
|
|
#
|
|
while read z1 z2 chain; do
|
|
[ "$z1" = "$z2" ] && op="-I" || op="-A"
|
|
if [ "$z1" = "$zone" ]; then
|
|
if [ "$z2" = "$FW" ]; then
|
|
do_iptables $op $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j $chain
|
|
else
|
|
source_chain=$(dynamic_fwd $interface)
|
|
if is_ipsec_host $z1 $newhost ; then
|
|
do_iptables $op $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd
|
|
else
|
|
eval dest_hosts=\"\$${z2}_hosts\"
|
|
|
|
for h in $dest_hosts; do
|
|
iface=${h%%:*}
|
|
hosts=${h#*:}
|
|
|
|
if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then
|
|
do_iptables $op $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
elif [ "$z2" = "$zone" ]; then
|
|
if [ "$z1" = "$FW" ]; then
|
|
#
|
|
# Add a rule to the dynamic out chain for the interface
|
|
#
|
|
do_iptables $op $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain
|
|
else
|
|
eval source_hosts=\"\$${z1}_hosts\"
|
|
|
|
for h in $source_hosts; do
|
|
iface=${h%%:*}
|
|
hosts=${h#*:}
|
|
|
|
if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then
|
|
if is_ipsec_host $z1 $h; then
|
|
do_iptables $op ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain
|
|
else
|
|
do_iptables $op $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
done < /var/lib/shorewall/chains
|
|
|
|
progress_message "$newhost added to zone $zone"
|
|
|
|
done
|
|
|
|
rm -rf $TMP_DIR
|
|
}
|
|
|
|
#
|
|
# Delete a host or networks from a zone
|
|
#
|
|
delete_from_zone() # $1 = <interface>[:<hosts>] $2 = zone
|
|
{
|
|
local interface host zone z h z1 z2 chain delhost
|
|
local dhcp_interfaces blacklist_interfaces maclist_interfaces tcpflags_interfaces
|
|
local rulenum source_chain dest_hosts iface hosts hostlist=
|
|
|
|
#
|
|
# Load $zones
|
|
#
|
|
determine_zones
|
|
#
|
|
# Validate Interfaces File
|
|
#
|
|
validate_interfaces_file
|
|
#
|
|
# Validate Hosts File
|
|
#
|
|
validate_hosts_file
|
|
#
|
|
# Validate IPSec File
|
|
#
|
|
f=$(find_file ipsec)
|
|
|
|
[ -f $f ] && setup_ipsec $f
|
|
|
|
#
|
|
# Normalize host list
|
|
#
|
|
while [ $# -gt 1 ]; do
|
|
interface=${1%%:*}
|
|
host=${1#*:}
|
|
#
|
|
# Be sure that the interface was dynamic at last [re]start
|
|
#
|
|
if ! chain_exists $(input_chain $interface) ; then
|
|
startup_error "Unknown interface $interface"
|
|
fi
|
|
|
|
if ! chain_exists $(dynamic_in $interface) ; then
|
|
startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf"
|
|
fi
|
|
|
|
if [ -z "$host" ]; then
|
|
hostlist="$hostlist $interface:0.0.0.0/0"
|
|
else
|
|
for h in $(separate_list $host); do
|
|
hostlist="$hostlist $interface:$h"
|
|
done
|
|
fi
|
|
|
|
shift
|
|
done
|
|
#
|
|
# Validate Zone
|
|
#
|
|
zone=$1
|
|
|
|
validate_zone $zone || startup_error "Unknown zone: $zone"
|
|
|
|
[ "$zone" = $FW ] && startup_error "Can't delete from the firewall zone"
|
|
|
|
#
|
|
# Be sure that Shorewall has been restarted using a DZ-aware version of the code
|
|
#
|
|
[ -f /var/lib/shorewall/chains ] || startup_error "/var/lib/shorewall/chains -- file not found"
|
|
[ -f /var/lib/shorewall/zones ] || startup_error "/var/lib/shorewall/zones -- file not found"
|
|
#
|
|
# Delete the passed hosts from the zone state file
|
|
#
|
|
> /var/lib/shorewall/zones_$$
|
|
|
|
while read z hosts; do
|
|
if [ "$z" = "$zone" ]; then
|
|
temp=$hosts
|
|
hosts=
|
|
|
|
for host in $hostlist; do
|
|
found=
|
|
for h in $temp; do
|
|
if [ "$h" = "$host" ]; then
|
|
found=Yes
|
|
break
|
|
fi
|
|
done
|
|
|
|
[ -n "$found" ] || error_message "Warning: $host does not appear to be in zone $zone"
|
|
done
|
|
|
|
for h in $temp; do
|
|
found=
|
|
for host in $hostlist; do
|
|
if [ "$h" = "$host" ]; then
|
|
found=Yes
|
|
break
|
|
fi
|
|
done
|
|
|
|
[ -n "$found" ] || hosts="$hosts $h"
|
|
done
|
|
fi
|
|
|
|
eval ${z}_hosts=\"$hosts\"
|
|
|
|
echo "$z $hosts" >> /var/lib/shorewall/zones_$$
|
|
done < /var/lib/shorewall/zones
|
|
|
|
mv -f /var/lib/shorewall/zones_$$ /var/lib/shorewall/zones
|
|
|
|
terminator=fatal_error
|
|
|
|
for delhost in $hostlist; do
|
|
interface=${delhost%%:*}
|
|
host=${delhost#*:}
|
|
#
|
|
# Delete any nat table entries for the host(s)
|
|
#
|
|
qt_iptables -t nat -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $zone $delhost) -j ${zone}_dnat
|
|
#
|
|
# Delete rules rules the input chains for the passed interface
|
|
#
|
|
while read z1 z2 chain; do
|
|
if [ "$z1" = "$zone" ]; then
|
|
if [ "$z2" = "$FW" ]; then
|
|
qt_iptables -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $delhost) -j $chain
|
|
else
|
|
source_chain=$(dynamic_fwd $interface)
|
|
if is_ipsec_host $z1 $delhost ; then
|
|
qt_iptables -D $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd
|
|
else
|
|
eval dest_hosts=\"\$${z2}_hosts\"
|
|
|
|
[ "$z2" = "$zone" ] && dest_hosts="$dest_hosts $hostlist"
|
|
|
|
for h in $dest_hosts; do
|
|
iface=${h%%:*}
|
|
hosts=${h#*:}
|
|
|
|
if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then
|
|
qt_iptables -D $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
elif [ "$z2" = "$zone" ]; then
|
|
if [ "$z1" = "$FW" ]; then
|
|
qt_iptables -D $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain
|
|
else
|
|
eval source_hosts=\"\$${z1}_hosts\"
|
|
|
|
for h in $source_hosts; do
|
|
iface=${h%%:*}
|
|
hosts=${h#*:}
|
|
|
|
if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then
|
|
if is_ipsec_host $z1 $h; then
|
|
qt_iptables -D ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain
|
|
else
|
|
qt_iptables -D $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
done < /var/lib/shorewall/chains
|
|
|
|
progress_message "$delhost removed from zone $zone"
|
|
|
|
done
|
|
|
|
rm -rf $TMP_DIR
|
|
}
|
|
|
|
#
|
|
# Determine the value for a parameter that defaults to Yes
|
|
#
|
|
added_param_value_yes() # $1 = Parameter Name, $2 = Parameter value
|
|
{
|
|
local val="$2"
|
|
|
|
if [ -z "$val" ]; then
|
|
echo "Yes"
|
|
else case $val in
|
|
[Yy][Ee][Ss])
|
|
echo "Yes"
|
|
;;
|
|
[Nn][Oo])
|
|
echo ""
|
|
;;
|
|
*)
|
|
startup_error "Invalid value ($val) for $1"
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Determine the value for a parameter that defaults to No
|
|
#
|
|
added_param_value_no() # $1 = Parameter Name, $2 = Parameter value
|
|
{
|
|
local val="$2"
|
|
|
|
if [ -z "$val" ]; then
|
|
echo ""
|
|
else case $val in
|
|
[Yy][Ee][Ss])
|
|
echo "Yes"
|
|
;;
|
|
[Nn][Oo])
|
|
echo ""
|
|
;;
|
|
*)
|
|
startup_error "Invalid value ($val) for $1"
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Initialize this program
|
|
#
|
|
do_initialize() {
|
|
|
|
# Run all utility programs using the C locale
|
|
#
|
|
# Thanks to Vincent Planchenault for this tip #
|
|
|
|
export LC_ALL=C
|
|
|
|
# Make sure umask is sane
|
|
umask 177
|
|
|
|
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
|
#
|
|
# Establish termination function
|
|
#
|
|
terminator=startup_error
|
|
#
|
|
# Clear all configuration variables
|
|
#
|
|
version=
|
|
IPTABLES=
|
|
FW=
|
|
SUBSYSLOCK=
|
|
ALLOWRELATED=Yes
|
|
LOGRATE=
|
|
LOGBURST=
|
|
LOGPARMS=
|
|
LOGLIMIT=
|
|
ADD_IP_ALIASES=
|
|
ADD_SNAT_ALIASES=
|
|
TC_ENABLED=
|
|
BLACKLIST_DISPOSITION=
|
|
BLACKLIST_LOGLEVEL=
|
|
CLAMPMSS=
|
|
ROUTE_FILTER=
|
|
LOG_MARTIANS=
|
|
DETECT_DNAT_IPADDRS=
|
|
MUTEX_TIMEOUT=
|
|
NEWNOTSYN=
|
|
LOGNEWNOTSYN=
|
|
FORWARDPING=
|
|
MACLIST_DISPOSITION=
|
|
MACLIST_LOG_LEVEL=
|
|
TCP_FLAGS_DISPOSITION=
|
|
TCP_FLAGS_LOG_LEVEL=
|
|
RFC1918_LOG_LEVEL=
|
|
MARK_IN_FORWARD_CHAIN=
|
|
SHARED_DIR=/usr/share/shorewall
|
|
FUNCTIONS=
|
|
VERSION_FILE=
|
|
LOGFORMAT=
|
|
LOGRULENUMBERS=
|
|
ADMINISABSENTMINDED=
|
|
BLACKLISTNEWONLY=
|
|
MODULE_SUFFIX=
|
|
ACTIONS=
|
|
USEDACTIONS=
|
|
SMURF_LOG_LEVEL=
|
|
DISABLE_IPV6=
|
|
BRIDGING=
|
|
DYNAMIC_ZONES=
|
|
PKTTYPE=
|
|
RETAIN_ALIASES=
|
|
DELAYBLACKLISTLOAD=
|
|
LOGTAGONLY=
|
|
LOGALLNEW=
|
|
RFC1918_STRICT=
|
|
MACLIST_TTL=
|
|
SAVE_IPSETS=
|
|
RESTOREFILE=
|
|
MAPOLDACTIONS=
|
|
|
|
RESTOREBASE=
|
|
TMP_DIR=
|
|
ALL_INTERFACES=
|
|
ROUTEMARK_INTERFACES=
|
|
ROUTEMARK=256
|
|
PROVIDERS=
|
|
CRITICALHOSTS=
|
|
IPSECFILE=
|
|
|
|
stopping=
|
|
have_mutex=
|
|
masq_seq=1
|
|
nonat_seq=1
|
|
aliases_to_add=
|
|
|
|
FUNCTIONS=$SHARED_DIR/functions
|
|
|
|
if [ -f $FUNCTIONS ]; then
|
|
[ -n "$QUIET" ] || echo "Loading $FUNCTIONS..."
|
|
. $FUNCTIONS
|
|
else
|
|
startup_error "$FUNCTIONS does not exist!"
|
|
fi
|
|
|
|
TMP_DIR=$(mktempdir)
|
|
|
|
[ -n "$TMP_DIR" ] && chmod 700 $TMP_DIR || \
|
|
startup_error "Can't create a temporary directory"
|
|
|
|
trap "rm -rf $TMP_DIR; my_mutex_off; exit 2" 1 2 3 4 5 6 9
|
|
|
|
ensure_config_path
|
|
|
|
VERSION_FILE=$SHARED_DIR/version
|
|
|
|
[ -f $VERSION_FILE ] && version=$(cat $VERSION_FILE)
|
|
|
|
run_user_exit params
|
|
|
|
config=$(find_file shorewall.conf)
|
|
|
|
if [ -f $config ]; then
|
|
if [ -r $config ]; then
|
|
[ -n "$QUIET" ] || echo "Processing $config..."
|
|
. $config
|
|
else
|
|
echo " ERROR: Cannot read $config (Hint: Are you root?)"
|
|
exit 2
|
|
fi
|
|
else
|
|
echo "$config does not exist!" >&2
|
|
exit 2
|
|
fi
|
|
#
|
|
# Restore CONFIG_PATH if the shorewall.conf file cleared it
|
|
#
|
|
ensure_config_path
|
|
#
|
|
# Determine the capabilities of the installed iptables/netfilter
|
|
# We load the kernel modules here to accurately determine
|
|
# capabilities when module autoloading isn't enabled.
|
|
#
|
|
|
|
[ -n "$MODULE_SUFFIX" ] || MODULE_SUFFIX="o gz ko o.gz ko.gz"
|
|
load_kernel_modules
|
|
|
|
if [ -z "$IPTABLES" ]; then
|
|
IPTABLES=$(which iptables 2> /dev/null)
|
|
|
|
[ -z "$IPTABLES" ] && startup_error "Can't find iptables executable"
|
|
else
|
|
[ -e "$IPTABLES" ] || startup_error "\$IPTABLES=$IPTABLES does not exist or is not executable"
|
|
fi
|
|
|
|
PKTTYPE=$(added_param_value_no PKTTYPE $PKTTYPE) # Used in determine_capabilities
|
|
|
|
determine_capabilities
|
|
|
|
[ -d /var/lib/shorewall ] || mkdir -p /var/lib/shorewall
|
|
|
|
[ -z "$FW" ] && FW=fw
|
|
|
|
ALLOWRELATED="$(added_param_value_yes ALLOWRELATED $ALLOWRELATED)"
|
|
[ -n "$ALLOWRELATED" ] || \
|
|
startup_error "ALLOWRELATED=No is not supported"
|
|
ADD_IP_ALIASES="$(added_param_value_yes ADD_IP_ALIASES $ADD_IP_ALIASES)"
|
|
TC_ENABLED="$(added_param_value_yes TC_ENABLED $TC_ENABLED)"
|
|
|
|
if [ -n "${LOGRATE}${LOGBURST}" ]; then
|
|
LOGLIMIT="--match limit"
|
|
[ -n "$LOGRATE" ] && LOGLIMIT="$LOGLIMIT --limit $LOGRATE"
|
|
[ -n "$LOGBURST" ] && LOGLIMIT="$LOGLIMIT --limit-burst $LOGBURST"
|
|
fi
|
|
|
|
if [ -n "$IP_FORWARDING" ]; then
|
|
case "$IP_FORWARDING" in
|
|
[Oo][Nn]|[Oo][Ff][Ff]|[Kk][Ee][Ee][Pp])
|
|
;;
|
|
*)
|
|
startup_error "Invalid value ($IP_FORWARDING) for IP_FORWARDING"
|
|
;;
|
|
esac
|
|
else
|
|
IP_FORWARDING=On
|
|
fi
|
|
|
|
if [ -n "$TC_ENABLED" -a -z "$MANGLE_ENABLED" ]; then
|
|
startup_error "Traffic Control requires Mangle"
|
|
fi
|
|
|
|
[ -z "$BLACKLIST_DISPOSITION" ] && BLACKLIST_DISPOSITION=DROP
|
|
|
|
case "$CLAMPMSS" in
|
|
[0-9]*)
|
|
;;
|
|
*)
|
|
CLAMPMSS=$(added_param_value_no CLAMPMSS $CLAMPMSS)
|
|
;;
|
|
esac
|
|
|
|
ADD_SNAT_ALIASES=$(added_param_value_no ADD_SNAT_ALIASES $ADD_SNAT_ALIASES)
|
|
ROUTE_FILTER=$(added_param_value_no ROUTE_FILTER $ROUTE_FILTER)
|
|
LOG_MARTIANS=$(added_param_value_no LOG_MARTIANS $LOG_MARTIANS)
|
|
DETECT_DNAT_IPADDRS=$(added_param_value_no DETECT_DNAT_IPADDRS $DETECT_DNAT_IPADDRS)
|
|
FORWARDPING=$(added_param_value_no FORWARDPING $FORWARDPING)
|
|
[ -n "$FORWARDPING" ] && \
|
|
startup_error "FORWARDPING=Yes is no longer supported"
|
|
|
|
NEWNOTSYN=$(added_param_value_yes NEWNOTSYN $NEWNOTSYN)
|
|
|
|
maclist_target=reject
|
|
|
|
if [ -n "$MACLIST_DISPOSITION" ] ; then
|
|
case $MACLIST_DISPOSITION in
|
|
REJECT)
|
|
;;
|
|
DROP)
|
|
maclist_target=DROP
|
|
;;
|
|
ACCEPT)
|
|
maclist_target=RETURN
|
|
;;
|
|
*)
|
|
startup_error "Invalid value ($MACLIST_DISPOSITION) for MACLIST_DISPOSITION"
|
|
;;
|
|
esac
|
|
else
|
|
MACLIST_DISPOSITION=REJECT
|
|
fi
|
|
|
|
if [ -n "$TCP_FLAGS_DISPOSITION" ] ; then
|
|
case $TCP_FLAGS_DISPOSITION in
|
|
REJECT|ACCEPT|DROP)
|
|
;;
|
|
*)
|
|
startup_error "Invalid value ($TCP_FLAGS_DISPOSITION) for TCP_FLAGS_DISPOSITION"
|
|
;;
|
|
esac
|
|
else
|
|
TCP_FLAGS_DISPOSITION=DROP
|
|
fi
|
|
|
|
[ -z "$RFC1918_LOG_LEVEL" ] && RFC1918_LOG_LEVEL=info
|
|
|
|
MARK_IN_FORWARD_CHAIN=$(added_param_value_no MARK_IN_FORWARD_CHAIN $MARK_IN_FORWARD_CHAIN)
|
|
[ -n "$MARK_IN_FORWARD_CHAIN" ] && MARKING_CHAIN=tcfor || MARKING_CHAIN=tcpre
|
|
if [ -n "$TC_ENABLED" ]; then
|
|
CLEAR_TC=$(added_param_value_yes CLEAR_TC $CLEAR_TC)
|
|
else
|
|
CLEAR_TC=
|
|
fi
|
|
|
|
if [ -n "$LOGFORMAT" ]; then
|
|
if [ -n "$(echo $LOGFORMAT | grep '%d')" ]; then
|
|
LOGRULENUMBERS=Yes
|
|
temp=$(printf "$LOGFORMAT" fooxx 1 barxx 2> /dev/null)
|
|
if [ $? -ne 0 ]; then
|
|
startup_error "Invalid LOGFORMAT string: \"$LOGFORMAT\""
|
|
fi
|
|
else
|
|
temp=$(printf "$LOGFORMAT" fooxx barxx 2> /dev/null)
|
|
if [ $? -ne 0 ]; then
|
|
startup_error "Invalid LOGFORMAT string: \"$LOGFORMAT\""
|
|
fi
|
|
fi
|
|
|
|
[ ${#temp} -le 29 ] || startup_error "LOGFORMAT string is longer than 29 characters: \"$LOGFORMAT\""
|
|
else
|
|
LOGFORMAT="Shorewall:%s:%s:"
|
|
fi
|
|
ADMINISABSENTMINDED=$(added_param_value_no ADMINISABSENTMINDED $ADMINISABSENTMINDED)
|
|
BLACKLISTNEWONLY=$(added_param_value_no BLACKLISTNEWONLY $BLACKLISTNEWONLY)
|
|
DISABLE_IPV6=$(added_param_value_no DISABLE_IPV6 $DISABLE_IPV6)
|
|
BRIDGING=$(added_param_value_no BRIDGING $BRIDGING)
|
|
DYNAMIC_ZONES=$(added_param_value_no DYNAMIC_ZONES $DYNAMIC_ZONES)
|
|
STARTUP_ENABLED=$(added_param_value_yes STARTUP_ENABLED $STARTUP_ENABLED)
|
|
RETAIN_ALIASES=$(added_param_value_no RETAIN_ALIASES $RETAIN_ALIASES)
|
|
DELAYBLACKLISTLOAD=$(added_param_value_no DELAYBLACKLISTLOAD $DELAYBLACKLISTLOAD)
|
|
LOGTAGONLY=$(added_param_value_no LOGTAGONLY $LOGTAGONLY)
|
|
RFC1918_STRICT=$(added_param_value_no RFC1918_STRICT $RFC1918_STRICT)
|
|
SAVE_IPSETS=$(added_param_value_no SAVE_IPSETS $SAVE_IPSETS)
|
|
MAPOLDACTIONS=$(added_param_value_yes MAPOLDACTIONS $MAPOLDACTIONS)
|
|
|
|
case ${IPSECFILE:=ipsec} in
|
|
ipsec|zones)
|
|
;;
|
|
*)
|
|
startup_error "Invalid value ($IPSECFILE) for IPSECFILE option"
|
|
;;
|
|
esac
|
|
|
|
[ "x${SHOREWALL_DIR}" = "x." ] && SHOREWALL_DIR="$PWD"
|
|
|
|
#
|
|
# Strip the files that we use often
|
|
#
|
|
strip_file interfaces
|
|
strip_file hosts
|
|
#
|
|
# Check out the user's shell
|
|
#
|
|
[ -n "$SHOREWALL_SHELL" ] || SHOREWALL_SHELL=/bin/sh
|
|
|
|
temp=$(decodeaddr 192.168.1.1)
|
|
if [ $(encodeaddr $temp) != 192.168.1.1 ]; then
|
|
startup_error "Shell $SHOREWALL_SHELL is broken and may not be used with Shorewall"
|
|
fi
|
|
|
|
rm -f $TMP_DIR/physdev
|
|
rm -f $TMP_DIR/iprange
|
|
}
|
|
|
|
#
|
|
# Give Usage Information
|
|
#
|
|
usage() {
|
|
echo "Usage: $0 [debug] {start|stop|reset|restart|status|refresh|clear|{add|delete} <interface>[:hosts] zone}}"
|
|
exit 1
|
|
}
|
|
|
|
#
|
|
# E X E C U T I O N B E G I N S H E R E
|
|
#
|
|
#
|
|
# Start trace if first arg is "debug"
|
|
#
|
|
[ $# -gt 1 ] && [ "$1" = "debug" ] && { set -x ; shift ; }
|
|
|
|
nolock=
|
|
|
|
[ $# -gt 1 ] && [ "$1" = "nolock" ] && { nolock=Yes; shift ; }
|
|
|
|
trap "my_mutex_off; exit 2" 1 2 3 4 5 6 9
|
|
|
|
COMMAND="$1"
|
|
|
|
case "$COMMAND" in
|
|
stop)
|
|
[ $# -ne 1 ] && usage
|
|
do_initialize
|
|
my_mutex_on
|
|
#
|
|
# Don't want to do a 'stop' when startup is disabled
|
|
#
|
|
check_disabled_startup
|
|
echo -n "Stopping Shorewall..."
|
|
stop_firewall
|
|
[ -n "$SUBSYSLOCK" ] && rm -f $SUBSYSLOCK
|
|
echo "done."
|
|
my_mutex_off
|
|
;;
|
|
|
|
start)
|
|
[ $# -ne 1 ] && usage
|
|
do_initialize
|
|
my_mutex_on
|
|
if qt $IPTABLES -L shorewall -n ; then
|
|
[ -n "$SUBSYSLOCK" ] && touch $SUBSYSLOCK
|
|
echo "Shorewall Already Started"
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
my_mutex_off
|
|
exit 0;
|
|
fi
|
|
define_firewall "Start" && [ -n "$SUBSYSLOCK" ] && touch $SUBSYSLOCK
|
|
my_mutex_off
|
|
;;
|
|
|
|
restart)
|
|
[ $# -ne 1 ] && usage
|
|
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)
|
|
[ $# -ne 1 ] && usage
|
|
echo "Shorewall-$version Status at $HOSTNAME - $(date)"
|
|
echo
|
|
$IPTABLES -L -n -v
|
|
;;
|
|
|
|
reset)
|
|
[ $# -ne 1 ] && usage
|
|
do_initialize
|
|
my_mutex_on
|
|
if ! qt $IPTABLES -L shorewall -n ; then
|
|
echo "Shorewall Not Started"
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
my_mutex_off
|
|
exit 2;
|
|
fi
|
|
$IPTABLES -Z
|
|
$IPTABLES -t nat -Z
|
|
$IPTABLES -t mangle -Z
|
|
report "Shorewall Counters Reset"
|
|
date > /var/lib/shorewall/restarted
|
|
my_mutex_off
|
|
;;
|
|
|
|
refresh)
|
|
[ $# -ne 1 ] && usage
|
|
do_initialize
|
|
my_mutex_on
|
|
if ! qt $IPTABLES -L shorewall -n ; then
|
|
echo "Shorewall Not Started"
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
my_mutex_off
|
|
exit 2;
|
|
fi
|
|
refresh_firewall;
|
|
my_mutex_off
|
|
;;
|
|
|
|
clear)
|
|
[ $# -ne 1 ] && usage
|
|
do_initialize
|
|
my_mutex_on
|
|
echo -n "Clearing Shorewall..."
|
|
clear_firewall
|
|
[ -n "$SUBSYSLOCK" ] && rm -f $SUBSYSLOCK
|
|
echo "done."
|
|
my_mutex_off
|
|
;;
|
|
|
|
check)
|
|
[ $# -ne 1 ] && usage
|
|
do_initialize
|
|
check_config
|
|
;;
|
|
|
|
add)
|
|
[ $# -lt 3 ] && usage
|
|
do_initialize
|
|
my_mutex_on
|
|
if ! qt $IPTABLES -L shorewall -n ; then
|
|
echo "Shorewall Not Started"
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
my_mutex_off
|
|
exit 2;
|
|
fi
|
|
shift
|
|
add_to_zone $@
|
|
my_mutex_off
|
|
;;
|
|
|
|
delete)
|
|
[ $# -lt 3 ] && usage
|
|
do_initialize
|
|
my_mutex_on
|
|
if ! qt $IPTABLES -L shorewall -n ; then
|
|
echo "Shorewall Not Started"
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
my_mutex_off
|
|
exit 2;
|
|
fi
|
|
shift
|
|
delete_from_zone $@
|
|
my_mutex_off
|
|
;;
|
|
|
|
call)
|
|
#
|
|
# Undocumented way to call functions in /usr/share/shorewall/firewall directly
|
|
#
|
|
shift;
|
|
do_initialize
|
|
EMPTY=
|
|
$@
|
|
;;
|
|
|
|
capabilities)
|
|
[ $# -ne 1 ] && usage
|
|
do_initialize
|
|
report_capabilities
|
|
;;
|
|
|
|
*)
|
|
usage
|
|
;;
|
|
|
|
esac
|