mirror of
https://gitlab.com/shorewall/code.git
synced 2024-11-23 16:13:18 +01:00
fb4ef97840
git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@4098 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
8954 lines
210 KiB
Bash
Executable File
8954 lines
210 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# The Shoreline Firewall (Shorewall) Packet Filtering Firewall Compiler - V3.2
|
|
#
|
|
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
|
|
#
|
|
# (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net)
|
|
#
|
|
# tcstart from tc4shorewall Version 0.5
|
|
# (c) 2005 Arne Bernin <arne@ucbering.de>
|
|
# Modified by Tom Eastep for integration into the Shorewall distribution
|
|
# published under GPL Version 2#
|
|
#
|
|
# 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:
|
|
#
|
|
# compile check Verify the configuration files.
|
|
# compile compile <path name> Compile into <path name>
|
|
#
|
|
# Environmental Variables:
|
|
#
|
|
# EXPORT=Yes -e option specified to /sbin/shorewall
|
|
# SHOREWALL_DIR A directory name was passed to /sbin/shorewall
|
|
# VERBOSE Standard Shorewall verbosity control.
|
|
|
|
# Fatal error -- stops the compiler after issuing the error message
|
|
#
|
|
fatal_error() # $* = Error Message
|
|
{ echo " ERROR: $@" >&2
|
|
[ -n "$TMP_DIR" ] && rm -rf $TMP_DIR
|
|
[ -n "$OUTPUT" ] && rm -f $OUTPUT
|
|
kill $$
|
|
exit 2
|
|
}
|
|
|
|
#
|
|
# Write the passed args to the compiler output file.
|
|
#
|
|
save_command()
|
|
{
|
|
|
|
[ $# -gt 0 ] && echo "${INDENT}${@}" >&3 || echo >&3
|
|
}
|
|
|
|
save_command_unindented()
|
|
{
|
|
echo "${@}" >&3
|
|
}
|
|
|
|
#
|
|
# Write a progress_message2 command to the output file.
|
|
#
|
|
save_progress_message()
|
|
{
|
|
echo >&3
|
|
echo "${INDENT}progress_message2 \"$@\"" >&3
|
|
echo >&3
|
|
}
|
|
|
|
save_progress_message_short()
|
|
{
|
|
echo "${INDENT}progress_message \"$@\"" >&3
|
|
}
|
|
|
|
progress_message_and_save()
|
|
{
|
|
progress_message "$@"
|
|
echo "${INDENT}progress_message \"$@\"" >&3
|
|
}
|
|
|
|
#
|
|
# Echo the contents of the passed file indented by $INDENT
|
|
#
|
|
indent() {
|
|
if [ -n "$INDENT" ]; then
|
|
if [ -n "$HAVEAWK" ]; then
|
|
eval awk \''BEGIN { indent=1; }; /^[[:space:]]*$/ { print ""; indent=1; next; }; { if (indent == 1) print "'"$INDENT"'" $0; else print; }; { indent=1; }; /\\$/ { indent=0; };'\' $1
|
|
else
|
|
eval sed \'s\/^/"$INDENT"\/\' $1
|
|
fi
|
|
else
|
|
cat $1
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Append a file to the compiler's output with indentation.
|
|
#
|
|
append_file() # $1 = File Name
|
|
{
|
|
local user_exit=$(find_file $1)
|
|
|
|
if [ -f $user_exit ]; then
|
|
save_progress_message "Processing $user_exit ..."
|
|
indent $user_exit >&3
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Generate a command to run iptables
|
|
#
|
|
do_iptables() {
|
|
save_command \$IPTABLES $@
|
|
}
|
|
|
|
#
|
|
# Generate an IPTABLES command. Include hacks to work around iptables limitations
|
|
#
|
|
run_iptables() {
|
|
if [ -z "$KLUDGEFREE" ]; then
|
|
#
|
|
# Purge the temporary files that we use to prevent duplicate '-m' specifications
|
|
#
|
|
[ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
|
|
[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
|
|
fi
|
|
|
|
save_command "$IPTABLES_COMMAND $@"
|
|
}
|
|
|
|
#
|
|
# Version of 'run_iptables' that inserts white space after "!" in the arg list
|
|
#
|
|
run_iptables2() {
|
|
if [ -z "$KLUDGEFREE" ]; then
|
|
#
|
|
# Purge the temporary files that we use to prevent duplicate '-m' specifications
|
|
#
|
|
[ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
|
|
[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
|
|
fi
|
|
|
|
save_command run_iptables $(fix_bang $@)
|
|
}
|
|
|
|
#
|
|
# Generate command to quietly run iptables
|
|
#
|
|
qt_iptables() {
|
|
if [ -z "$KLUDGEFREE" ]; then
|
|
#
|
|
# Purge the temporary files that we use to prevent duplicate '-m' specifications
|
|
#
|
|
[ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev
|
|
[ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange
|
|
fi
|
|
|
|
save_command qt \$IPTABLES $@
|
|
}
|
|
|
|
#
|
|
# Generate a command to run tc
|
|
#
|
|
run_tc() {
|
|
save_command run_tc $@
|
|
}
|
|
|
|
#
|
|
# Add the implicit ACCEPT rules at the end of a rules file section
|
|
#
|
|
finish_chain_section() # $1 = canonical chain $2 = state list
|
|
{
|
|
local policy policychain
|
|
|
|
[ -n "$FASTACCEPT" ] || run_iptables -A $1 -m state --state $2 -j ACCEPT
|
|
|
|
if list_search RELATED $(separate_list $2) ; then
|
|
if is_policy_chain $1 ; then
|
|
if eval test -n \"\$${1}_synparams\" ; then
|
|
if [ $SECTION = DONE ]; then
|
|
eval policy=\$${1}_policy
|
|
|
|
case $policy in
|
|
ACCEPT|CONTINUE|QUEUE)
|
|
run_iptables -A $1 -p tcp --syn -j @$1
|
|
;;
|
|
esac
|
|
else
|
|
run_iptables -A $1 -p tcp --syn -j @$1
|
|
fi
|
|
fi
|
|
else
|
|
eval policychain=\$${1}_policychain
|
|
|
|
if eval test -n \"\$${policychain}_synparams\" ; then
|
|
run_iptables -A $1 -p tcp --syn -j @$policychain
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
finish_section() # $1 = Section(s)
|
|
{
|
|
local zone zone1 chain
|
|
|
|
for zone in $ZONES $FW; do
|
|
for zone1 in $ZONES $FW; do
|
|
chain=${zone}2${zone1}
|
|
if havechain $chain; then
|
|
finish_chain_section $chain $1
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
|
|
#
|
|
# 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", do section-end processing
|
|
{
|
|
local c=$(chain_base $1)
|
|
|
|
run_iptables -N $1
|
|
|
|
if [ $2 = yes ]; then
|
|
case $SECTION in
|
|
NEW|DONE)
|
|
finish_chain_section $1 ESTABLISHED,RELATED
|
|
;;
|
|
RELATED)
|
|
finish_chain_section $1 ESTABLISHED
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
eval exists_${c}=Yes
|
|
}
|
|
|
|
createchain2() # $1 = chain name, $2 = If "yes", create default rules
|
|
{
|
|
local c=$(chain_base $1)
|
|
|
|
ensurechain $1
|
|
|
|
if [ $2 = yes ]; then
|
|
case $SECTION in
|
|
NEW|DONE)
|
|
finish_chain_section $1 ESTABLISHED,RELATED
|
|
;;
|
|
RELATED)
|
|
finish_chain_section $1 ESTABLISHED
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
eval exists_${c}=Yes
|
|
}
|
|
|
|
#
|
|
# 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
|
|
}
|
|
|
|
#
|
|
# 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 $@
|
|
}
|
|
|
|
#
|
|
# Query NetFilter about the existence of a filter chain
|
|
#
|
|
chain_exists() # $1 = chain name
|
|
{
|
|
qt $IPTABLES -L $1 -n
|
|
}
|
|
|
|
#
|
|
# Create a mangle chain
|
|
#
|
|
# Create a variable exists_mangle_${1} and set its value to Yes to indicate that
|
|
# the chain now exists.
|
|
#
|
|
createmanglechain() # $1 = chain name
|
|
{
|
|
run_iptables -t mangle -N $1
|
|
|
|
eval exists_mangle_${1}=Yes
|
|
}
|
|
|
|
#
|
|
# Determine if a mangle 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".
|
|
#
|
|
havemanglechain() # $1 = name of chain
|
|
{
|
|
eval test \"\$exists_mangle_${1}\" = Yes
|
|
}
|
|
|
|
#
|
|
# Ensure that a mangle chain exists (create it if it doesn't)
|
|
#
|
|
ensuremanglechain() # $1 = chain name
|
|
{
|
|
havemanglechain $1 || createmanglechain $1
|
|
}
|
|
|
|
#
|
|
# 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 $@
|
|
}
|
|
|
|
#
|
|
# Create a rule to delete a chain if it exists
|
|
#
|
|
deletechain() # $1 = name of chain
|
|
{
|
|
save_command "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
|
|
}
|
|
|
|
#
|
|
# This function assumes that the TMP_DIR variable is set and that
|
|
# its value names an existing directory.
|
|
#
|
|
determine_zones()
|
|
{
|
|
local zone parent parents rest new_zone_file= r
|
|
|
|
merge_zone()
|
|
{
|
|
local z zones="$ZONES" merged=
|
|
|
|
if [ -n "$parents" ]; then
|
|
ZONES=
|
|
for z in $zones; do
|
|
if [ -z "$merged" ] && list_search $z $parents; then
|
|
ZONES="$ZONES $zone"
|
|
merged=Yes
|
|
fi
|
|
ZONES="$ZONES $z"
|
|
done
|
|
else
|
|
ZONES="$ZONES $zone"
|
|
fi
|
|
}
|
|
|
|
strip_file zones
|
|
|
|
ZONES=
|
|
IPV4_ZONES=
|
|
IPSEC_ZONES=
|
|
|
|
[ "$IPSECFILE" = zones ] && new_zone_file=Yes || test -n "${FW:=fw}"
|
|
|
|
while read zone type rest; do
|
|
expandv zone type
|
|
|
|
case $zone in
|
|
*:*)
|
|
parents=${zone#*:}
|
|
zone=${zone%:*}
|
|
[ -n "$zone" ] || fatal_error "Invalid nested zone syntax: :$parents"
|
|
parents=$(separate_list $parents)
|
|
eval ${zone}_parents=\"$parents\"
|
|
;;
|
|
*)
|
|
parents=
|
|
eval ${zone}_parents=
|
|
;;
|
|
esac
|
|
|
|
for parent in $parents; do
|
|
[ "$parent" = "$FW" ] && fatal_error "Sub-zones of the firewall zone are not allowed"
|
|
list_search $parent $ZONES || fatal_error "Parent zone not defined: $parent"
|
|
done
|
|
|
|
[ ${#zone} -gt 5 ] && fatal_error "Zone name longer than 5 characters: $zone"
|
|
|
|
case "$zone" in
|
|
[0-9*])
|
|
fatal_error "Illegal zone name \"$zone\" in zones file"
|
|
;;
|
|
all|none)
|
|
fatal_error "Reserved zone name \"$zone\" in zones file"
|
|
;;
|
|
esac
|
|
|
|
if [ -n "$new_zone_file" ]; then
|
|
case ${type:=ipv4} in
|
|
ipv4|IPv4|IPV4|plain|-)
|
|
list_search $zone $ZONES $FW && fatal_error "Zone $zone is defined more than once"
|
|
merge_zone
|
|
IPV4_ZONES="$IPV4_ZONES $zone"
|
|
;;
|
|
ipsec|IPSEC|ipsec4|IPSEC4)
|
|
list_search $zone $ZONES $FW && fatal_error "Zone $zone is defined more than once"
|
|
[ -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
|
|
merge_zone
|
|
IPSEC_ZONES="$IPSEC_ZONES $zone"
|
|
;;
|
|
firewall)
|
|
[ -n "$FW" ] && fatal_error "Only one firewall zone may be defined"
|
|
list_search $zone $ZONES && fatal_error "Zone $zone is defined more than once"
|
|
[ -n "$parents" ] && fatal_error "The firewall zone may not be nested"
|
|
for r in $rest; do
|
|
[ "x$r" = x- ] || fatal_error "OPTIONS not allowed on the firewall zone"
|
|
done
|
|
FW=$zone
|
|
;;
|
|
*)
|
|
fatal_error "Invalid Zone Type: $type"
|
|
;;
|
|
esac
|
|
|
|
eval ${zone}_type=$type
|
|
else
|
|
list_search $zone $ZONES $FW && fatal_error "Zone $zone is defined more than once"
|
|
ZONES="$ZONES $zone"
|
|
IPV4_ZONES="$IPV4_ZONES $zone"
|
|
eval ${zone}_type=ipv4
|
|
fi
|
|
done < $TMP_DIR/zones
|
|
|
|
[ -z "$ZONES" ] && fatal_error "No ipv4 or ipsec Zones Defined"
|
|
|
|
[ -z "$FW" ] && fatal_error "No Firewall Zone 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 an interface
|
|
#
|
|
snat_chain() # $1 = 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 [ -n "$KLUDGEFREE" ]; then
|
|
echo "-m iprange $@"
|
|
elif [ -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
|
|
{
|
|
#
|
|
# Note: There is a lot of unnecessary evaluation in this function just so my text
|
|
# editor (kate) doesn't get lost trying to follow the shell syntax for highlighting.
|
|
#
|
|
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]\])
|
|
eval temp='${1#*\[}'
|
|
eval temp='${temp%\]}'
|
|
eval setname='${1%\[*}'
|
|
while [ $temp -gt 1 ]; do
|
|
options="$options,$2"
|
|
temp=$(($temp - 1))
|
|
done
|
|
;;
|
|
*\[*\])
|
|
eval options='${1#*\[}'
|
|
eval options='${options%\]}'
|
|
eval setname='${1%\[*}'
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
|
|
echo "--set ${setname#+} $options"
|
|
}
|
|
|
|
#
|
|
# Horrible hack to work around an iptables limitation
|
|
#
|
|
physdev_echo()
|
|
{
|
|
if [ -n "$KLUDGEFREE" ]; then
|
|
echo -m physdev $@
|
|
elif [ -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 ${CONFDIR}/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 ${CONFDIR}/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
|
|
}
|
|
|
|
known_port() # $1 = port name
|
|
{
|
|
local port
|
|
|
|
for port in $ALL_PORTS ; do
|
|
if if_match $port $1 ; then
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
match_source_dev()
|
|
{
|
|
if [ -n "$BRIDGING" ]; then
|
|
known_port $1 && physdev_echo "--physdev-in $1" || echo -i $1
|
|
elif known_interface $1; then
|
|
echo -i $1
|
|
elif [ -n "$PHYSDEV_MATCH" ]; then
|
|
physdev_echo "--physdev-in $1"
|
|
else
|
|
echo -i $1
|
|
fi
|
|
}
|
|
|
|
match_dest_dev()
|
|
{
|
|
if [ -n "$BRIDGING" ]; then
|
|
known_port $1 && physdev_echo "--physdev-out $1" || echo -o $1
|
|
elif known_interface $1; then
|
|
echo -o $1
|
|
elif [ -n "$PHYSDEV_MATCH" ]; then
|
|
physdev_echo "--physdev-out $1"
|
|
else
|
|
echo -o $1
|
|
fi
|
|
}
|
|
|
|
verify_interface()
|
|
{
|
|
known_interface $1 || { [ -n "$BRIDGING" ] && known_port $1 ; }
|
|
}
|
|
|
|
#
|
|
# 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
|
|
#
|
|
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 && \
|
|
fatal_error "Invalid zone definition for zone $zone"
|
|
list_search $interface:0/0 $hosts && \
|
|
fatal_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
|
|
[ $VERBOSE -ge 1 ] && 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 || fatal_error "Invalid zone ($z) in record \"$r\""
|
|
fi
|
|
|
|
list_search $interface $ALL_INTERFACES && \
|
|
fatal_error "Duplicate Interface $interface"
|
|
|
|
wildcard=
|
|
|
|
case $interface in
|
|
*:*|+)
|
|
fatal_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|tcpflags|arp_filter|routefilter|maclist|logmartians|sourceroute|blacklist|proxyarp|nosmurfs|upnp|-)
|
|
;;
|
|
norfc1918)
|
|
indent >&3 << __EOF__
|
|
addr=\$(ip -f inet addr show $interface 2> /dev/null | grep 'inet\ ' | head -n1)
|
|
if [ -n "\$addr" ]; then
|
|
addr=\$(echo \$addr | sed 's/inet //;s/\/.*//;s/ peer.*//')
|
|
for network in 10.0.0.0/8 176.16.0.0/12 192.168.0.0/16; do
|
|
if in_network \$addr \$network; then
|
|
startup_error "The 'norfc1918' option has been specified on an interface with an RFC 1918 address. Interface:$interface"
|
|
fi
|
|
done
|
|
fi
|
|
__EOF__
|
|
;;
|
|
arp_ignore=*)
|
|
eval ${iface}_arp_ignore=${option#*=}
|
|
;;
|
|
arp_ignore)
|
|
eval ${iface}_arp_ignore=1
|
|
;;
|
|
detectnets)
|
|
[ -n "$wildcard" ] && \
|
|
fatal_error "The \"detectnets\" option may not be used with a wild-card interface"
|
|
[ -n "$EXPORT" ] && \
|
|
fatal_error "'detectnets' not permitted with the -e run-line option"
|
|
;;
|
|
routeback)
|
|
[ -n "$z" ] || fatal_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" ] && fatal_error "No Interfaces Defined"
|
|
}
|
|
|
|
#
|
|
# Process the providers file
|
|
#
|
|
setup_providers()
|
|
{
|
|
local table number mark duplicate interface gateway options provider address copy route loose addresses rulenum rulebase echobin=$(mywhich echo) balance save_indent="$INDENT" mask= first=Yes
|
|
|
|
copy_table() {
|
|
indent >&3 << __EOF__
|
|
ip route show table $duplicate | while read net route; do
|
|
case \$net in
|
|
default|nexthop)
|
|
;;
|
|
*)
|
|
run_ip route add table $number \$net \$route"
|
|
;;
|
|
esac
|
|
done
|
|
__EOF__
|
|
}
|
|
|
|
copy_and_edit_table() {
|
|
indent >&3 << __EOF__
|
|
ip route show table $duplicate | while read net route; do
|
|
case \$net in
|
|
default|nexthop)
|
|
;;
|
|
*)
|
|
case \$(find_device \$route) in
|
|
`echo $copy\) | sed 's/ /|/g'`
|
|
run_ip route add table $number \$net \$route
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
done
|
|
|
|
__EOF__
|
|
}
|
|
|
|
balance_default_route() # $1 = weight
|
|
{
|
|
balance=yes
|
|
|
|
if [ -n "$first"]; then
|
|
if [ "x$gateway" = xdetect ] ; then
|
|
save_command "DEFAULT_ROUTE=\"nexthop via \$gateway dev $interface weight $1\""
|
|
else
|
|
save_command "DEFAULT_ROUTE=\"nexthop via $gateway dev $interface weight $1\""
|
|
fi
|
|
else
|
|
if [ "x$gateway" = xdetect ] ; then
|
|
save_command "DEFAULT_ROUTE=\"\$DEFAULT_ROUTE nexthop via \$gateway dev $interface weight $1\""
|
|
else
|
|
save_command "DEFAULT_ROUTE=\"\$DEFAULT_ROUTE nexthop via $gateway dev $interface weight $1\""
|
|
fi
|
|
fi
|
|
}
|
|
|
|
add_a_provider() {
|
|
local t n iface option first=Yes
|
|
|
|
[ -n "$MANGLE_ENABLED" ] || fatal_error "Providers require mangle support in your kernel and iptables"
|
|
|
|
for t in $PROVIDERS local main default unspec; do
|
|
if [ "$t" = "$table" ]; then
|
|
fatal_error "Duplicate Provider: $table, provider: \"$provider\""
|
|
fi
|
|
|
|
eval n=\$${t}_number
|
|
#
|
|
# The following is because the %$#@ shell doesn't accept hex numbers in '-eq' tests
|
|
#
|
|
if [ $(($n)) -eq $(($number)) ]; then
|
|
fatal_error "Duplicate Provider number: $number, provider: \"$provider\""
|
|
fi
|
|
done
|
|
|
|
eval ${table}_number=$number
|
|
|
|
indent >&3 << __EOF__
|
|
#
|
|
# Add Provider $table ($number)
|
|
#
|
|
__EOF__
|
|
save_command "qt ip route flush table $number"
|
|
|
|
if [ "x${duplicate:=-}" != x- ]; then
|
|
if [ "x${copy:=-}" != "x-" ]; then
|
|
if [ "x${copy}" = xnone ]; then
|
|
copy=$interface
|
|
else
|
|
copy="$interface $(separate_list $copy)"
|
|
fi
|
|
copy_and_edit_table
|
|
else
|
|
copy_table
|
|
fi
|
|
fi
|
|
|
|
if [ "x$gateway" = xdetect ] ; then
|
|
indent >&3 << __EOF__
|
|
gateway=\$(detect_gateway $interface)
|
|
|
|
if [ -n "\$gateway" ]; then
|
|
run_ip route replace \$gateway src \$(find_first_interface_address $interface) dev $interface table $number
|
|
run_ip route add default via \$gateway dev $interface table $number
|
|
else
|
|
fatal_error "Unable to detect the gateway through interface $interface"
|
|
fi
|
|
|
|
__EOF__
|
|
else
|
|
indent >&3 << __EOF__
|
|
run_ip route replace $gateway src \$(find_first_interface_address $interface) dev $interface table $number
|
|
run_ip route add default via $gateway dev $interface table $number
|
|
__EOF__
|
|
fi
|
|
|
|
if [ x${mark} != x- ]; then
|
|
verify_mark $mark
|
|
|
|
if [ $(($mark)) -lt 256 ]; then
|
|
if [ -n "$HIGH_ROUTE_MARKS" ]; then
|
|
fatal_error "Invalid Mark Value ($mark) with HIGH_ROUTE_MARKS=Yes"
|
|
fi
|
|
elif [ -z "$HIGH_ROUTE_MARKS" ]; then
|
|
fatal_error "Invalid Mark Value ($mark) with HIGH_ROUTE_MARKS=No"
|
|
fi
|
|
|
|
eval ${table}_mark=$mark
|
|
|
|
save_command "qt ip rule del fwmark $mark"
|
|
save_command "run_ip rule add fwmark $mark pref $((10000 + $mark)) table $number"
|
|
fi
|
|
|
|
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)
|
|
[ x${mark} = x- ] && fatal_error "The 'track' option requires a numeric value in the MARK column - Provider \"$provider\""
|
|
eval ${iface}_routemark=$mark
|
|
ROUTEMARK_INTERFACES="$ROUTEMARK_INTERFACES $interface"
|
|
;;
|
|
balance=*)
|
|
balance_default_route ${option#*=}
|
|
;;
|
|
balance)
|
|
balance_default_route 1
|
|
;;
|
|
loose)
|
|
loose=Yes
|
|
;;
|
|
*)
|
|
error_message "WARNING: Invalid option ($option) ignored in provider \"$provider\""
|
|
;;
|
|
esac
|
|
first=
|
|
done
|
|
|
|
rulenum=0
|
|
|
|
if [ -z "$loose" ]; then
|
|
rulebase=$(( 20000 + ( 256 * ($number-1) ) ))
|
|
indent >&3 << __EOF__
|
|
|
|
rulenum=0
|
|
|
|
find_interface_addresses $interface | while read address; do
|
|
qt ip rule del from \$address
|
|
run_ip rule add from \$address pref \$(( $rulebase + \$rulenum )) table $number
|
|
rulenum=\$((\$rulenum + 1))
|
|
done
|
|
__EOF__
|
|
else
|
|
indent >&3 << __EOF__
|
|
|
|
find_interface_addresses $interface | while read address; do
|
|
qt ip rule del from \$address
|
|
done
|
|
__EOF__
|
|
fi
|
|
|
|
indent >&3 << __EOF__
|
|
|
|
progress_message " Provider $table ($number) Added"
|
|
|
|
__EOF__
|
|
|
|
}
|
|
|
|
verify_provider()
|
|
{
|
|
local p n
|
|
|
|
for p in $PROVIDERS main; do
|
|
[ "$p" = "$1" ] && return 0
|
|
eval n=\$${p}_number}
|
|
[ "$n" = "$1" ] && return 0
|
|
done
|
|
|
|
fatal_error "Unknown provider $1 in route rule \"$rule\""
|
|
}
|
|
|
|
add_an_rtrule()
|
|
{
|
|
verify_provider $provider
|
|
|
|
[ "x$source" = x- ] && source=
|
|
[ "x$dest" = x- ] && dest= || dest="to $dest"
|
|
|
|
[ -n "${source}${dest}" ] || fatal_error "You must specify either the source or destination in an rt rule: \"$rule\""
|
|
|
|
[ -n "$source" ] && case $source in
|
|
*:*)
|
|
source="iif ${source%:*} from ${source#*:}"
|
|
;;
|
|
*.*.*)
|
|
source="from $source"
|
|
;;
|
|
*)
|
|
source="iif $source"
|
|
;;
|
|
esac
|
|
|
|
case "$priority" in
|
|
[0-9][0-9][0-9][0-9]|[0-9][0-9][0-9][0-9][0-9])
|
|
;;
|
|
*)
|
|
fatal_error "Invalid priority ($priority) in rule \"$rule\""
|
|
;;
|
|
esac
|
|
|
|
priority="priority $priority"
|
|
|
|
save_command "qt ip rule del $source $dest $priority"
|
|
save_command "run_ip rule add $source $dest $priority table $provider"
|
|
|
|
progress_message "Routing rule \"$rule\" $DONE"
|
|
}
|
|
|
|
local_number=255
|
|
main_number=254
|
|
default_number=253
|
|
unspec_number=0
|
|
|
|
strip_file providers $1
|
|
|
|
if [ -s $TMP_DIR/providers ]; then
|
|
DEFAULT_ROUTE=
|
|
balance=
|
|
|
|
progress_message2 "$DOING $1..."
|
|
save_command
|
|
save_command "if [ -z \"\$NOROUTES\" ]; then"
|
|
INDENT="$INDENT "
|
|
save_progress_message "Adding Providers..."
|
|
save_command "DEFAULT_ROUTE="
|
|
|
|
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 $DONE"
|
|
done < $TMP_DIR/providers
|
|
|
|
if [ -n "$PROVIDERS" ]; then
|
|
if [ -n "$balance" ]; then
|
|
save_command "run_ip route replace default scope global \$DEFAULT_ROUTE"
|
|
save_command "progress_message Default route \$DEFAULT_ROUTE Added"
|
|
fi
|
|
|
|
cat >&3 << __EOF__
|
|
${INDENT}cat > /etc/iproute2/rt_tables <<EOF
|
|
#
|
|
# reserved values
|
|
#
|
|
255 local
|
|
254 main
|
|
253 default
|
|
0 unspec
|
|
#
|
|
# local
|
|
#
|
|
EOF
|
|
__EOF__
|
|
for table in $PROVIDERS; do
|
|
eval number=\$${table}_number
|
|
indent >&3 << __EOF__
|
|
\${echobin:-echo} -e "$number\t$table" >> /etc/iproute2/rt_tables
|
|
__EOF__
|
|
done
|
|
|
|
f=$(find_file route_rules)
|
|
|
|
if [ -f $f ]; then
|
|
strip_file route_rules $f
|
|
|
|
if [ -s $TMP_DIR/route_rules ]; then
|
|
progress_message2 "$DOING $f..."
|
|
|
|
save_command
|
|
|
|
while read source dest provider priority; do
|
|
expandv source dest provider priority
|
|
rule="$source $dest $priority $provider"
|
|
add_an_rtrule
|
|
done < $TMP_DIR/route_rules
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
save_command "run_ip route flush cache"
|
|
INDENT="$save_indent"
|
|
save_command "fi"
|
|
save_command
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Validate the zone names and options in the hosts file
|
|
#
|
|
validate_hosts_file() {
|
|
local z hosts options r interface host option zports
|
|
|
|
check_bridge_port()
|
|
{
|
|
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 || fatal_error "Invalid zone ($z) in record \"$r\""
|
|
|
|
case $hosts in
|
|
*:*)
|
|
|
|
interface=${hosts%%:*}
|
|
iface=$(chain_base $interface)
|
|
|
|
list_search $interface $ALL_INTERFACES || \
|
|
fatal_error "Unknown interface ($interface) in record \"$r\""
|
|
|
|
hosts=${hosts#*:}
|
|
;;
|
|
*)
|
|
fatal_error "Invalid HOST(S) column contents: $hosts"
|
|
;;
|
|
esac
|
|
|
|
eval zports=\$${z}_ports
|
|
|
|
for host in $(separate_list $hosts); do
|
|
if [ -n "$BRIDGING" ]; then
|
|
case $host in
|
|
*:*)
|
|
known_interface ${host%:*} && \
|
|
fatal_error "Bridged interfaces may not be defined in ${CONFDIR}/interfaces: $host"
|
|
check_bridge_port ${host%%:*}
|
|
;;
|
|
*.*.*)
|
|
;;
|
|
*+|+*)
|
|
eval ${z}_is_complex=Yes
|
|
;;
|
|
*)
|
|
known_interface $host && \
|
|
fatal_error "Bridged interfaces may not be defined in ${CONFDIR}/interfaces: $host"
|
|
check_bridge_port $host
|
|
;;
|
|
esac
|
|
else
|
|
case $host in
|
|
*.*.*)
|
|
;;
|
|
*+)
|
|
eval ${z}_is_complex=Yes
|
|
;;
|
|
*)
|
|
fatal_error "BRIDGING=Yes is needed for this zone definition: $r"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
for option in $(separate_list $options) ; do
|
|
case $option in
|
|
norfc1918|blacklist|maclist|tcpflags|nosmurfs|-)
|
|
;;
|
|
ipsec)
|
|
[ -n "$POLICY_MATCH" ] || \
|
|
fatal_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)
|
|
eval ${z}_routeback=\"$interface:$host \$${z}_routeback\"
|
|
;;
|
|
*)
|
|
error_message "WARNING: Invalid option ($option) in record \"$r\""
|
|
;;
|
|
esac
|
|
done
|
|
done
|
|
|
|
[ -n "$zports" ] && eval ${z}_ports=\"$zports\"
|
|
|
|
done < $TMP_DIR/hosts
|
|
|
|
[ -n "$ALL_PORTS" ] && progress_message2 " 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
|
|
local parents
|
|
|
|
print_policy() # $1 = source zone, $2 = destination zone
|
|
{
|
|
[ $1 = $2 ] || \
|
|
[ $1 = all ] || \
|
|
[ $2 = all ] || \
|
|
progress_message " Policy for $1 to $2 is $policy using chain $chain"
|
|
}
|
|
|
|
ALL_POLICY_CHAINS=
|
|
|
|
for zone in $ZONES $FW; do
|
|
chain=${zone}2${zone}
|
|
eval ${chain}_is_policy=Yes
|
|
eval ${chain}_is_optional=Yes
|
|
eval ${chain}_policy=ACCEPT
|
|
eval ${chain}_policychain=$chain
|
|
ALL_POLICY_CHAINS="$ALL_POLICY_CHAINS $chain"
|
|
|
|
if [ -n "$IMPLICIT_CONTINUE" ]; then
|
|
eval parents=\$${zone}_parents
|
|
if [ -n "$parents" ]; then
|
|
for zone1 in $ZONES $FW; do
|
|
chain=${zone}2${zone1}
|
|
eval ${chain}_is_policy=Yes
|
|
eval ${chain}_is_optional=Yes
|
|
eval ${chain}_policy=CONTINUE
|
|
eval ${chain}_policychain=$chain
|
|
ALL_POLICY_CHAINS="$ALL_POLICY_CHAINS $chain"
|
|
chain=${zone1}2${zone}
|
|
eval ${chain}_is_policy=Yes
|
|
eval ${chain}_is_optional=Yes
|
|
eval ${chain}_policy=CONTINUE
|
|
eval ${chain}_policychain=$chain
|
|
ALL_POLICY_CHAINS="$ALL_POLICY_CHAINS $chain"
|
|
done
|
|
fi
|
|
fi
|
|
done
|
|
|
|
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
|
|
fatal_error "Undefined zone $client"
|
|
fi
|
|
esac
|
|
|
|
case "$server" in
|
|
all|ALL)
|
|
serverwild=Yes
|
|
;;
|
|
*)
|
|
if ! validate_zone $server; then
|
|
fatal_error "Undefined zone $server"
|
|
fi
|
|
esac
|
|
|
|
case $policy in
|
|
ACCEPT|REJECT|DROP|CONTINUE|QUEUE)
|
|
;;
|
|
NONE)
|
|
[ "$client" = "$FW" -o "$server" = "$FW" ] && \
|
|
fatal_error " $client $server $policy $loglevel $synparams: NONE policy not allowed to/from the $FW zone"
|
|
|
|
[ -n "$clientwild" -o -n "$serverwild" ] && \
|
|
fatal_error " $client $server $policy $loglevel $synparams: NONE policy not allowed with \"all\""
|
|
;;
|
|
*)
|
|
fatal_error "Invalid policy $policy"
|
|
;;
|
|
esac
|
|
|
|
chain=${client}2${server}
|
|
|
|
if is_policy_chain $chain ; then
|
|
if eval test -n \"\$${chain}_is_optional\" ; then
|
|
eval ${chain}_is_optional=
|
|
else
|
|
fatal_error "Duplicate policy: $client $server $policy"
|
|
fi
|
|
fi
|
|
|
|
[ "x$loglevel" = "x-" ] && loglevel=
|
|
[ "x$synparams" = "x-" ] && synparams=
|
|
|
|
[ $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
|
|
|
|
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 -- if we are compiling a script and 'detect' is specified for an interface
|
|
# the function returns nothing for that interface
|
|
#
|
|
find_broadcasts() {
|
|
for interface in $ALL_INTERFACES; do
|
|
eval bcast=\$$(chain_base $interface)_broadcast
|
|
if [ "x$bcast" != "xdetect" -a "x${bcast}" != "x-" ]; then
|
|
echo $(separate_list $bcast)
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# Find interfaces with BROADCAST=detect -- Only returns information if we are compiling a script
|
|
#
|
|
find_bcastdetect_interfaces() {
|
|
for interface in $ALL_INTERFACES; do
|
|
eval bcast=\$$(chain_base $interface)_broadcast
|
|
[ "x$bcast" = "xdetect" ] && echo $interface
|
|
done
|
|
}
|
|
|
|
#
|
|
# 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
|
|
}
|
|
|
|
#
|
|
# Set /proc/sys/net/ipv4/ip_forward based on $IP_FORWARDING
|
|
#
|
|
setup_forwarding() {
|
|
|
|
progress_message2 "Compiling IP Forwarding..."
|
|
|
|
case "$IP_FORWARDING" in
|
|
[Oo][Nn])
|
|
save_progress_message "IP Forwarding Enabled"
|
|
save_command "echo 1 > /proc/sys/net/ipv4/ip_forward"
|
|
;;
|
|
[Oo][Ff][Ff])
|
|
save_progress_message "IP Forwarding Disabled!"
|
|
save_command "echo 0 > /proc/sys/net/ipv4/ip_forward"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
#
|
|
# 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#*:}
|
|
source_range=$(source_ip_range $networks)
|
|
dest_range=$(dest_ip_range $networks)
|
|
run_iptables $1 INPUT -i $interface $source_range -j ACCEPT
|
|
[ -z "$ADMINISABSENTMINDED" ] && \
|
|
run_iptables $1 OUTPUT -o $interface $dest_range -j ACCEPT
|
|
|
|
matched=
|
|
|
|
if list_search $host $source ; then
|
|
run_iptables $1 FORWARD -i $interface $source_range -j ACCEPT
|
|
matched=Yes
|
|
fi
|
|
|
|
if list_search $host $dest ; then
|
|
run_iptables $1 FORWARD -o $interface $dest_range -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#*:}
|
|
do_iptables -A INPUT -i $interface $(source_ip_range $networks) -j ACCEPT
|
|
do_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#*:}
|
|
do_iptables -D INPUT -i $interface $(source_ip_range $networks) -j ACCEPT
|
|
do_iptables -D OUTPUT -o $interface $(dest_ip_range $networks) -j ACCEPT
|
|
done
|
|
}
|
|
|
|
#
|
|
# Set up ipsec tunnels
|
|
#
|
|
setup_tunnels() # $1 = name of tunnels file
|
|
{
|
|
local inchain
|
|
local outchain
|
|
local source
|
|
local dest
|
|
|
|
setup_one_ipsec() # $1 = Tunnel Kind $2 = gateway zones
|
|
{
|
|
local kind=$1 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 -j ACCEPT
|
|
addrule2 $outchain -p 50 $dest -j ACCEPT
|
|
|
|
if [ -z "$noah" ]; then
|
|
run_iptables -A $inchain -p 51 $source -j ACCEPT
|
|
run_iptables -A $outchain -p 51 $dest -j ACCEPT
|
|
fi
|
|
|
|
run_iptables -A $outchain -p udp $dest --dport 500 $options
|
|
|
|
if [ $kind = ipsec ]; then
|
|
run_iptables -A $inchain -p udp $source --dport 500 $options
|
|
else
|
|
run_iptables -A $inchain -p udp $source --dport 500 $options
|
|
run_iptables -A $inchain -p udp $source --dport 4500 $options
|
|
fi
|
|
|
|
for z in $(separate_list $2); do
|
|
if validate_zone $z; then
|
|
if [ -z "$POLICY_MATCH" ]; then
|
|
addrule ${z}2${FW} -p 50 $source -j ACCEPT
|
|
addrule ${FW}2${z} -p 50 $dest -j ACCEPT
|
|
if [ -z "$noah" ]; then
|
|
addrule ${z}2${FW} -p 51 $source -j ACCEPT
|
|
addrule ${FW}2${z} -p 51 $dest -j ACCEPT
|
|
fi
|
|
fi
|
|
if [ $kind = ipsec ]; then
|
|
addrule ${z}2${FW} -p udp $source --dport 500 $options
|
|
addrule ${FW}2${z} -p udp $dest --dport 500 $options
|
|
else
|
|
addrule ${z}2${FW} -p udp $source --dport 500 $options
|
|
addrule ${FW}2${z} -p udp $dest --dport 500 $options
|
|
addrule ${z}2${FW} -p udp $source --dport 4500 $options
|
|
addrule ${FW}2${z} -p udp $dest --dport 4500 $options
|
|
fi
|
|
else
|
|
fatal_error "Invalid gateway zone ($z) -- Tunnel \"$tunnel\""
|
|
fi
|
|
done
|
|
|
|
progress_message_and_save " IPSEC tunnel to $gateway defined."
|
|
}
|
|
|
|
setup_one_other() # $1 = TYPE, $2 = protocol
|
|
{
|
|
addrule2 $inchain -p $2 $source -j ACCEPT
|
|
addrule2 $outchain -p $2 $dest -j ACCEPT
|
|
|
|
progress_message_and_save " $1 tunnel to $gateway compiled."
|
|
}
|
|
|
|
setup_pptp_client()
|
|
{
|
|
addrule2 $outchain -p 47 $dest -j ACCEPT
|
|
addrule2 $inchain -p 47 $source -j ACCEPT
|
|
addrule2 $outchain -p tcp --dport 1723 $dest -j ACCEPT
|
|
|
|
progress_message_and_save " PPTP tunnel to $gateway defined."
|
|
}
|
|
|
|
setup_pptp_server()
|
|
{
|
|
addrule2 $inchain -p 47 $source -j ACCEPT
|
|
addrule2 $outchain -p 47 $dest -j ACCEPT
|
|
addrule2 $inchain -p tcp --dport 1723 $source -j ACCEPT
|
|
|
|
progress_message_and_save " PPTP server defined."
|
|
}
|
|
|
|
setup_one_openvpn() # $1 = kind[:port]
|
|
{
|
|
local protocol=udp
|
|
local p=1194
|
|
|
|
case $1 in
|
|
*:*:*)
|
|
protocol=${1%:*}
|
|
protocol=${protocol#*:}
|
|
p=${1##*:}
|
|
;;
|
|
*:tcp|*:udp|*:TCP|*:UDP)
|
|
protocol=${1#*:}
|
|
;;
|
|
*:*)
|
|
p=${1#*:}
|
|
;;
|
|
esac
|
|
|
|
addrule2 $inchain -p $protocol $source --dport $p -j ACCEPT
|
|
addrule2 $outchain -p $protocol $dest --dport $p -j ACCEPT
|
|
|
|
progress_message_and_save " OPENVPN tunnel to $gateway:$protocol:$p defined."
|
|
}
|
|
|
|
setup_one_openvpn_server() # $1 = kind[:port]
|
|
{
|
|
local protocol=udp
|
|
local p=1194
|
|
|
|
case $1 in
|
|
*:*:*)
|
|
protocol=${1%:*}
|
|
protocol=${protocol#*:}
|
|
p=${1##*:}
|
|
;;
|
|
*:tcp|*:udp|*:TCP|*:UDP)
|
|
protocol=${1#*:}
|
|
;;
|
|
*:*)
|
|
p=${1#*:}
|
|
;;
|
|
esac
|
|
|
|
addrule2 $inchain -p $protocol $source --dport $p -j ACCEPT
|
|
addrule2 $outchain -p $protocol $dest --sport $p -j ACCEPT
|
|
|
|
progress_message_and_save " OPENVPN server tunnel from $gateway:$protocol:$p defined."
|
|
}
|
|
|
|
setup_one_openvpn_client() # $1 = kind[:port]
|
|
{
|
|
local protocol=udp
|
|
local p=1194
|
|
|
|
case $1 in
|
|
*:*:*)
|
|
protocol=${1%:*}
|
|
protocol=${protocol#*:}
|
|
p=${1##*:}
|
|
;;
|
|
*:tcp|*:udp|*:TCP|*:UDP)
|
|
protocol=${1#*:}
|
|
;;
|
|
*:*)
|
|
p=${1#*:}
|
|
;;
|
|
esac
|
|
|
|
addrule2 $inchain -p $protocol $source --sport $p -j ACCEPT
|
|
addrule2 $outchain -p $protocol $dest --dport $p -j ACCEPT
|
|
|
|
progress_message_and_save " OPENVPN client tunnel to $gateway:$protocol:$p defined."
|
|
}
|
|
|
|
setup_one_generic() # $1 = kind:protocol[:port]
|
|
{
|
|
local protocol
|
|
local p=
|
|
|
|
case $1 in
|
|
*:*:*)
|
|
p=${1##*:}
|
|
protocol=${1%:*}
|
|
protocol=${protocol#*:}
|
|
;;
|
|
*:*)
|
|
protocol=${1#*:}
|
|
;;
|
|
*)
|
|
protocol=udp
|
|
p=5000
|
|
;;
|
|
esac
|
|
|
|
p=${p:+--dport $p}
|
|
|
|
addrule2 $inchain -p $protocol $source $p -j ACCEPT
|
|
addrule2 $outchain -p $protocol $dest $p -j ACCEPT
|
|
|
|
progress_message_and_save " 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}
|
|
source=$(source_ip_range $gateway)
|
|
dest=$(dest_ip_range $gateway)
|
|
|
|
case $kind in
|
|
ipsec|IPSEC|ipsec:*|IPSEC:*)
|
|
setup_one_ipsec $kind $z1
|
|
;;
|
|
ipsecnat|IPSECNAT|ipsecnat:*|IPSECNAT:*)
|
|
setup_one_ipsec $kind $z1
|
|
;;
|
|
ipip|IPIP)
|
|
setup_one_other IPIP 4
|
|
;;
|
|
gre|GRE)
|
|
setup_one_other GRE 47
|
|
;;
|
|
6to4|6TO4)
|
|
setup_one_other 6to4 41
|
|
;;
|
|
pptpclient|PPTPCLIENT)
|
|
setup_pptp_client
|
|
;;
|
|
pptpserver|PPTPSERVER)
|
|
setup_pptp_server
|
|
;;
|
|
openvpn|OPENVPN|openvpn:*|OPENVPN:*)
|
|
setup_one_openvpn $kind
|
|
;;
|
|
openvpnclient|OPENVPNCLIENT|openvpnclient:*|OPENVPNCLIENT:*)
|
|
setup_one_openvpn_client $kind
|
|
;;
|
|
openvpnserver|OPENVPNSERVER|openvpnserver:*|OPENVPNSERVER:*)
|
|
setup_one_openvpn_server $kind
|
|
;;
|
|
generic:*|GENERIC:*)
|
|
setup_one_generic $kind
|
|
;;
|
|
*)
|
|
error_message "WARNING: Tunnels of type $kind are not supported:" \
|
|
"Tunnel \"$tunnel\" Ignored"
|
|
;;
|
|
esac
|
|
else
|
|
error_message "ERROR: 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
|
|
ensurechain $1
|
|
run_iptables -I $1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss $2
|
|
fi
|
|
}
|
|
#
|
|
# Set up rules to set MSS to and/or from zone "$zone"
|
|
#
|
|
set_mss() # $1 = MSS value, $2 = _in, _out or ""
|
|
{
|
|
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
|
|
}
|
|
|
|
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_message2 "$DOING IPSEC..."
|
|
;;
|
|
*)
|
|
f=$IPSECFILE
|
|
strip_file $f
|
|
progress_message2 "$DOING $f..."
|
|
using_ipsec=Yes
|
|
;;
|
|
esac
|
|
|
|
while read zone type options in_options out_options mss; do
|
|
expandv zone type options in_options out_options mss
|
|
|
|
if [ -n "$using_ipsec" ]; then
|
|
validate_zone1 $zone || fatal_error "Unknown zone: $zone"
|
|
fi
|
|
|
|
if [ -n "$type" ]; then
|
|
if [ -n "$using_ipsec" ]; then
|
|
case $type 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
|
|
eval ${zone}_type=ipsec4
|
|
;;
|
|
*)
|
|
fatal_error "Invalid IPSEC column contents"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
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
|
|
save_command "[ -n \"\$NOROUTES\" ] || run_ip route replace $address dev $interface"
|
|
[ -n "$persistent" ] && haveroute=yes
|
|
fi
|
|
|
|
indent >&3 << __EOF__
|
|
if ! arp -i $external -Ds $address $external pub; then
|
|
fatal_error "Command \"arp -i $external -Ds $address $external pub\" failed"
|
|
fi
|
|
|
|
progress_message " Host $address connected to $interface added to ARP on $external"
|
|
|
|
__EOF__
|
|
echo $address $interface $external $haveroute >> $STATEDIR/proxyarp
|
|
|
|
progress_message " Host $address connected to $interface added to ARP on $external"
|
|
}
|
|
|
|
> $STATEDIR/proxyarp
|
|
|
|
save_progress_message "Setting up 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 || \
|
|
save_command "echo 0 > /proc/sys/net/ipv4/conf/$interface/proxy_arp"
|
|
done
|
|
|
|
for interface in $setlist; do
|
|
save_command "echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp"
|
|
done
|
|
|
|
interfaces=$(find_interfaces_by_option proxyarp)
|
|
|
|
for interface in $interfaces; do
|
|
indent >&3 << __EOF__
|
|
if [ -f /proc/sys/net/ipv4/conf/$interface/proxy_arp ] ; then
|
|
echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp
|
|
else
|
|
error_message "WARNING: Unable to enable proxy ARP on $interface"
|
|
fi
|
|
|
|
__EOF__
|
|
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=
|
|
|
|
create_mac_chain()
|
|
{
|
|
case $MACLIST_TABLE in
|
|
filter)
|
|
createchain $1 no
|
|
;;
|
|
*)
|
|
createmanglechain $1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
have_mac_chain()
|
|
{
|
|
local result
|
|
|
|
case $MACLIST_TABLE in
|
|
filter)
|
|
havechain $1 && result=0 || result=1
|
|
;;
|
|
*)
|
|
havemanglechain $1 && result=0 || result=1
|
|
;;
|
|
esac
|
|
|
|
return $result
|
|
}
|
|
#
|
|
# 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 "$DOING MAC Verification on $maclist_interfaces..."
|
|
#
|
|
# Create chains.
|
|
#
|
|
for interface in $maclist_interfaces; do
|
|
chain=$(mac_chain $interface)
|
|
create_mac_chain $chain
|
|
#
|
|
# If we're using the mangle table and the interface is DHCP-enabled then we need to accept DHCP broadcasts from 0.0.0.0
|
|
#
|
|
if [ $MACLIST_TABLE = mangle ] && interface_has_option $interface dhcp; then
|
|
run_iptables -t mangle -A $chain -s 0.0.0.0 -d 255.255.255.255 -p udp --dport 67:68 -j RETURN
|
|
fi
|
|
|
|
if [ -n "$MACLIST_TTL" ]; then
|
|
chain1=$(macrecent_target $interface)
|
|
create_mac_chain $chain1
|
|
run_iptables -A $chain -t $MACLIST_TABLE -m recent --rcheck --seconds $MACLIST_TTL --name $chain -j RETURN
|
|
run_iptables -A $chain -t $MACLIST_TABLE -j $chain1
|
|
run_iptables -A $chain -t $MACLIST_TABLE -m recent --update --name $chain -j RETURN
|
|
run_iptables -A $chain -t $MACLIST_TABLE -m recent --set --name $chain
|
|
fi
|
|
done
|
|
|
|
#
|
|
# Process the maclist file producing the verification rules
|
|
#
|
|
while read disposition interface mac addresses; do
|
|
expandv disposition interface mac addresses
|
|
|
|
level=
|
|
|
|
case $disposition in
|
|
ACCEPT:*)
|
|
level=${disposition#*:}
|
|
disposition=ACCEPT
|
|
target=RETURN
|
|
;;
|
|
ACCEPT)
|
|
target=RETURN
|
|
;;
|
|
REJECT:*)
|
|
[ $MACLIST_TABLE = mangle ] && fatal_error "DISPOSITION = REJECT is incompatible with MACLIST_TABLE=mangle"
|
|
target=reject
|
|
disposition=REJECT
|
|
;;
|
|
REJECT)
|
|
[ $MACLIST_TABLE = mangle ] && fatal_error "DISPOSITION = REJECT is incompatible with MACLIST_TABLE=mangle"
|
|
target=reject
|
|
;;
|
|
DROP:*)
|
|
level=${disposition#*:}
|
|
disposition=DROP
|
|
target=DROP
|
|
;;
|
|
DROP)
|
|
target=DROP
|
|
;;
|
|
*)
|
|
addresses="$mac"
|
|
mac="$interface"
|
|
interface="$disposition"
|
|
disposition=ACCEPT
|
|
target=RETURN
|
|
;;
|
|
esac
|
|
|
|
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 ! have_mac_chain $chain ; then
|
|
fatal_error "No hosts on $interface have the maclist option specified"
|
|
fi
|
|
|
|
if [ x${mac:=-} = x- ]; then
|
|
if [ -z "$addresses" ]; then
|
|
fatal_error "You must specify a MAC address or an IP address"
|
|
else
|
|
macpart=
|
|
fi
|
|
else
|
|
macpart=$(mac_match $mac)
|
|
fi
|
|
|
|
if [ -z "$addresses" ]; then
|
|
[ -n "$level" ] && \
|
|
log_rule_limit $level $chain $(mac_chain $interface) $disposition "$LOGLIMIT" "" -A -t $MACLIST_TABLE $macpart $physdev_part
|
|
run_iptables -A $chain -t $MACLIST_TABLE $macpart $physdev_part -j $target
|
|
else
|
|
for address in $(separate_list $addresses) ; do
|
|
[ -n "$level" ] && \
|
|
log_rule_limit $level $chain $(mac_chain $interface) $disposition "$LOGLIMIT" "" -A -t $MACLIST_TABLE $macpart -s $address $physdev_part
|
|
run_iptables2 -A $chain -t $MACLIST_TABLE $macpart -s $address $physdev_part -j $target
|
|
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)
|
|
|
|
if [ -n "$MACLIST_LOG_LEVEL" -o $MACLIST_DISPOSITION != ACCEPT ]; then
|
|
indent >&3 << __EOF__
|
|
|
|
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 -t $MACLIST_TABLE -A $chain -s \$address -d \$broadcast -j RETURN
|
|
fi
|
|
|
|
run_iptables -t $MACLIST_TABLE -A $chain -s \$address -d 255.255.255.255 -j RETURN
|
|
run_iptables -t $MACLIST_TABLE -A $chain -s \$address -d 224.0.0.0/4 -j RETURN
|
|
done
|
|
|
|
__EOF__
|
|
fi
|
|
|
|
if [ -n "$MACLIST_LOG_LEVEL" ]; then
|
|
log_rule_limit $MACLIST_LOG_LEVEL $chain $(mac_chain $interface) $MACLIST_DISPOSITION "$LOGLIMIT" "" -A -t $MACLIST_TABLE
|
|
fi
|
|
|
|
if [ $MACLIST_DISPOSITION != ACCEPT ]; then
|
|
run_iptables -A $chain -t $MACLIST_TABLE -j $maclist_target
|
|
fi
|
|
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#*:}
|
|
case $MACLIST_TABLE in
|
|
filter)
|
|
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
|
|
;;
|
|
*)
|
|
run_iptables -t mangle -A PREROUTING -i $interface $(match_source_hosts $hosts) -m state --state NEW \
|
|
$policy -j $(mac_chain $interface)
|
|
;;
|
|
esac
|
|
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
|
|
}
|
|
|
|
setup_syn_flood_chains()
|
|
{
|
|
for chain in $ALL_POLICY_CHAINS; do
|
|
eval loglevel=\$${chain}_loglevel
|
|
eval synparams=\$${chain}_synparams
|
|
|
|
[ -n "$synparams" ] && setup_syn_flood_chain $chain $synparams $loglevel
|
|
done
|
|
}
|
|
|
|
#
|
|
# Delete existing Proxy ARP
|
|
#
|
|
delete_proxy_arp() {
|
|
indent >&3 << __EOF__
|
|
if [ -f \${VARDIR}/proxyarp ]; then
|
|
while read address interface external haveroute; do
|
|
qt arp -i \$external -d \$address pub
|
|
[ -z "\$haveroute" -a -z "\$NOROUTE" ] && qt ip route del \$address dev \$interface
|
|
done < \${VARDIR}/proxyarp
|
|
|
|
rm -f \${VARDIR}/proxyarp
|
|
fi
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
[ -f \$f/proxy_arp ] && echo 0 > \$f/proxy_arp
|
|
done
|
|
|
|
__EOF__
|
|
|
|
[ -d $STATEDIR ] && touch $STATEDIR/proxyarp
|
|
|
|
}
|
|
|
|
#
|
|
# 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" ] || save_command del_ip_addr $external $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
|
|
#
|
|
> $STATEDIR/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 "Setting up one-to-one NAT..."
|
|
|
|
while read external interface internal allints localnat; do
|
|
expandv external interface internal allints localnat
|
|
|
|
do_one_nat
|
|
|
|
progress_message_and_save " 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
|
|
|
|
[ -d $STATEDIR ] && touch $STATEDIR/nat
|
|
|
|
indent >&3 << __EOF__
|
|
|
|
if [ -f \${VARDIR}/nat ]; then
|
|
while read external interface; do
|
|
ip_addr_del \$external \$interface
|
|
done < \${VARDIR}/nat
|
|
|
|
rm -f \${VARDIR}/nat
|
|
fi
|
|
|
|
__EOF__
|
|
}
|
|
|
|
#
|
|
# 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_and_save " 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
|
|
|
|
progress_message2 "$DOING $1..."
|
|
|
|
while read interface host; do
|
|
expandv interface host
|
|
list_search $interface $ALL_INTERFACES || \
|
|
fatal_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 "$DOING ECN control on${interfaces}..."
|
|
|
|
for interface in $interfaces; do
|
|
chain=$(ecn_chain $interface)
|
|
if havemanglechain $chain; then
|
|
flushmangle $chain
|
|
else
|
|
createmanglechain $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_and_save " ECN Disabled to $h through $interface"
|
|
done
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Set up an exclusion chain
|
|
#
|
|
build_exclusion_chain() # $1 = variable to store chain name into $2 = table, $3 = SOURCE exclusion list, $4 = DESTINATION exclusion list
|
|
{
|
|
local c=excl_${EXCLUSION_SEQ} net
|
|
|
|
EXCLUSION_SEQ=$(( $EXCLUSION_SEQ + 1 ))
|
|
|
|
run_iptables -t $2 -N $c
|
|
|
|
for net in $(separate_list $3); do
|
|
run_iptables -t $2 -A $c $(source_ip_range $net) -j RETURN
|
|
done
|
|
|
|
for net in $(separate_list $4); do
|
|
run_iptables -t $2 -A $c $(dest_ip_range $net) -j RETURN
|
|
done
|
|
|
|
case $2 in
|
|
filter)
|
|
eval exists_${c}=Yes
|
|
;;
|
|
nat)
|
|
eval exists_nat_${c}=Yes
|
|
;;
|
|
esac
|
|
|
|
eval $1=$c
|
|
}
|
|
|
|
#
|
|
# Setup queuing and classes
|
|
#
|
|
setup_tc1() {
|
|
local mark_part=
|
|
#
|
|
# Create the TC mangle chains
|
|
#
|
|
|
|
createmanglechain tcpre
|
|
|
|
if [ -n "$MANGLE_FORWARD" ]; then
|
|
createmanglechain tcfor
|
|
createmanglechain tcpost
|
|
fi
|
|
|
|
createmanglechain tcout
|
|
#
|
|
# Process the TC Rules File
|
|
#
|
|
strip_file tcrules
|
|
|
|
while read mark sources dests proto ports sports user testval length tos; do
|
|
expandv mark sources dests proto ports sports user testval length tos
|
|
rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval $length $tos")
|
|
process_tc_rule
|
|
done < $TMP_DIR/tcrules
|
|
#
|
|
# Link to the TC mangle chains from the main chains
|
|
#
|
|
|
|
#
|
|
# Route marks are restored in PREROUTING/OUTPUT prior to these rules. We only send
|
|
# packets that are not part of a marked connection to the 'tcpre/tcout' chains.
|
|
#
|
|
if [ -n "$ROUTEMARK_INTERFACES" ]; then
|
|
mark_part="-m mark --mark 0/0xFF00"
|
|
#
|
|
# But let marks in tcpre override those assigned by 'track'
|
|
#
|
|
for interface in $ROUTEMARK_INTERFACES; do
|
|
run_iptables -t mangle -A PREROUTING -i $interface -j tcpre
|
|
done
|
|
fi
|
|
|
|
run_iptables -t mangle -A PREROUTING $mark_part -j tcpre
|
|
run_iptables -t mangle -A OUTPUT $mark_part -j tcout
|
|
|
|
if [ -n "$MANGLE_FORWARD" ]; then
|
|
run_iptables -t mangle -A FORWARD -j tcfor
|
|
run_iptables -t mangle -A POSTROUTING -j tcpost
|
|
fi
|
|
|
|
if [ -n "$HIGH_ROUTE_MARKS" ]; then
|
|
for chain in INPUT FORWARD; do
|
|
run_iptables -t mangle -I $chain -j MARK --and-mark 0xFF
|
|
done
|
|
fi
|
|
|
|
if [ -n "$TC_SCRIPT" ]; then
|
|
save_progress_message "Setting up Traffic Control..."
|
|
append_file $TC_SCRIPT
|
|
elif [ -n "$TC_ENABLED" ]; then
|
|
setup_traffic_shaping
|
|
fi
|
|
}
|
|
|
|
setup_tc() {
|
|
|
|
progress_message2 "$DOING Traffic Control Rules..."
|
|
|
|
setup_tc1
|
|
}
|
|
|
|
#
|
|
# Clear Traffic Shaping
|
|
#
|
|
delete_tc()
|
|
{
|
|
clear_one_tc() {
|
|
save_command "tc qdisc del dev $1 root 2> /dev/null"
|
|
save_command "tc qdisc del dev $1 ingress 2> /dev/null"
|
|
|
|
}
|
|
|
|
save_progress_message "Clearing Traffic Control/QOS"
|
|
|
|
append_file tcclear
|
|
|
|
indent >&3 << __EOF__
|
|
ip link list | while read inx interface details; do
|
|
case \$inx in
|
|
[0-9]*)
|
|
qt tc qdisc del dev \${interface%:} root
|
|
qt tc qdisc del dev \${interface%:} ingress
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
done
|
|
__EOF__
|
|
}
|
|
|
|
#
|
|
# 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"
|
|
}
|
|
|
|
do_ipp2p() {
|
|
[ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support"
|
|
case $proto in
|
|
*:*)
|
|
proto=${proto#*:}
|
|
;;
|
|
*)
|
|
proto=tcp
|
|
;;
|
|
esac
|
|
|
|
rule="$rule -p $proto -m ipp2p --${port:-ipp2p}"
|
|
}
|
|
|
|
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|IPP2P|ipp2p:*|IPP2P:*)
|
|
do_ipp2p
|
|
;;
|
|
*)
|
|
rule="$rule -p $proto"
|
|
;;
|
|
esac
|
|
|
|
multiport=
|
|
|
|
[ -n "$port" ] && case $port in
|
|
-|any|all)
|
|
;;
|
|
*)
|
|
if [ -n "$MULTIPORT" ]; then
|
|
rule="$rule -m multiport --dports $port"
|
|
multiport=Yes
|
|
else
|
|
rule="$rule --dport $port"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
[ -n "$sport" ] && case $sport in
|
|
-|any|all)
|
|
;;
|
|
*)
|
|
if [ -n "$MULTIPORT" ]; then
|
|
[ -n "$multiport" ] && rule="$rule --sports $sport" || rule="$rule -m multiport --sports $sport"
|
|
else
|
|
rule="$rule --sport $sport"
|
|
fi
|
|
;;
|
|
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:=accounting}" = "x-" ] && chain=accounting
|
|
|
|
ensurechain1 $chain
|
|
|
|
if do_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 $DONE
|
|
save_progress_message_short " 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
|
|
{
|
|
|
|
progress_message2 "$DOING Accounting..."
|
|
|
|
save_progress_message "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 -I $chain -j accounting
|
|
done
|
|
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.
|
|
# 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()
|
|
{
|
|
local chain1
|
|
|
|
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
|
|
}
|
|
|
|
handle_exclusion()
|
|
{
|
|
build_exclusion_chain chain1 filter "$excludesource" "$excludedest"
|
|
|
|
run_iptables -A $chain $(fix_bang $cli $proto $sports $multiport $dports) $user -j $chain1
|
|
|
|
cli=
|
|
proto=
|
|
sports=
|
|
multiport=
|
|
dports=
|
|
user=
|
|
}
|
|
|
|
do_ipp2p() {
|
|
[ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. Rule: \"$rule\""
|
|
|
|
dports="-m ipp2p --${port:-ipp2p}"
|
|
|
|
case $proto in
|
|
ipp2p|IPP2P)
|
|
proto=tcp
|
|
port=
|
|
do_ports
|
|
;;
|
|
ipp2p:udpIPP2P:UDP)
|
|
proto=udp
|
|
port=
|
|
do_ports
|
|
;;
|
|
ipp2p:all|IPP2P:ALL)
|
|
proto=all
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# 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=
|
|
chain1=$chain
|
|
user="$userandgroup"
|
|
|
|
[ x$port = x- ] && port=
|
|
[ x$cport = x- ] && cport=
|
|
|
|
case $proto in
|
|
tcp|TCP|6)
|
|
do_ports
|
|
;;
|
|
tcp:syn)
|
|
proto="$proto --syn"
|
|
do_ports
|
|
;;
|
|
udp|UDP|17)
|
|
do_ports
|
|
;;
|
|
icmp|ICMP|1)
|
|
[ -n "$port" ] && dports="--icmp-type $port"
|
|
;;
|
|
ipp2p|IPP2P|ipp2p:*|IPP2P:*)
|
|
do_ipp2p
|
|
;;
|
|
*)
|
|
[ -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 [ -n "${excludesource}${excludedest}" ]; then
|
|
handle_exclusion
|
|
fi
|
|
|
|
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 $chain1 $action $logtarget "$ratelimit" "$logtag" -A $user \
|
|
$(fix_bang $proto $sports $multiport $cli $(dest_ip_range $srv) $dports)
|
|
fi
|
|
|
|
run_iptables2 -A $chain1 $proto $multiport $cli $sports \
|
|
$(dest_ip_range $srv) $dports $ratelimit $user -j $target
|
|
done
|
|
done
|
|
else
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain1 $action $logtarget "$ratelimit" "$logtag" -A $user \
|
|
$(fix_bang $proto $sports $multiport $cli $dest_interface $dports)
|
|
fi
|
|
|
|
run_iptables2 -A $chain1 $proto $multiport $cli $dest_interface $sports \
|
|
$dports $ratelimit $user -j $target
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Process a record from an action file
|
|
#
|
|
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=
|
|
[ $target = LOG ] && return
|
|
;;
|
|
esac
|
|
|
|
loglevel=${loglevel%\!}
|
|
fi
|
|
|
|
logtarget="$target"
|
|
|
|
case $target in
|
|
REJECT)
|
|
target=reject
|
|
;;
|
|
CONTINUE)
|
|
target=RETURN
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
|
|
excludesource=
|
|
|
|
case ${clients:=-} in
|
|
*!*!*)
|
|
fatal_error "Invalid SOURCE in rule \"$rule\""
|
|
;;
|
|
!*)
|
|
if [ $(list_count $clients) -gt 1 ]; then
|
|
excludesource=${clients#!}
|
|
clients=
|
|
fi
|
|
;;
|
|
*!*)
|
|
excludesource=${clients#*!}
|
|
clients=${clients%!*}
|
|
;;
|
|
esac
|
|
|
|
excludedest=
|
|
|
|
case ${servers:=-} in
|
|
*!*!*)
|
|
fatal_error "Invalid DEST in rule \"$rule\""
|
|
;;
|
|
!*)
|
|
if [ $(list_count $servers) -gt 1 ]; then
|
|
excludedest=${servers#*!}
|
|
servers=
|
|
fi
|
|
;;
|
|
*!*)
|
|
excludedest=${servers#*!}
|
|
servers=${servers%!*}
|
|
;;
|
|
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
|
|
#
|
|
progress_message " Rule \"$rule\" $DONE."
|
|
save_progress_message_short " Rule \"$rule\" added."
|
|
}
|
|
|
|
#
|
|
# Append a file in ${VARDIR} to $OUTPUT
|
|
#
|
|
append_action_file() # $1 = File Name
|
|
{
|
|
local user_exit=$(find_file $1)
|
|
|
|
if [ -f $user_exit ]; then
|
|
save_progress_message "Processing $user_exit ..."
|
|
save_command "CHAIN=$CHAIN"
|
|
save_command "LEVEL=$LEVEL"
|
|
save_command "TAG=$TAG"
|
|
|
|
append_file $user_exit
|
|
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
|
|
|
|
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))
|
|
|
|
createchain $CHAIN No
|
|
LEVEL=${level%:*}
|
|
if [ "$LEVEL" != "$level" ]; then
|
|
TAG=${level#*:}
|
|
else
|
|
TAG=
|
|
fi
|
|
|
|
[ none = "${LEVEL%\!}" ] && LEVEL=
|
|
|
|
append_action_file $1
|
|
|
|
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
|
|
*::*)
|
|
fatal_error "Invalid ACTION $1"
|
|
;;
|
|
*:*:*)
|
|
set -- $(split $1)
|
|
createlogactionchain $1 $2:$3
|
|
;;
|
|
*:*)
|
|
set -- $(split $1)
|
|
createlogactionchain $1 $2
|
|
;;
|
|
*)
|
|
CHAIN=$1
|
|
LEVEL=
|
|
TAG=
|
|
createchain $CHAIN no
|
|
|
|
append_action_file $CHAIN
|
|
;;
|
|
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#*:}
|
|
;;
|
|
*)
|
|
havechain $action || fatal_error "Fatal error in find_logactionchain"
|
|
echo $action
|
|
return
|
|
;;
|
|
esac
|
|
|
|
eval chains="\$${action}_chains"
|
|
|
|
set -- $chains
|
|
|
|
while [ $# -gt 0 ]; do
|
|
[ "$1" = "$level" ] && { echo $2 ; return ; }
|
|
shift 2
|
|
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!':$3
|
|
return
|
|
;;
|
|
*'!')
|
|
echo ${subordinate%%:*}:$2:$3
|
|
return
|
|
;;
|
|
*)
|
|
case $subordinate in
|
|
*:*:*)
|
|
echo $subordinate
|
|
return
|
|
;;
|
|
*:*)
|
|
echo $subordinate:$3
|
|
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
|
|
return
|
|
fi
|
|
fi
|
|
|
|
echo $1
|
|
}
|
|
|
|
#
|
|
# Combine a source/dest from the macro body with one from the macro invocation
|
|
#
|
|
merge_macro_source_dest() # $1 = source/dest from macro body, $2 = source/dest from invocation
|
|
{
|
|
case $2 in
|
|
-)
|
|
echo ${1}
|
|
;;
|
|
*.*.*|+*|~*|!~*)
|
|
#
|
|
# Value in the invocation is an address -- put it behind the value from the macro
|
|
#
|
|
echo ${1}:${2}
|
|
;;
|
|
*)
|
|
echo ${2}:${1}
|
|
;;
|
|
esac
|
|
}
|
|
|
|
#
|
|
# The next three functions implement the three phases of action processing.
|
|
#
|
|
# The first phase (process_actions1) occurs before the rules file is processed. ${SHAREDIR}/actions.std
|
|
# and ${CONFDIR}/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 dropNotSyn rejNotSyn dropInvalid allowInvalid allowinUPnP allowoutUPnP forwardUPnP"
|
|
|
|
USEDACTIONS=
|
|
|
|
strip_file actions
|
|
|
|
strip_file actions.std ${SHAREDIR}/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
|
|
;;
|
|
*)
|
|
fatal_error "Common Actions are only allowed for ACCEPT, DROP, REJECT and QUEUE"
|
|
;;
|
|
esac
|
|
esac
|
|
|
|
[ -z "$xaction" ] && continue
|
|
|
|
[ "$xaction" = "$(chain_base $xaction)" ] || fatal_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
|
|
progress_message2 " 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"
|
|
fatal_error "Invalid Macro Parameter in rule \"$rule\""
|
|
;;
|
|
esac
|
|
temp=${temp%%/*}
|
|
;;
|
|
esac
|
|
|
|
f1=macro.${temp}
|
|
fn=$(find_file $f1)
|
|
|
|
if [ ! -f $TMP_DIR/$f1 ]; then
|
|
#
|
|
# We must only verify macros once to ensure that they don't invoke any non-standard actions
|
|
#
|
|
if [ -f $fn ]; then
|
|
strip_file $f1 $fn
|
|
|
|
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"
|
|
fatal_error "Invalid TARGET in rule \"$rule\""
|
|
esac
|
|
done < $TMP_DIR/$f1
|
|
|
|
progress_message " ..End Macro"
|
|
else
|
|
rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec"
|
|
fatal_error "Invalid TARGET in rule \"$rule\""
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
esac
|
|
done < $TMP_DIR/$f
|
|
else
|
|
fatal_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)"
|
|
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
|
|
|
|
save_progress_message "Creating action chain $xaction1"
|
|
|
|
#
|
|
# Handle Builtin actions
|
|
#
|
|
case $xaction1 in
|
|
dropBcast)
|
|
if [ -n "$USEPKTTYPE" ]; 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 interface in $(find_bcastdetect_interfaces); do
|
|
indent >&3 << __EOF__
|
|
|
|
ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u | while read address; do
|
|
__EOF__
|
|
case $xlevel in
|
|
none*)
|
|
;;
|
|
*)
|
|
[ -n "$xlevel" ] && \
|
|
indent >&3 << __EOF__
|
|
log_rule_limit ${xlevel%\!} $xchain dropBcast DROP "" "$xtag" -A -d \$address
|
|
__EOF__
|
|
;;
|
|
esac
|
|
|
|
indent >&3 << __EOF__
|
|
run_iptables -A $xchain -d \$address -j DROP
|
|
done
|
|
|
|
__EOF__
|
|
done
|
|
|
|
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
|
|
;;
|
|
allowBcast)
|
|
if [ -n "$USEPKTTYPE" ]; 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 interface in $(find_bcastdetect_interfaces); do
|
|
indent >&3 << __EOF__
|
|
|
|
ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u | while read address; do
|
|
__EOF__
|
|
case $xlevel in
|
|
none*)
|
|
;;
|
|
*)
|
|
[ -n "$xlevel" ] && \
|
|
indent >&3 << __EOF__
|
|
log_rule_limit ${xlevel%\!} $xchain allowBcast ACCEPT "" "$xtag" -A -d \$address
|
|
__EOF__
|
|
;;
|
|
esac
|
|
|
|
indent >&3 << __EOF__
|
|
run_iptables -A $xchain -d \$address -j ACCEPT
|
|
done
|
|
|
|
__EOF__
|
|
done
|
|
|
|
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
|
|
;;
|
|
dropNotSyn)
|
|
[ -n "$xlevel" ] && \
|
|
log_rule_limit ${xlevel%\!} $xchain dropNotSyn DROP "" "$xtag" -A -p tcp ! --syn
|
|
run_iptables -A $xchain -p tcp ! --syn -j DROP
|
|
;;
|
|
rejNotSyn)
|
|
[ -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
|
|
;;
|
|
dropInvalid)
|
|
[ -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
|
|
;;
|
|
allowInvalid)
|
|
[ -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
|
|
;;
|
|
forwardUPnP)
|
|
;;
|
|
allowinUPnP)
|
|
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
|
|
;;
|
|
allowoutUPnP)
|
|
[ -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
|
|
;;
|
|
*)
|
|
#
|
|
# Not a builtin
|
|
#
|
|
f=action.$xaction1
|
|
|
|
progress_message2 "$DOING $(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
|
|
-|SOURCE)
|
|
mclients=${xclients}
|
|
;;
|
|
DEST)
|
|
mclients=${xservers}
|
|
;;
|
|
*)
|
|
mclients=$(merge_macro_source_dest $mclients $xclients)
|
|
;;
|
|
esac
|
|
else
|
|
mclients=${xclients}
|
|
fi
|
|
|
|
if [ -n "$mservers" ]; then
|
|
case $mservers in
|
|
-|DEST)
|
|
mservers=${xservers}
|
|
;;
|
|
SOURCE)
|
|
mservers=${xclients}
|
|
;;
|
|
*)
|
|
mservers=$(merge_macro_source_dest $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
|
|
# 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
|
|
# excludesource = Source Exclusion List
|
|
#
|
|
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 ${CONFDIR}/masq instead"
|
|
fi
|
|
|
|
# Set original destination address
|
|
|
|
case $addr in
|
|
all)
|
|
addr=
|
|
;;
|
|
detect)
|
|
eval interfaces=\$${source}_interfaces
|
|
|
|
if [ -n "$DETECT_DNAT_IPADDRS" -a "$source" != "$FW" ]; then
|
|
|
|
save_command
|
|
if [ $(list_count1 $interfaces) -eq 1 ]; then
|
|
save_command "addr=\$(find_first_interface_address $interface)"
|
|
else
|
|
save_command "addr="
|
|
for interface in $interfaces; do
|
|
ident >&3 << __EOF__
|
|
addr="\$addr \$(find_first_interface_address $interface)"
|
|
__EOF__
|
|
done
|
|
fi
|
|
else
|
|
addr=
|
|
fi
|
|
;;
|
|
!*)
|
|
if [ $(list_count $addr) -gt 1 ]; then
|
|
excludedests="${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
|
|
|
|
# Generate nat table rules
|
|
|
|
if [ "$source" = "$FW" ]; then
|
|
if [ -n "${excludesource}${excludedests}" ]; then
|
|
build_exclusion_chain chain nat "$excludesource" $excludedests
|
|
|
|
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
|
|
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain OUTPUT $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
|
|
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
|
|
if [ -n "${excludesource}${excludedests}" ]; then
|
|
build_exclusion_chain chain nat "$excludesource" $excludedests
|
|
|
|
if [ $addr = detect ]; then
|
|
ensurenatchain $(dnat_chain $source)
|
|
|
|
indent >&3 << __EOF__
|
|
|
|
for adr in \$addr; do
|
|
run_iptables -t nat -A $(fix_bang $(dnat_chain $source) $cli $proto $multiport $sports $dports) -d \$adr -j $chain
|
|
__EOF__
|
|
else
|
|
for adr in $(separate_list $addr); do
|
|
addnatrule $(dnat_chain $source) $cli $proto $multiport $sports $dports $(dest_ip_range $adr) -j $chain
|
|
done
|
|
fi
|
|
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $(dnat_chain $source) $logtarget "$ratelimit" "$logtag" -A -t nat
|
|
fi
|
|
|
|
addnatrule $chain $ratelimit $proto -j $target1 # Protocol is necessary for port redirection
|
|
else
|
|
chain=$(dnat_chain $source)
|
|
|
|
if [ $addr = detect ]; then
|
|
ensurenatchain $chain
|
|
|
|
indent >&3 << __EOF__
|
|
|
|
for adr in \$addr; do
|
|
__EOF__
|
|
if [ -n "$loglevel" ]; then
|
|
indent >&3 << __EOF__
|
|
log_rule_limit $loglevel $chain $chain $logtarget "$ratelimit" "$logtag" -A -t nat $(fix_bang $proto $cli $sports $multiport $dports) -d \$adr
|
|
__EOF__
|
|
fi
|
|
|
|
indent >&3 << __EOF__
|
|
run_iptables -t nat -A $chain $(fix_bang $proto $ratelimit $cli $sports $multiport $dports) -d \$adr -j $target1
|
|
__EOF__
|
|
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=
|
|
}
|
|
|
|
#
|
|
# Process a record from the rules file
|
|
#
|
|
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=
|
|
#
|
|
# Add one Filter Rule
|
|
#
|
|
# The caller has established the following variables:
|
|
# COMMAND = current command.
|
|
# 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
|
|
# logchain = The chain that should be mentioned in log messages
|
|
# ratelimit = Optional rate limiting clause
|
|
# userandgroup = -m owner clause
|
|
# userspec = User name
|
|
# logtag = Log tag
|
|
# policy = Applicable Policy
|
|
#
|
|
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
|
|
}
|
|
|
|
handle_exclusion()
|
|
{
|
|
build_exclusion_chain chain filter "$excludesource" "$excludedest"
|
|
|
|
if [ -n "$addr" -a -n "$CONNTRACK_MATCH" ]; then
|
|
for adr in $(separate_list $addr); do
|
|
run_iptables -A $logchain $state $(fix_bang $proto $sports $multiport $dports) $user -m conntrack --ctorigdst $adr -j $chain
|
|
done
|
|
addr=
|
|
else
|
|
run_iptables -A $state $logchain $(fix_bang $cli $proto $sports $multiport $dports) $user -j $chain
|
|
fi
|
|
|
|
cli=
|
|
proto=
|
|
sports=
|
|
multiport=
|
|
dports=
|
|
user=
|
|
state=
|
|
}
|
|
|
|
do_ipp2p() {
|
|
[ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. Rule: \"$rule\""
|
|
|
|
dports="-m ipp2p --${port:-ipp2p}"
|
|
|
|
case $proto in
|
|
ipp2p|IPP2P|ipp2p:tcp|IPP2P:TCP)
|
|
port=
|
|
proto=tcp
|
|
do_ports
|
|
;;
|
|
ipp2p:udp|IPP2P:UDP)
|
|
port=
|
|
proto=udp
|
|
do_ports
|
|
;;
|
|
ipp2p:all|IPP2P:ALL)
|
|
port=
|
|
proto=all
|
|
;;
|
|
*)
|
|
fatal_error "Invalid IPP2P protocol ${proto#*:}. Rule: \"$rule\""
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# 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=
|
|
user="$userandgroup"
|
|
|
|
# Restore $chain to the canonical chain.
|
|
|
|
chain=$logchain
|
|
|
|
[ x$port = x- ] && port=
|
|
[ x$cport = x- ] && cport=
|
|
|
|
case $proto in
|
|
tcp|TCP|6)
|
|
do_ports
|
|
;;
|
|
tcp:syn)
|
|
proto="tcp --syn"
|
|
do_ports
|
|
;;
|
|
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=
|
|
;;
|
|
ipp2p|IPP2P|ipp2p:*|IPP2P:*)
|
|
do_ipp2p
|
|
;;
|
|
*)
|
|
[ -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 "$user" -a -z "$excludesource" -a -z "$excludedest" ] ; then
|
|
error_message "WARNING -- Rule \"$rule\" is a POLICY"
|
|
error_message " -- and should be moved to the policy file"
|
|
fi
|
|
;;
|
|
REDIRECT)
|
|
[ -n "$excludedest" ] && fatal_error "Invalid DEST for this ACTION; rule \"$rule\""
|
|
|
|
[ -n "$serv" ] && \
|
|
fatal_error "REDIRECT rules cannot specify a server IP; rule: \"$rule\""
|
|
servport=${servport:=$port}
|
|
natrule=Yes
|
|
;;
|
|
DNAT|SAME)
|
|
[ -n "$excludedest" ] && fatal_error "Invalid DEST for this ACTION; rule \"$rule\""
|
|
|
|
[ -n "$serv" ] || \
|
|
fatal_error "$logtarget rules require a server address; rule: \"$rule\""
|
|
natrule=Yes
|
|
;;
|
|
LOG)
|
|
[ -z "$loglevel" ] && \
|
|
fatal_error "LOG requires log level"
|
|
;;
|
|
esac
|
|
|
|
case $SECTION in
|
|
ESTABLISHED|RELATED)
|
|
[ -n "$FASTACCEPT" ] && fatal_error "Entries in the $SECTION SECTION of the rules file not permitted with FASTACCEPT=Yes"
|
|
state="-m state --state $SECTION"
|
|
;;
|
|
*)
|
|
state=
|
|
;;
|
|
esac
|
|
|
|
if [ -n "${serv}${servport}" ]; then
|
|
|
|
# A specific server or server port given
|
|
|
|
if [ -n "$natrule" ]; then
|
|
add_nat_rule
|
|
[ $policy = ACCEPT ] && return
|
|
elif [ -n "$servport" -a "$servport" != "$port" ]; then
|
|
fatal_error "Only DNAT, SAME and REDIRECT rules may specify destination port mapping; rule \"$rule\""
|
|
fi
|
|
|
|
if [ -n "${excludesource}${excludedest}" ]; then
|
|
handle_exclusion
|
|
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
|
|
if [ "$addr" = detect ]; then
|
|
indent >&3 << __EOF__
|
|
run_iptables -A $chain $state $proto $ratelimit $multiport $cli $sports $(dest_ip_range $srv) $dports -m conntrack --ctorigdst \$adr $user -j $target
|
|
done
|
|
|
|
__EOF__
|
|
else
|
|
for adr in $(separate_list $addr); do
|
|
if [ -n "$loglevel" -a -z "$natrule" ]; then
|
|
log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A -m conntrack --ctorigdst $adr \
|
|
$user $(fix_bang $proto $sports $multiport $cli $(dest_ip_range $srv) $dports) $state
|
|
fi
|
|
|
|
run_iptables2 -A $chain $state $proto $ratelimit $multiport $cli $sports \
|
|
$(dest_ip_range $srv) $dports -m conntrack --ctorigdst $adr $user -j $target
|
|
done
|
|
fi
|
|
else
|
|
if [ -n "$loglevel" -a -z "$natrule" ]; then
|
|
log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A $user \
|
|
$state $(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 $user -j RETURN
|
|
fi
|
|
|
|
if [ "$logtarget" != NONAT ]; then
|
|
run_iptables2 -A $chain $state $proto $multiport $cli $sports \
|
|
$(dest_ip_range $srv) $dports $ratelimit $user -j $target
|
|
fi
|
|
fi
|
|
done
|
|
done
|
|
else
|
|
if [ -n "$loglevel" -a -z "$natrule" ]; then
|
|
log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A $user \
|
|
$state $(fix_bang $proto $sports $multiport $cli $dports)
|
|
fi
|
|
|
|
[ -n "$nonat" ] && \
|
|
addnatrule $(dnat_chain $source) $proto $multiport \
|
|
$cli $sports $dports $ratelimit $user -j RETURN
|
|
|
|
[ "$logtarget" != NONAT ] && \
|
|
run_iptables2 -A $chain $state $proto $multiport $cli $sports \
|
|
$dports $ratelimit $user -j $target
|
|
fi
|
|
fi
|
|
else
|
|
|
|
# Destination is a simple zone
|
|
|
|
if [ -n "${excludesource}${excludedest}" ]; then
|
|
handle_exclusion
|
|
fi
|
|
|
|
if [ -n "$addr" ]; then
|
|
for adr in $(separate_list $addr); do
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A $user \
|
|
$state $(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 $user -m conntrack --ctorigdst $adr -j RETURN
|
|
fi
|
|
|
|
if [ "$logtarget" != NONAT ]; then
|
|
run_iptables2 -A $chain $state $proto $multiport $cli $dest_interface \
|
|
$sports $dports $ratelimit $user -m conntrack --ctorigdst $adr -j $target
|
|
fi
|
|
fi
|
|
done
|
|
else
|
|
if [ -n "$loglevel" ]; then
|
|
log_rule_limit $loglevel $chain $logchain $logtarget "$ratelimit" "$logtag" -A $user \
|
|
$state $(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 $user -j RETURN
|
|
fi
|
|
|
|
if [ "$logtarget" != NONAT ]; then
|
|
run_iptables2 -A $chain $state $proto $multiport $cli $dest_interface \
|
|
$sports $dports $ratelimit $user -j $target
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# # # # # F u n c t i o n B o d y # # # # #
|
|
|
|
[ "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=
|
|
[ $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)
|
|
[ $SECTION = NEW ] || fatal_error "$target rules are not allowed in the $SECTION SECTION"
|
|
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*)
|
|
[ $SECTION = NEW ] || fatal_error "$target rules are not allowed in the $SECTION SECTION"
|
|
target=ACCEPT
|
|
address=${address:=detect}
|
|
;;
|
|
REDIRECT*)
|
|
[ $SECTION = NEW ] || fatal_error "REDIRECT rules are not allowed in the $SECTION SECTION"
|
|
target=ACCEPT
|
|
address=${address:=all}
|
|
if [ "x-" = "x$servers" ]; then
|
|
servers=$FW
|
|
else
|
|
servers="$FW::$servers"
|
|
fi
|
|
;;
|
|
*-)
|
|
[ $SECTION = NEW ] || fatal_error "$target rules are not allowed in the $SECTION SECTION"
|
|
;;
|
|
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\""
|
|
fi
|
|
|
|
excludesource=
|
|
|
|
case $clients in
|
|
*!*!*)
|
|
fatal_error "Invalid SOURCE in rule \"$rule\""
|
|
;;
|
|
!*)
|
|
if [ $(list_count $clients) -gt 1 ]; then
|
|
excludesource=${clients#!}
|
|
clients=
|
|
fi
|
|
;;
|
|
*!*)
|
|
excludesource=${clients#*!}
|
|
clients=${clients%!*}
|
|
;;
|
|
esac
|
|
|
|
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
|
|
|
|
excludedest=
|
|
|
|
case $servers in
|
|
*!*!*)
|
|
fatal_error "Invalid DEST in rule \"$rule\""
|
|
;;
|
|
!*)
|
|
if [ $(list_count $servers) -gt 1 ]; then
|
|
excludedest=${servers#*!}
|
|
servers=
|
|
fi
|
|
;;
|
|
*!*)
|
|
excludedest=${servers#*!}
|
|
servers=${servers%!*}
|
|
;;
|
|
esac
|
|
|
|
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}
|
|
|
|
# If we have one or more exclusion lists, we will create a new chain and
|
|
# store it's name in 'chain'. We still want log rules to reflect the
|
|
# canonical chain so we store it's name in $logchain.
|
|
|
|
logchain=$chain
|
|
|
|
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\""
|
|
|
|
[ "x$protocol" = "x-" ] && protocol=all || protocol=${protocol:=all}
|
|
|
|
ensurechain $chain
|
|
|
|
# Generate Netfilter rule(s)
|
|
|
|
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
|
|
#
|
|
progress_message " Rule \"$rule\" $DONE."
|
|
save_progress_message_short " Rule \"$rule\" added."
|
|
}
|
|
|
|
#
|
|
# 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
|
|
|
|
case ${mtarget%%:*} in
|
|
ACCEPT|ACCEPT+|NONAT|DROP|REJECT|DNAT|DNAT-|REDIRECT|REDIRECT-|LOG|CONTINUE|QUEUE|SAME|SAME-)
|
|
;;
|
|
*)
|
|
if list_search ${mtarget%%:*} $ACTIONS; then
|
|
if ! list_search $mtarget $USEDACTIONS; then
|
|
createactionchain $mtarget
|
|
USEDACTIONS="$USEDACTIONS $mtarget"
|
|
fi
|
|
|
|
mtarget=$(find_logactionchain $mtarget)
|
|
else
|
|
fatal_error "Invalid Action in rule \"$mtarget ${mclients:--} ${mservers:--} ${mprotocol:--} ${mports:--} ${mcports:--} ${xaddress:--} ${mratelimit:--} ${muserspec:--}\""
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [ -n "$mclients" ]; then
|
|
case $mclients in
|
|
-|SOURCE)
|
|
mclients=${iclients}
|
|
;;
|
|
DEST)
|
|
mclients=${iservers}
|
|
;;
|
|
*)
|
|
mclients=$(merge_macro_source_dest $mclients $iclients)
|
|
;;
|
|
esac
|
|
else
|
|
mclients=${iclients}
|
|
fi
|
|
|
|
if [ -n "$mservers" ]; then
|
|
case $mservers in
|
|
-|DEST)
|
|
mservers=${iservers}
|
|
;;
|
|
SOURCE)
|
|
mservers=${iclients}
|
|
;;
|
|
*)
|
|
mservers=$(merge_macro_source_dest $mservers $iservers)
|
|
;;
|
|
esac
|
|
else
|
|
mservers=${iservers}
|
|
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
|
|
#
|
|
process_rules()
|
|
{
|
|
#
|
|
# Process a rule where the source or destination is "all"
|
|
#
|
|
process_wildcard_rule() # $1 = Yes, if this is a macro, $2 = Yes if we want intrazone traffic
|
|
{
|
|
local yclients yservers ysourcezone ydestzone ypolicy
|
|
|
|
for yclients in $xclients; do
|
|
for yservers in $xservers; do
|
|
ysourcezone=${yclients%%:*}
|
|
ydestzone=${yservers%%:*}
|
|
if [ "${ysourcezone}" != "${ydestzone}" -o "$2" = Yes ] ; 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 intrazone=
|
|
|
|
if [ -z "$SECTIONS" ]; then
|
|
finish_section ESTABLISHED,RELATED
|
|
SECTIONS="ESTABLISHED RELATED NEW"
|
|
SECTION=NEW
|
|
fi
|
|
|
|
case $xclients in
|
|
all+)
|
|
xclients=all
|
|
intrazone=Yes
|
|
;;
|
|
all+-|all-+)
|
|
xclients=all-
|
|
intrazone=Yes
|
|
;;
|
|
esac
|
|
|
|
case $xservers in
|
|
all+)
|
|
xservers=all
|
|
intrazone=Yes
|
|
;;
|
|
all+-|all-+)
|
|
xservers=all-
|
|
intrazone=Yes
|
|
;;
|
|
esac
|
|
|
|
case $xclients in
|
|
all|all-)
|
|
[ $xclients = all ] && xclients="$ZONES $FW" || xclients="$ZONES"
|
|
|
|
if [ "x$xservers" = xall ]; then
|
|
xservers="$ZONES $FW"
|
|
elif [ "x$xservers" = xall- ]; then
|
|
xservers="$ZONES"
|
|
fi
|
|
|
|
process_wildcard_rule "$1" $intrazone
|
|
return
|
|
;;
|
|
esac
|
|
|
|
case $xservers in
|
|
all|all-)
|
|
xservers="$ZONES $FW"
|
|
process_wildcard_rule "$1" $intrazone
|
|
return
|
|
;;
|
|
esac
|
|
|
|
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
|
|
;;
|
|
SECTION)
|
|
list_search $xclients $SECTIONS && fatal_error "Duplicate or out of order SECTION $xclients"
|
|
|
|
case $xclients in
|
|
ESTABLISHED)
|
|
SECTIONS=ESTABLISHED
|
|
;;
|
|
RELATED)
|
|
finish_section ESTABLISHED
|
|
SECTIONS="ESTABLISHED RELATED"
|
|
;;
|
|
NEW)
|
|
[ $SECTION = RELATED ] && finish_section RELATED || finish_section ESTABLISHED,RELATED
|
|
SECTIONS="ESTABLISHED RELATED NEW"
|
|
;;
|
|
*)
|
|
fatal_error "Invalid SECTION $xclients"
|
|
;;
|
|
esac
|
|
|
|
[ -n "$xservers" ] && fatal_error "Invalid SECTION $xclients $xservers"
|
|
|
|
SECTION=$xclients
|
|
;;
|
|
*)
|
|
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
|
|
|
|
case $SECTION in
|
|
ESTABLISHED)
|
|
finish_section ESTABLISHED,RELATED
|
|
;;
|
|
RELATED)
|
|
finish_section RELATED
|
|
;;
|
|
esac
|
|
|
|
SECTION=DONE
|
|
}
|
|
|
|
#
|
|
# 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\" $DONE."
|
|
save_progress_message "Rule \"$rule\" Added."
|
|
}
|
|
|
|
#
|
|
# Process the tos file
|
|
#
|
|
process_tos() # $1 = name of tos file
|
|
{
|
|
progress_message2 "$DOING $1..."
|
|
|
|
strip_file tos $1
|
|
|
|
if [ -s $TMP_DIR/tos ] ; then
|
|
createmanglechain pretos
|
|
createmanglechain 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 = loglevel
|
|
{
|
|
local target="$2"
|
|
|
|
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 3 -a "x${3}" != "x-" ]; then
|
|
log_rule $3 $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() {
|
|
#
|
|
# Add a jump to from the canonical chain to the policy chain. On return,
|
|
# $chain is set to the name of the policy chain
|
|
#
|
|
run_iptables -A $chain -j $chain1
|
|
chain=$chain1
|
|
}
|
|
|
|
report_syn_flood_protection()
|
|
{
|
|
progress_message " Enabled SYN flood protection"
|
|
}
|
|
|
|
apply_default()
|
|
{
|
|
#
|
|
# Generate policy file column values from the policy chain
|
|
#
|
|
eval policy=\$${chain1}_policy
|
|
eval loglevel=\$${chain1}_loglevel
|
|
eval synparams=\$${chain1}_synparams
|
|
#
|
|
# 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 $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.
|
|
#
|
|
report_syn_flood_protection
|
|
policy_rules $chain $policy $loglevel
|
|
else
|
|
#
|
|
# No problem with double-counting so just jump to the
|
|
# policy chain.
|
|
#
|
|
jump_to_policy_chain
|
|
fi
|
|
;;
|
|
CONTINUE)
|
|
#
|
|
# Silly to jump to the policy chain -- add any logging
|
|
# rules and enable SYN flood protection if requested
|
|
#
|
|
[ -n "$synparams" ] && \
|
|
report_syn_flood_protection
|
|
policy_rules $chain $policy $loglevel
|
|
;;
|
|
*)
|
|
#
|
|
# DROP or REJECT policy -- enforce in the policy chain and
|
|
# enable SYN flood protection if requested.
|
|
#
|
|
[ -n "$synparams" ] && \
|
|
report_syn_flood_protection
|
|
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=
|
|
|
|
append_file $1
|
|
|
|
eval policychain=\$${2}2${3}_policychain
|
|
|
|
if [ -n "$policychain" ]; then
|
|
eval policy=\$${policychain}_policy
|
|
eval loglevel=\$${policychain}_loglevel
|
|
eval
|
|
|
|
policy_rules $1 $policy $loglevel
|
|
else
|
|
policy_rules $1 DROP 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
|
|
}
|
|
|
|
#
|
|
# Set up Routing
|
|
#
|
|
setup_routes()
|
|
{
|
|
local mask=0xFF
|
|
|
|
[ -n "$HIGH_ROUTE_MARKS" ] && mask=0xFF00
|
|
|
|
run_iptables -t mangle -A PREROUTING -m connmark ! --mark 0/$mask -j CONNMARK --restore-mark --mask $mask
|
|
run_iptables -t mangle -A OUTPUT -m connmark ! --mark 0/$mask -j CONNMARK --restore-mark --mask $mask
|
|
createmanglechain 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/$mask -j routemark
|
|
run_iptables -t mangle -A routemark -i $interface -j MARK --or-mark $mark_value
|
|
|
|
done
|
|
|
|
run_iptables -t mangle -A routemark -m mark ! --mark 0/$mask -j CONNMARK --save-mark --mask $mask
|
|
|
|
}
|
|
|
|
#
|
|
# 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}"
|
|
|
|
detectinterface=
|
|
|
|
case $source in
|
|
*.*.*|+*|!+*)
|
|
;;
|
|
*)
|
|
detectinterface=$networks
|
|
networks=
|
|
;;
|
|
esac
|
|
|
|
[ "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)
|
|
|
|
ensurenatchain $chain
|
|
|
|
case $destnets in
|
|
!*)
|
|
destnets=${destnets#!}
|
|
|
|
build_exclusion_chain newchain nat "$nomasq" "$destnets"
|
|
|
|
if [ -n "$networks" ]; then
|
|
for s in $networks; do
|
|
addnatrule $chain $(source_ip_range $s) $proto $ports $policy -j $newchain
|
|
done
|
|
networks=
|
|
elif [ -n "$detectinterface" ]; then
|
|
indent >&3 << __EOF__
|
|
|
|
networks="\$(get_routed_networks $detectinterface)"
|
|
|
|
[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\""
|
|
|
|
for network in \$networks; do
|
|
run_iptables -t nat -A $chain -s \$network $proto $ports $policy -j $newchain
|
|
done
|
|
|
|
__EOF__
|
|
else
|
|
addnatrule $chain -j $newchain
|
|
fi
|
|
|
|
chain=$newchain
|
|
destnets=0.0.0.0/0
|
|
proto=
|
|
ports=
|
|
policy=
|
|
|
|
[ -n "$nomasq" ] && source="$source except $nomasq"
|
|
;;
|
|
*)
|
|
if [ -n "$nomasq" ]; then
|
|
build_exclusion_chain newchain nat $nomasq
|
|
|
|
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
|
|
elif [ -n "$detectinterface" ]; then
|
|
indent >&3 << __EOF__
|
|
|
|
networks="\$(get_routed_networks $detectinterface)"
|
|
|
|
[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\""
|
|
|
|
for network in \$networks; do
|
|
__EOF__
|
|
for destnet in $(separate_list $destnets); do
|
|
indent >&3 << __EOF__
|
|
run_iptables -t nat -A $chain -s \$network $(dest_ip_range $destnet) $proto $sports $policy -j $netchain
|
|
__EOF__
|
|
done
|
|
indent >&3 << __EOF__
|
|
|
|
done
|
|
__EOF__
|
|
else
|
|
for destnet in $(separate_list $destnets); do
|
|
addnatrule $chain $(dest_ip_range $destnet) $proto $ports $policy -j $newchain
|
|
done
|
|
fi
|
|
|
|
chain=$newchain
|
|
networks=
|
|
destnets=0.0.0.0/0
|
|
proto=
|
|
ports=
|
|
policy=
|
|
source="$source except $nomasq"
|
|
fi
|
|
|
|
;;
|
|
esac
|
|
|
|
addrlist=
|
|
target=MASQUERADE
|
|
|
|
[ "x$addresses" = x- ] && addresses=
|
|
|
|
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
|
|
|
|
if [ -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 ip_addr_del $addr $interface
|
|
ALIASES_TO_ADD="$ALIASES_TO_ADD $addr $fullinterface"
|
|
case $fullinterface in
|
|
*:*)
|
|
fullinterface=${fullinterface%:*}:$((${fullinterface#*:} + 1 ))
|
|
;;
|
|
esac
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
fi
|
|
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_and_save " To $destination $displayproto from $network through ${interface} using $addresses"
|
|
else
|
|
progress_message_and_save " To $destination $displayproto from $network through ${interface}"
|
|
fi
|
|
done
|
|
elif [ -n "$detectinterface" ]; then
|
|
indent >&3 << __EOF__
|
|
|
|
networks="\$(get_routed_networks $detectinterface)"
|
|
|
|
[ -z "\$networks" ] && fatal_error "Unable to determine the routes through interface \"$detectinterface\""
|
|
|
|
for network in \$networks; do
|
|
__EOF__
|
|
for destnet in $(separate_list $destnets); do
|
|
indent >&3 << __EOF__
|
|
run_iptables -t nat -A $chain -s \$network $(dest_ip_range $destnet) $proto $ports $policy -j $target $addrlist
|
|
__EOF__
|
|
done
|
|
|
|
if [ -n "$addresses" ]; then
|
|
message=" To $destination $displayproto from \$network through ${interface} using $addresses"
|
|
else
|
|
message=" To $destination $displayproto from \$network through ${interface}"
|
|
fi
|
|
|
|
indent >&3 << __EOF__
|
|
progress_message "$message"
|
|
done
|
|
|
|
__EOF__
|
|
|
|
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_and_save " To $destination $displayproto from $source through ${interface} using $addresses"
|
|
else
|
|
progress_message_and_save " To $destination $displayproto from $source through ${interface}"
|
|
fi
|
|
fi
|
|
|
|
}
|
|
|
|
strip_file masq $1
|
|
|
|
if [ -n "$NAT_ENABLED" ]; then
|
|
progress_message2 "$DOING Masquerading/SNAT"
|
|
save_progress_message "Setting up Masquerading/SNAT..."
|
|
fi
|
|
|
|
while read fullinterface networks addresses proto ports ipsec; do
|
|
expandv fullinterface networks addresses proto ports ipsec
|
|
if [ -n "$NAT_ENABLED" ]; then
|
|
setup_one
|
|
else
|
|
error_message "WARNING: NAT disabled; masq rule ignored"
|
|
fi
|
|
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 [ -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
|
|
}
|
|
|
|
#
|
|
# 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
|
|
-)
|
|
source=
|
|
;;
|
|
~*|!~*)
|
|
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
|
|
|
|
progress_message_and_save " $addr added to Black List"
|
|
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
|
|
progress_message2 "$DOING 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_and_save " 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
|
|
progress_message2 "$DOING Black List..."
|
|
save_progress_message "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
|
|
|
|
progress_message2 "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
|
|
}
|
|
|
|
#
|
|
# 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 () {
|
|
setup_mss()
|
|
{
|
|
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
|
|
}
|
|
|
|
report_capabilities
|
|
|
|
if [ -n "$BRIDGING" ]; then
|
|
[ -n "$PHYSDEV_MATCH" ] || fatal_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
|
|
fatal_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" ] && \
|
|
fatal_error "RFC1918_STRICT=Yes requires Connection Tracking match"
|
|
|
|
progress_message2 "Determining Zones..."
|
|
|
|
determine_zones
|
|
|
|
if [ $VERBOSE -ge 1 ]; then
|
|
display_list "IPv4 Zones:" $IPV4_ZONES
|
|
[ -n "$IPSEC_ZONES" ] && \
|
|
display_list "IPSEC Zones:" $IPSEC_ZONES
|
|
display_list "Firewall Zone:" $FW
|
|
fi
|
|
|
|
progress_message2 "Validating interfaces file..."
|
|
|
|
validate_interfaces_file
|
|
|
|
progress_message2 "Validating hosts file..."
|
|
|
|
validate_hosts_file
|
|
|
|
progress_message2 "Validating Policy file..."
|
|
|
|
validate_policy
|
|
|
|
progress_message2 "Determining Hosts in Zones..."
|
|
|
|
determine_interfaces
|
|
determine_hosts
|
|
|
|
append_file 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
|
|
|
|
progress_message2 "Pre-processing Actions..."
|
|
process_actions1
|
|
|
|
TERMINATOR=fatal_error
|
|
|
|
deletechain shorewall
|
|
|
|
if [ -n "$NAT_ENABLED" ]; then
|
|
delete_nat
|
|
for chain in PREROUTING POSTROUTING OUTPUT; do
|
|
qt_iptables -t nat -P $chain ACCEPT
|
|
done
|
|
fi
|
|
|
|
delete_proxy_arp
|
|
|
|
if [ -n "$MANGLE_ENABLED" ]; then
|
|
run_iptables -t mangle -F
|
|
run_iptables -t mangle -X
|
|
for chain in PREROUTING INPUT FORWARD POSTROUTING; do
|
|
qt_iptables -t mangle -P $chain ACCEPT
|
|
done
|
|
fi
|
|
|
|
if [ -n "$RAW_TABLE" ]; then
|
|
run_iptables -t raw -F
|
|
run_iptables -t raw -X
|
|
for chain in PREROUTING OUTPUT; do
|
|
qt_iptables -t raw -P $chain ACCEPT
|
|
done
|
|
fi
|
|
|
|
[ -n "$CLEAR_TC" ] && delete_tc
|
|
|
|
progress_message2 "Deleting user chains..."
|
|
|
|
save_progress_message "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
|
|
|
|
[ -n "$CLAMPMSS" ] && setup_mss
|
|
|
|
setcontinue FORWARD
|
|
setcontinue INPUT
|
|
setcontinue OUTPUT
|
|
else
|
|
|
|
setpolicy INPUT DROP
|
|
setpolicy OUTPUT DROP
|
|
setpolicy FORWARD DROP
|
|
|
|
deleteallchains
|
|
|
|
[ -n "$CLAMPMSS" ] && setup_mss
|
|
|
|
setcontinue FORWARD
|
|
setcontinue INPUT
|
|
setcontinue OUTPUT
|
|
fi
|
|
|
|
f=$(find_file ipsets)
|
|
|
|
if [ -f $f ]; then
|
|
progress_message2 "Processing $f ..."
|
|
save_progress_message "Restoring IPSETS..."
|
|
save_command "ipset -U :all: :all:"
|
|
save_command "ipset -F"
|
|
save_command "ipset -X"
|
|
save_command "ipset -R < $f"
|
|
fi
|
|
|
|
append_file continue
|
|
|
|
f=$(find_file routestopped)
|
|
|
|
progress_message2 "$DOING $f ..."
|
|
|
|
strip_file routestopped $f
|
|
|
|
process_routestopped -A
|
|
|
|
if [ -n "$DISABLE_IPV6" ]; then
|
|
save_command disable_ipv6
|
|
fi
|
|
|
|
save_progress_message "Enabling Loopback and DNS Lookups"
|
|
|
|
#
|
|
# 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
|
|
|
|
accounting_file=$(find_file accounting)
|
|
|
|
[ -f $accounting_file ] && setup_accounting $accounting_file
|
|
|
|
createchain reject no
|
|
createchain dynamic no
|
|
createchain logdrop no
|
|
createchain logreject no
|
|
createchain smurfs no
|
|
|
|
log_rule ${BLACKLIST_LOGLEVEL:-info} logdrop DROP
|
|
log_rule ${BLACKLIST_LOGLEVEL:-info} logreject REJECT
|
|
|
|
run_iptables -A logdrop -j DROP
|
|
run_iptables -A logreject -j reject
|
|
|
|
indent >&3 << __EOF__
|
|
|
|
if [ -f \${VARDIR}/save ]; then
|
|
progress_message2 "Setting up dynamic rules..."
|
|
while read target ignore1 ignore2 address rest; do
|
|
case \$target in
|
|
DROP|reject|logdrop|logreject)
|
|
run_iptables -A dynamic -s \$address -j \$target
|
|
;;
|
|
esac
|
|
done < \${VARDIR}/save
|
|
fi
|
|
__EOF__
|
|
|
|
[ -n "$BLACKLISTNEWONLY" ] && state="-m state --state NEW,INVALID" || state=
|
|
|
|
progress_message2 "Creating Interface Chains..."
|
|
|
|
save_progress_message "Creating Interface Chains..."
|
|
|
|
for interface in $ALL_INTERFACES; do
|
|
for chain in $(input_chain $interface) $(forward_chain $interface); do
|
|
createchain $chain no
|
|
run_iptables -A $chain $state -j dynamic
|
|
done
|
|
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 interface in $(find_bcastdetect_interfaces); do
|
|
indent >&3 << __EOF__
|
|
|
|
ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u | while read address; do
|
|
run_iptables -A reject -d \$address -j DROP
|
|
done
|
|
|
|
__EOF__
|
|
done
|
|
|
|
for address in $broadcasts ; do
|
|
run_iptables -A reject -d $address -j DROP
|
|
done
|
|
}
|
|
#
|
|
# Populate the smurf chain
|
|
#
|
|
save_progress_message "Setting up SMURF control..."
|
|
|
|
for interface in $(find_bcastdetect_interfaces); do
|
|
indent >&3 << __EOF__
|
|
|
|
ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet.*brd //; s/scope.*//;' | sort -u | while read address; do
|
|
__EOF__
|
|
[ -n "$SMURF_LOG_LEVEL" ] && \
|
|
indent >&3 << __EOF__
|
|
log_rule $SMURF_LOG_LEVEL smurfs DROP -s \$address
|
|
__EOF__
|
|
indent >&3 << __EOF__
|
|
run_iptables -A smurfs -s \$address -j DROP
|
|
done
|
|
|
|
__EOF__
|
|
done
|
|
|
|
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
|
|
run_iptables -A reject -s $address -j DROP
|
|
done
|
|
#
|
|
# Reject Rules -- Don't respond to broadcasts with an ICMP
|
|
#
|
|
if [ -n "$USEPKTTYPE" ]; then
|
|
run_iptables -A reject -m pkttype --pkt-type broadcast -j DROP
|
|
run_iptables -A reject -m pkttype --pkt-type multicast -j DROP
|
|
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
|
|
#
|
|
if [ -n "$ENHANCED_REJECT" ]; then
|
|
run_iptables -A reject -p icmp -j REJECT --reject-with icmp-host-unreachable
|
|
run_iptables -A reject -j REJECT --reject-with icmp-host-prohibited
|
|
else
|
|
run_iptables -A reject -j REJECT
|
|
fi
|
|
|
|
#
|
|
# Create common action chains
|
|
#
|
|
for action in $USEDACTIONS; do
|
|
createactionchain $action
|
|
done
|
|
|
|
append_file initdone
|
|
|
|
#
|
|
# Process Black List
|
|
#
|
|
save_progress_message "Setting up Black List..."
|
|
|
|
setup_blacklist
|
|
|
|
#
|
|
# SMURFS
|
|
#
|
|
hosts=$(find_hosts_by_option nosmurfs)
|
|
|
|
if [ -n "$hosts" ]; then
|
|
|
|
progress_message2 "Adding Anti-smurf Rules"
|
|
|
|
save_progress_message "Adding Anti-smurf Jumps..."
|
|
|
|
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
|
|
|
|
progress_message2 "Adding rules for DHCP"
|
|
|
|
save_progress_message "Setting up 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" ] && \
|
|
do_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
|
|
progress_message2 "Enabling RFC1918 Filtering"
|
|
|
|
save_progress_message "Setting up 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
|
|
#
|
|
createmanglechain man1918
|
|
createmanglechain 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
|
|
progress_message2 "$DOING TCP Flags checking..."
|
|
|
|
save_progress_message "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 "Setting up ARP filtering..."
|
|
|
|
indent >&3 << __EOF__
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
[ -f \$f/arp_filter ] && echo 0 > \$f/arp_filter
|
|
[ -f \$f/arp_ignore ] && echo 0 > \$f/arp_ignore
|
|
done
|
|
|
|
__EOF__
|
|
|
|
interfaces=$(find_interfaces_by_option arp_filter)
|
|
interfaces1=$(find_interfaces_by_option1 arp_ignore)
|
|
|
|
if [ -n "${interfaces}${interfaces1}" ]; then
|
|
progress_message2 "$DOING ARP Filtering..."
|
|
|
|
for interface in $interfaces; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/arp_filter
|
|
indent >&3 << __EOF__
|
|
if [ -f $file ]; then
|
|
echo 1 > $file
|
|
else
|
|
error_message "WARNING: Cannot set ARP filtering on $interface"
|
|
fi
|
|
__EOF__
|
|
done
|
|
|
|
for interface in $interfaces1; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/arp_ignore
|
|
eval value="\$$(chain_base $interface)_arp_ignore"
|
|
indent >&3 << __EOF__
|
|
if [ -f $file ]; then
|
|
echo $value > $file
|
|
else
|
|
error_message "WARNING: Cannot set ARP filtering on $interface"
|
|
fi
|
|
__EOF__
|
|
done
|
|
fi
|
|
#
|
|
# Route Filtering
|
|
#
|
|
interfaces="$(find_interfaces_by_option routefilter)"
|
|
|
|
if [ -n "$interfaces" -o -n "$ROUTE_FILTER" ]; then
|
|
progress_message2 "$DOING Kernel Route Filtering..."
|
|
|
|
save_progress_message "Setting up Route Filtering..."
|
|
|
|
indent >&3 << __EOF__
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
[ -f \$f/log_martians ] && echo 0 > \$f/rp_filter
|
|
done
|
|
|
|
__EOF__
|
|
for interface in $interfaces; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/rp_filter
|
|
|
|
indent >&3 << __EOF__
|
|
if [ -f $file ]; then
|
|
echo 1 > $file
|
|
else
|
|
error_message "WARNING: Cannot set route filtering on $interface"
|
|
fi
|
|
__EOF__
|
|
done
|
|
|
|
save_command "echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter"
|
|
|
|
if [ -n "$ROUTE_FILTER" ]; then
|
|
save_command "echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter"
|
|
save_command "echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter"
|
|
fi
|
|
|
|
save_command "[ -n \"\$NOROUTES\" ] || ip route flush cache"
|
|
fi
|
|
|
|
#
|
|
# Martian Logging
|
|
#
|
|
interfaces="$(find_interfaces_by_option logmartians)"
|
|
|
|
if [ -n "$interfaces" -o -n "$LOG_MARTIANS" ]; then
|
|
progress_message2 "$DOING Martian Logging..."
|
|
|
|
save_progress_message "Setting up Martian Logging..."
|
|
|
|
indent >&3 << __EOF__
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
[ -f \$f/log_martians ] && echo 0 > \$f/log_martians
|
|
done
|
|
|
|
__EOF__
|
|
for interface in $interfaces; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/log_martians
|
|
|
|
indent >&3 << __EOF__
|
|
if [ -f $file ]; then
|
|
echo 1 > $file
|
|
else
|
|
error_message "WARNING: Cannot set Martian logging on $interface"
|
|
fi
|
|
__EOF__
|
|
done
|
|
|
|
if [ -n "$LOG_MARTIANS" ]; then
|
|
save_command "echo 1 > /proc/sys/net/ipv4/conf/default/log_martians"
|
|
save_command "echo 1 > /proc/sys/net/ipv4/conf/all/log_martians"
|
|
fi
|
|
|
|
fi
|
|
|
|
#
|
|
# Source Routing
|
|
#
|
|
save_progress_message "Setting up Accept Source Routing..."
|
|
|
|
indent >&3 << __EOF__
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
[ -f \$f/accept_source_route ] && echo 0 > \$f/accept_source_route
|
|
done
|
|
|
|
__EOF__
|
|
|
|
interfaces=$(find_interfaces_by_option sourceroute)
|
|
|
|
if [ -n "$interfaces" ]; then
|
|
progress_message2 "$DOING Accept Source Routing..."
|
|
|
|
save_progress_message "Setting up Source Routing..."
|
|
|
|
for interface in $interfaces; do
|
|
file=/proc/sys/net/ipv4/conf/$interface/accept_source_route
|
|
|
|
indent >&3 << __EOF__
|
|
if [ -f $file ]; then
|
|
echo 1 > $file
|
|
else
|
|
error_message "WARNING: Cannot set Accept Source Routing on $interface"
|
|
fi
|
|
__EOF__
|
|
done
|
|
fi
|
|
|
|
if [ -n "$DYNAMIC_ZONES" ]; then
|
|
progress_message "$DOING 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
|
|
progress_message2 "$DOING UPnP..."
|
|
|
|
save_progress_message "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 optional=\$${chain}_is_optional
|
|
|
|
if [ "$policy" != NONE ]; then
|
|
if ! havechain $chain && [ -z "$optional" -a "$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
|
|
#
|
|
case $chain in
|
|
all2*|*2all)
|
|
policy_rules $chain $policy $loglevel
|
|
;;
|
|
esac
|
|
fi
|
|
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
|
|
append_file $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
|
|
elif [ -z "$KLUDGEFREE" ]; then
|
|
[ -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\)\)
|
|
elif [ -z "$KLUDGEFREE" ]; then
|
|
[ -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
|
|
}
|
|
|
|
#
|
|
# Create a dynamic chain for a zone and jump to it from a second chain
|
|
#
|
|
create_zone_dyn_chain() # $1 = zone, $2 = second chain
|
|
{
|
|
createchain ${1}_dyn No
|
|
run_iptables -A $2 -j ${1}_dyn
|
|
}
|
|
#
|
|
# 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
|
|
|
|
> $STATEDIR/chains
|
|
echo "$FW firewall" > $STATEDIR/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
|
|
[ -n "$DYNAMIC_ZONES" ] && create_zone_dyn_chain $zone $frwd_chain
|
|
else
|
|
eval source_hosts=\$${zone}_ipsec_hosts
|
|
[ -n "$DYNAMIC_ZONES" -a -n "$source_hosts" ] && create_zone_dyn_chain $zone $frwd_chain
|
|
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
|
|
eval type=\$${zone}_type
|
|
|
|
[ -n "$complex" ] && frwd_chain=${zone}_frwd
|
|
|
|
echo $zone $type $source_hosts >> $STATEDIR/zones
|
|
|
|
if [ -n "$DYNAMIC_ZONES" ]; then
|
|
echo "$FW $zone $chain1" >> $STATEDIR/chains
|
|
echo "$zone $FW $chain2" >> $STATEDIR/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 chain for DNAT rules
|
|
#
|
|
addrulejump PREROUTING $(dnat_chain $zone) -i $interface $(match_source_hosts $networks) $(match_ipsec_in $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" >> $STATEDIR/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
|
|
done
|
|
|
|
chain=${FW}2${FW}
|
|
|
|
if havechain $chain; then
|
|
#
|
|
# There is a fw->fw chain. Send loopback output through that chain
|
|
#
|
|
run_iptables -A OUTPUT -o lo -j $chain
|
|
#
|
|
# 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
|
|
[ -n "$FASTACCEPT" ] || 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
|
|
}
|
|
|
|
#
|
|
# Compile a script that will stop the firewall
|
|
#
|
|
# This function is called by compile_firewall() so all of the overloaded functions
|
|
# from that script are available here
|
|
#
|
|
compile_stop_firewall() {
|
|
local IPTABLES_COMMAND="\$IPTABLES"
|
|
local INDENT=" "
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
#
|
|
# Stop/restore the firewall after an error or because of a "stop" or "clear" command
|
|
#
|
|
stop_firewall() {
|
|
|
|
deletechain() {
|
|
qt \$IPTABLES -L \$1 -n && qt \$IPTABLES -F \$1 && qt \$IPTABLES -X \$1
|
|
}
|
|
|
|
deleteallchains() {
|
|
\$IPTABLES -F
|
|
\$IPTABLES -X
|
|
}
|
|
|
|
setcontinue() {
|
|
\$IPTABLES -A \$1 -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
}
|
|
|
|
delete_nat() {
|
|
\$IPTABLES -t nat -F
|
|
\$IPTABLES -t nat -X
|
|
|
|
if [ -f \${VARDIR}/nat ]; then
|
|
while read external interface; do
|
|
ip_addr_del \$external dev \$interface
|
|
done < \${VARDIR}/nat
|
|
|
|
rm -f \${VARDIR}/nat
|
|
fi
|
|
}
|
|
|
|
case \$COMMAND in
|
|
stop|clear)
|
|
;;
|
|
*)
|
|
set +x
|
|
|
|
[ -n "\${RESTOREFILE:=restore}" ]
|
|
|
|
RESTOREPATH=\${VARDIR}/\$RESTOREFILE
|
|
|
|
if [ -x \$RESTOREPATH ]; then
|
|
|
|
if [ -x \${RESTOREPATH}-ipsets ]; then
|
|
progress_message2 Restoring Ipsets...
|
|
#
|
|
# We must purge iptables to be sure that there are no
|
|
# references to ipsets
|
|
#
|
|
for table in mangle nat filter; do
|
|
\$IPTABLES -t \$table -F
|
|
\$IPTABLES -t \$table -X
|
|
done
|
|
|
|
\${RESTOREPATH}-ipsets
|
|
fi
|
|
|
|
echo Restoring \${PRODUCT:=Shorewall}...
|
|
|
|
if \$RESTOREPATH restore; then
|
|
echo "\$PRODUCT restored from \$RESTOREPATH"
|
|
set_state "Started"
|
|
else
|
|
set_state "Unknown"
|
|
fi
|
|
|
|
kill \$\$
|
|
exit 2
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
set_state "Stopping"
|
|
|
|
STOPPING="Yes"
|
|
|
|
TERMINATOR=
|
|
|
|
deletechain shorewall
|
|
|
|
determine_capabilities
|
|
|
|
__EOF__
|
|
|
|
append_file stop
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
if [ -n "\$MANGLE_ENABLED" ]; then
|
|
run_iptables -t mangle -F
|
|
run_iptables -t mangle -X
|
|
for chain in PREROUTING INPUT FORWARD POSTROUTING; do
|
|
qt \$IPTABLES -t mangle -P \$chain ACCEPT
|
|
done
|
|
fi
|
|
|
|
if [ -n "\$RAW_TABLE" ]; then
|
|
run_iptables -t raw -F
|
|
run_iptables -t raw -X
|
|
for chain in PREROUTING OUTPUT; do
|
|
qt \$IPTABLES -t raw -P \$chain ACCEPT
|
|
done
|
|
fi
|
|
|
|
if [ -n "\$NAT_ENABLED" ]; then
|
|
delete_nat
|
|
for chain in PREROUTING POSTROUTING OUTPUT; do
|
|
qt \$IPTABLES -t nat -P \$chain ACCEPT
|
|
done
|
|
fi
|
|
|
|
if [ -f \${VARDIR}/proxyarp ]; then
|
|
while read address interface external haveroute; do
|
|
qt arp -i \$external -d \$address pub
|
|
[ -z "\${haveroute}\${NOROUTES}" ] && qt ip route del \$address dev \$interface
|
|
done < \${VARDIR}/proxyarp
|
|
fi
|
|
|
|
for f in /proc/sys/net/ipv4/conf/*; do
|
|
[ -f \$f/proxy_arp ] && echo 0 > \$f/proxy_arp
|
|
done
|
|
|
|
__EOF__
|
|
[ -n "$CLEAR_TC" ] && save_command "delete_tc1"
|
|
|
|
[ -n "$DISABLE_IPV6" ] && save_command "disable_ipv6"
|
|
|
|
process_criticalhosts
|
|
|
|
if [ -n "$CRITICALHOSTS" ]; then
|
|
if [ -z "$ADMINISABSENTMINDED" ]; then
|
|
cat >&3 << __EOF__
|
|
|
|
for chain in INPUT OUTPUT; do
|
|
setpolicy \$chain ACCEPT
|
|
done
|
|
|
|
setpolicy FORWARD DROP
|
|
|
|
deleteallchains
|
|
|
|
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 chain in INPUT OUTPUT; do
|
|
setpolicy $\chain DROP
|
|
done
|
|
|
|
__EOF__
|
|
else
|
|
cat >&3 << __EOF__
|
|
|
|
for chain in INPUT OUTPUT; do
|
|
setpolicy \$chain ACCEPT
|
|
done
|
|
|
|
setpolicy FORWARD DROP
|
|
|
|
deleteallchains
|
|
|
|
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
|
|
|
|
setpolicy INPUT DROP
|
|
|
|
for chain in INPUT FORWARD; do
|
|
setcontinue \$chain
|
|
done
|
|
|
|
__EOF__
|
|
fi
|
|
elif [ -z "$ADMINISABSENTMINDED" ]; then
|
|
cat >&3 << __EOF__
|
|
|
|
for chain in INPUT OUTPUT FORWARD; do
|
|
setpolicy \$chain DROP
|
|
done
|
|
|
|
deleteallchains
|
|
|
|
__EOF__
|
|
else
|
|
cat >&3 << __EOF__
|
|
|
|
for chain in INPUT FORWARD; do
|
|
setpolicy \$chain DROP
|
|
done
|
|
|
|
setpolicy OUTPUT ACCEPT
|
|
|
|
deleteallchains
|
|
|
|
for chain in INPUT FORWARD; do
|
|
setcontinue \$chain
|
|
done
|
|
|
|
__EOF__
|
|
fi
|
|
|
|
process_routestopped -A
|
|
|
|
save_command "\$IPTABLES -A INPUT -i lo -j ACCEPT"
|
|
|
|
[ -z "$ADMINISABSENTMINDED" ] && \
|
|
save_command "\$IPTABLES -A OUTPUT -o lo -j ACCEPT"
|
|
|
|
for interface in $(find_interfaces_by_option dhcp); do
|
|
save_command "\$IPTABLES -A INPUT -p udp -i $interface --dport 67:68 -j ACCEPT"
|
|
[ -z "$ADMINISABSENTMINDED" ] && \
|
|
save_command "\$IPTABLES -A OUTPUT -p udp -o $interface --dport 67:68 -j ACCEPT"
|
|
#
|
|
# This might be a bridge
|
|
#
|
|
save_command "\$IPTABLES -A FORWARD -p udp -i $interface -o $interface --dport 67:68 -j ACCEPT"
|
|
done
|
|
|
|
save_command
|
|
|
|
case "$IP_FORWARDING" in
|
|
[Oo][Nn])
|
|
save_command "echo 1 > /proc/sys/net/ipv4/ip_forward"
|
|
save_command "progress_message2 IP Forwarding Enabled"
|
|
;;
|
|
[Oo][Ff][Ff])
|
|
save_command "echo 0 > /proc/sys/net/ipv4/ip_forward"
|
|
save_command "progress_message2 IP Forwarding Disabled!"
|
|
;;
|
|
esac
|
|
|
|
append_file stopped
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
set_state "Stopped"
|
|
|
|
logger "Shorewall Stopped"
|
|
|
|
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
|
|
#
|
|
kill \$\$
|
|
;;
|
|
esac
|
|
}
|
|
__EOF__
|
|
}
|
|
|
|
#
|
|
# Compile a Restore Script
|
|
#
|
|
compile_firewall() # $1 = File Name
|
|
{
|
|
local IPTABLES_COMMAND=run_iptables
|
|
local INDENT=""
|
|
|
|
outfile=$1 dir=
|
|
#
|
|
# Overload a function from the library
|
|
#
|
|
deletechain() # $1 = name of chain
|
|
{
|
|
save_command "qt \$IPTABLES -L $1 -n && qt \$IPTABLES -F $1 && qt \$IPTABLES -X $1"
|
|
}
|
|
#
|
|
# END OVERLOADED FUNCTIONS
|
|
#
|
|
|
|
#
|
|
# So that mktempdir doesn't have to jump through hoops when there isn't a working 'mktemp',
|
|
# we create the compiler's temporary directory in TMP_DIR
|
|
#
|
|
STATEDIR=$TMP_DIR/compiler_state/
|
|
|
|
mkdir $STATEDIR || fatal_error "Cannot create temporary directory in $TMP_DIR"
|
|
|
|
if [ $COMMAND = compile ]; then
|
|
dir=$(dirname $1)
|
|
[ -d $dir ] || fatal_error "Directory $dir does not exist"
|
|
[ -h $dir ] && fatal_error "$dir is a Symbolic Link"
|
|
[ -d $outfile ] && fatal_error "$outfile is a Directory"
|
|
[ -h $outfile ] && fatal_error "$outfile is a Symbolic Link"
|
|
[ -f $outfile -a ! -x $outfile ] && fatal_error "$outfile exists and is not a restore file"
|
|
|
|
DOING=Compiling
|
|
DONE=compiled
|
|
|
|
OUTPUT=$(mktempfile $STATEDIR)
|
|
|
|
[ -n "$OUTPUT" ] || fatal_error "Cannot create temporary file in /tmp"
|
|
|
|
exec 3>>$OUTPUT
|
|
else
|
|
DOING=Checking
|
|
DONE=checked
|
|
|
|
exec 3>/dev/null
|
|
fi
|
|
|
|
cat >&3 << __EOF__
|
|
#
|
|
# Compiled startup file generated by Shorewall $VERSION - $(date)"
|
|
#
|
|
__EOF__
|
|
|
|
if [ -n "$EXPORT" ]; then
|
|
cat >&3 << __EOF__
|
|
SHAREDIR=/usr/share/shorewall-lite
|
|
CONFDIR=/etc/shorewall-lite
|
|
VARDIR=/var/lib/shorewall-lite
|
|
__EOF__
|
|
else
|
|
cat >&3 << __EOF__
|
|
SHAREDIR=/usr/share/shorewall
|
|
CONFDIR=/etc/shorewall
|
|
VARDIR=/var/lib/shorewall
|
|
__EOF__
|
|
fi
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
. \${SHAREDIR}/functions
|
|
__EOF__
|
|
|
|
compile_stop_firewall
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
#
|
|
# Set policy of chain \$1 to \$2
|
|
#
|
|
setpolicy() {
|
|
\$IPTABLES -P \$1 \$2
|
|
}
|
|
|
|
#
|
|
# Remove all Shorewall-added rules
|
|
#
|
|
clear_firewall() {
|
|
stop_firewall
|
|
|
|
setpolicy INPUT ACCEPT
|
|
setpolicy FORWARD ACCEPT
|
|
setpolicy OUTPUT ACCEPT
|
|
|
|
run_iptables -F
|
|
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
|
|
__EOF__
|
|
if [ -n "$DISABLE_IPV6" ]; then
|
|
cat >&3 << __EOF__
|
|
if qt mywhich ip6tables; then
|
|
ip6tables -P INPUT ACCEPT 2> /dev/null
|
|
ip6tables -P OUTPUT ACCEPT 2> /dev/null
|
|
ip6tables -P FORWARD ACCEPT 2> /dev/null
|
|
fi
|
|
|
|
__EOF__
|
|
fi
|
|
|
|
append_file clear
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
set_state "Cleared"
|
|
|
|
logger "Shorewall Cleared"
|
|
}
|
|
|
|
#
|
|
# Issue a message and stop/restore the firewall
|
|
#
|
|
fatal_error()
|
|
{
|
|
echo " ERROR: \$@" >&2
|
|
stop_firewall
|
|
exit 2
|
|
}
|
|
|
|
#
|
|
# Issue a message and stop
|
|
#
|
|
startup_error() # \$* = Error Message
|
|
{
|
|
echo " ERROR: \$@" >&2
|
|
kill \$\$
|
|
exit 2
|
|
}
|
|
|
|
#
|
|
# Run iptables and if an error occurs, stop/restore the firewall
|
|
#
|
|
run_iptables()
|
|
{
|
|
if ! \$IPTABLES \$@; then
|
|
error_message "ERROR: Command \"\$IPTABLES \$@\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Run iptables and if an error occurs, stop/restore the firewall
|
|
#
|
|
run_ip()
|
|
{
|
|
if ! ip \$@; then
|
|
error_message "ERROR: Command \"ip \$@\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Run tc and if an error occurs, stop/restore the firewall
|
|
#
|
|
run_tc() {
|
|
if ! tc \$@ ; then
|
|
error_message "ERROR: Command \"tc \$@\" Failed"
|
|
stop_firewall
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Functions to appease unconverted extension scripts
|
|
#
|
|
save_command()
|
|
{
|
|
return 0
|
|
}
|
|
|
|
run_and_save_command() {
|
|
eval \$@
|
|
}
|
|
|
|
ensure_and_save_command() {
|
|
eval \$@ || fatal_error "Command \"\$@\" failed"
|
|
}
|
|
|
|
#
|
|
# Initialize environment
|
|
#
|
|
initialize() {
|
|
__EOF__
|
|
INDENT=" "
|
|
|
|
if [ -n "$EXPORT" ]; then
|
|
cat >&3 << __EOF__
|
|
if [ ! -f \${SHAREDIR}/version ]; then
|
|
fatal_error "This script requires Shorewall Lite which do not appear to be installed on this system"
|
|
fi
|
|
|
|
local version=\$(cat \${SHAREDIR}/version)
|
|
|
|
if [ \${LIBVERSION:-0} -lt 30191 ]; then
|
|
fatal_error "This script requires Shorewall Lite version 3.2.0-RC2 or later; current version is \$version"
|
|
fi
|
|
|
|
__EOF__
|
|
else
|
|
cat >&3 << __EOF__
|
|
if [ ! -f \${SHAREDIR}/version ]; then
|
|
fatal_error "This script requires Shorewall which do not appear to be installed on this system (did you forget "-e" when you compiled?)"
|
|
fi
|
|
|
|
local version=\$(cat \${SHAREDIR}/version)
|
|
|
|
if [ \${LIBVERSION:-0} -lt 30191 ]; then
|
|
fatal_error "This script requires Shorewall version 3.2.0-Beta7 or later; current version is \$version"
|
|
fi
|
|
|
|
__EOF__
|
|
fi
|
|
|
|
cat >&3 << __EOF__
|
|
#
|
|
# These variables are required by the library functions called in this script
|
|
#
|
|
[ -n "\${COMMAND:=restart}" ]
|
|
[ -n "\${VERBOSE:=0}" ]
|
|
MODULESDIR="$MODULESDIR"
|
|
MODULE_SUFFIX="$MODULE_SUFFIX"
|
|
LOGLIMIT="$LOGLIMIT"
|
|
LOGTAGONLY="$LOGTAGONLY"
|
|
LOGRULENUMBERS="$LOGRULENUMBERS"
|
|
__EOF__
|
|
|
|
if [ -n "$LOGFORMAT" ]; then
|
|
cat >&3 << __EOF__
|
|
LOGFORMAT="$LOGFORMAT"
|
|
__EOF__
|
|
else
|
|
cat >&3 << __EOF__
|
|
[ -n "\$LOGFORMAT\" ] || LOGFORMAT="Shorewall:%s:%s:"
|
|
__EOF__
|
|
fi
|
|
|
|
cat >&3 << __EOF__
|
|
RESTOREFILE="$RESTOREFILE"
|
|
VERSION="$VERSION"
|
|
CONFIG_PATH="$CONFIG_PATH"
|
|
SUBSYSLOCK="$SUBSYSLOCK"
|
|
PATH="$PATH"
|
|
TERMINATOR=fatal_error
|
|
|
|
__EOF__
|
|
if [ -n "$IPTABLES" ]; then
|
|
cat >&3 << __EOF__
|
|
IPTABLES="$IPTABLES"
|
|
|
|
[ -e "$IPTABLES" ] || startup_error "IPTABLES=$IPTABLES does not exist or is not executable"
|
|
__EOF__
|
|
else
|
|
cat >&3 << __EOF__
|
|
[ -z "\$IPTABLES" ] && IPTABLES=\$(mywhich iptables 2> /dev/null)
|
|
|
|
[ -n "\$IPTABLES" -a -e "\$IPTABLES" ] || startup_error "Can't find iptables executable"
|
|
__EOF__
|
|
fi
|
|
|
|
append_file params
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
STOPPING=
|
|
#
|
|
# The library requires that ${VARDIR} exist
|
|
#
|
|
[ -d \${VARDIR} ] || mkdir -p \${VARDIR}
|
|
|
|
}
|
|
|
|
#
|
|
# Start/Restart/Reload the firewall
|
|
#
|
|
define_firewall() {
|
|
local restore_file=\$1
|
|
__EOF__
|
|
|
|
INDENT=" "
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
load_kernel_modules
|
|
|
|
__EOF__
|
|
|
|
progress_message2 "Initializing..."
|
|
save_progress_message "Initializing..."
|
|
|
|
initialize_netfilter
|
|
|
|
progress_message2 "$DOING Proxy ARP"; setup_proxy_arp
|
|
#
|
|
# [re]-Establish routing
|
|
#
|
|
setup_providers $(find_file providers)
|
|
[ -n "$ROUTEMARK_INTERFACES" ] && setup_routes
|
|
|
|
progress_message2 "$DOING NAT..."; setup_nat
|
|
progress_message2 "$DOING NETMAP..."; setup_netmap
|
|
progress_message2 "$DOING Common Rules"; add_common_rules
|
|
|
|
save_progress_message "Setting up SYN Flood Protection..."
|
|
|
|
setup_syn_flood_chains
|
|
|
|
save_progress_message "Setting up IPSEC management..."
|
|
|
|
setup_ipsec
|
|
|
|
maclist_hosts=$(find_hosts_by_option maclist)
|
|
|
|
if [ -n "$maclist_hosts" ]; then
|
|
save_progress_message "Setting up MAC Filtration..."
|
|
setup_mac_lists
|
|
fi
|
|
|
|
progress_message2 "$DOING $(find_file rules)..."
|
|
save_progress_message "Setting up Rules..."
|
|
process_rules
|
|
|
|
tunnels=$(find_file tunnels)
|
|
if [ -f $tunnels ]; then
|
|
progress_message2 "$DOING $tunnels..."
|
|
save_progress_message "Setting up Tunnels..."
|
|
setup_tunnels $tunnels
|
|
fi
|
|
|
|
save_progress_message "Setting up Actions..."
|
|
|
|
progress_message2 "$DOING Actions..."; process_actions2
|
|
process_actions3
|
|
|
|
save_progress_message "Applying Policies..."
|
|
|
|
progress_message2 "$DOING $(find_file policy)..."; apply_policy_rules
|
|
|
|
masq=$(find_file masq)
|
|
if [ -f $masq ]; then
|
|
setup_masq $masq
|
|
fi
|
|
|
|
tos=$(find_file tos)
|
|
if [ -f $tos -a -n "$MANGLE_ENABLED" ]; then
|
|
save_progress_message "Setting up TOS..."
|
|
process_tos $tos
|
|
fi
|
|
|
|
ecn=$(find_file ecn)
|
|
if [ -f $ecn -a -n "$MANGLE_ENABLED" ]; then
|
|
save_progress_message "Setting up ECN..."
|
|
setup_ecn $ecn
|
|
fi
|
|
|
|
if [ -n "$MANGLE_ENABLED" ]; then
|
|
save_progress_message "Setting up TC Rules..."
|
|
setup_tc
|
|
fi
|
|
|
|
progress_message2 "$DOING Rule Activation..."
|
|
save_progress_message "Activating Rules..."
|
|
activate_rules
|
|
|
|
if [ -n "$ALIASES_TO_ADD" ]; then
|
|
save_command add_ip_aliases $ALIASES_TO_ADD
|
|
fi
|
|
|
|
for file in chains nat proxyarp zones; do
|
|
save_command "cat > \${VARDIR}/$file $LEFTSHIFT __EOF__"
|
|
cat $STATEDIR/$file >&3
|
|
save_command_unindented __EOF__
|
|
done
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
if [ \$COMMAND = restore ]; then
|
|
iptables-restore < \$restore_file
|
|
fi
|
|
|
|
__EOF__
|
|
save_command "date > \${VARDIR}/restarted"
|
|
|
|
append_file start
|
|
|
|
[ -n "$DELAYBLACKLISTLOAD" ] && refresh_blacklist
|
|
|
|
createchain shorewall no
|
|
|
|
save_command set_state "Started"
|
|
|
|
append_file started
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
cp -f \$(my_pathname) \${VARDIR}/.restore
|
|
|
|
case \$COMMAND in
|
|
start)
|
|
logger "Shorewall started"
|
|
;;
|
|
restart)
|
|
logger "Shorewall restarted"
|
|
;;
|
|
restore)
|
|
logger "Shorewall restored"
|
|
;;
|
|
esac
|
|
|
|
}
|
|
|
|
#
|
|
# Silently define Firewall and ignore errors
|
|
#
|
|
restore_firewall()
|
|
{
|
|
iptables_save_file=\${VARDIR}/\$(basename \$0)-iptables
|
|
|
|
fatal_error()
|
|
{
|
|
echo " ERROR: \$@" >&2
|
|
}
|
|
|
|
startup_error() # \$@ = Error Message
|
|
{
|
|
echo " ERROR: \$@" >&2
|
|
}
|
|
|
|
run_iptables() { return 0; }
|
|
|
|
VERBOSE=-1 # The progress messages don't make sense without iptables
|
|
|
|
IPTABLES=run_iptables
|
|
|
|
if [ -f \$iptables_save_file ]; then
|
|
{
|
|
define_firewall \$iptables_save_file
|
|
}
|
|
else
|
|
fatal_error "\$iptables_save_file does not exist"
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
__EOF__
|
|
|
|
exec 3>&-
|
|
|
|
if [ $COMMAND = check ]; then
|
|
progress_message3 "Shorewall configuration verified"
|
|
else
|
|
INDENT=
|
|
cat $(find_file prog.header) $OUTPUT $(find_file prog.footer) > $outfile
|
|
chmod 700 $outfile
|
|
progress_message3 "Shorewall configuration compiled to $outfile"
|
|
rm -f $OUTPUT
|
|
fi
|
|
|
|
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 ""
|
|
;;
|
|
*)
|
|
fatal_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 ""
|
|
;;
|
|
*)
|
|
fatal_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 077
|
|
|
|
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
|
#
|
|
# Establish termination function
|
|
#
|
|
TERMINATOR=fatal_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=
|
|
FORWARDPING=
|
|
MACLIST_DISPOSITION=
|
|
MACLIST_LOG_LEVEL=
|
|
TCP_FLAGS_DISPOSITION=
|
|
TCP_FLAGS_LOG_LEVEL=
|
|
RFC1918_LOG_LEVEL=
|
|
MARK_IN_FORWARD_CHAIN=
|
|
FUNCTIONS=
|
|
VERSION_FILE=
|
|
LOGFORMAT=
|
|
LOGRULENUMBERS=
|
|
ADMINISABSENTMINDED=
|
|
BLACKLISTNEWONLY=
|
|
MODULE_SUFFIX=
|
|
ACTIONS=
|
|
USEDACTIONS=
|
|
SMURF_LOG_LEVEL=
|
|
DISABLE_IPV6=
|
|
BRIDGING=
|
|
DYNAMIC_ZONES=
|
|
PKTTYPE=
|
|
USEPKTYPE=
|
|
RETAIN_ALIASES=
|
|
DELAYBLACKLISTLOAD=
|
|
LOGTAGONLY=
|
|
LOGALLNEW=
|
|
RFC1918_STRICT=
|
|
MACLIST_TTL=
|
|
SAVE_IPSETS=
|
|
RESTOREFILE=
|
|
MAPOLDACTIONS=
|
|
IMPLICIT_CONTINUE=
|
|
HIGH_ROUTE_MARKS=
|
|
|
|
OUTPUT=
|
|
TMP_DIR=
|
|
ALL_INTERFACES=
|
|
ROUTEMARK_INTERFACES=
|
|
IPSECMARK=256
|
|
PROVIDERS=
|
|
CRITICALHOSTS=
|
|
IPSECFILE=
|
|
EXCLUSION_SEQ=1
|
|
STOPPING=
|
|
HAVE_MUTEX=
|
|
ALIASES_TO_ADD=
|
|
SECTION=ESTABLISHED
|
|
SECTIONS=
|
|
ALL_PORTS=
|
|
|
|
SHAREDIR=/usr/share/shorewall
|
|
VARDIR=/var/lib/shorewall
|
|
CONFDIR=/etc/shorewall
|
|
|
|
FUNCTIONS=${SHAREDIR}/functions
|
|
|
|
[ -n "${VERBOSE:=2}" ]
|
|
|
|
if [ -f $FUNCTIONS ]; then
|
|
[ $VERBOSE -ge 2 ] && echo "Loading $FUNCTIONS..."
|
|
. $FUNCTIONS
|
|
else
|
|
fatal_error "$FUNCTIONS does not exist!"
|
|
fi
|
|
|
|
TMP_DIR=$(mktempdir)
|
|
|
|
[ -n "$TMP_DIR" ] && chmod 700 $TMP_DIR || \
|
|
fatal_error "Can't create a temporary directory"
|
|
|
|
trap "[ -n "$OUTPUT" ] && rm -f $OUTPUT;rm -rf $TMP_DIR; exit 2" 1 2 3 4 5 6 9
|
|
|
|
ensure_config_path
|
|
|
|
VERSION_FILE=$SHAREDIR/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
|
|
progress_message "Processing $config..."
|
|
. $config
|
|
else
|
|
fatal_error "Cannot read $config (Hint: Are you root?)"
|
|
fi
|
|
else
|
|
fatal_error "$config does not exist!"
|
|
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.
|
|
#
|
|
PKTTYPE=$(added_param_value_no PKTTYPE $PKTTYPE)
|
|
|
|
[ -n "${MODULE_SUFFIX:=o gz ko o.gz ko.gz}" ]
|
|
|
|
if [ -z "$EXPORT" -a "$(whoami)" = root ]; then
|
|
|
|
load_kernel_modules
|
|
|
|
if [ -z "$IPTABLES" ]; then
|
|
IPTABLES=$(mywhich iptables 2> /dev/null)
|
|
|
|
[ -z "$IPTABLES" ] && fatal_error "Can't find iptables executable"
|
|
else
|
|
[ -e "$IPTABLES" ] || fatal_error "\$IPTABLES=$IPTABLES does not exist or is not executable"
|
|
fi
|
|
determine_capabilities
|
|
|
|
else
|
|
f=$(find_file capabilities)
|
|
|
|
[ -f $f ] && . $f || fatal_error "The -e flag requires a capabilities file"
|
|
fi
|
|
|
|
ALLOWRELATED="$(added_param_value_yes ALLOWRELATED $ALLOWRELATED)"
|
|
[ -n "$ALLOWRELATED" ] || \
|
|
fatal_error "ALLOWRELATED=No is not supported"
|
|
ADD_IP_ALIASES="$(added_param_value_yes ADD_IP_ALIASES $ADD_IP_ALIASES)"
|
|
|
|
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])
|
|
;;
|
|
*)
|
|
fatal_error "Invalid value ($IP_FORWARDING) for IP_FORWARDING"
|
|
;;
|
|
esac
|
|
else
|
|
IP_FORWARDING=On
|
|
fi
|
|
|
|
[ -n "${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" ] && \
|
|
fatal_error "FORWARDPING=Yes is no longer supported"
|
|
|
|
maclist_target=reject
|
|
|
|
if [ -n "$MACLIST_DISPOSITION" ] ; then
|
|
case $MACLIST_DISPOSITION in
|
|
REJECT)
|
|
;;
|
|
DROP)
|
|
maclist_target=DROP
|
|
;;
|
|
ACCEPT)
|
|
maclist_target=RETURN
|
|
;;
|
|
*)
|
|
fatal_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)
|
|
;;
|
|
*)
|
|
fatal_error "Invalid value ($TCP_FLAGS_DISPOSITION) for TCP_FLAGS_DISPOSITION"
|
|
;;
|
|
esac
|
|
else
|
|
TCP_FLAGS_DISPOSITION=DROP
|
|
fi
|
|
|
|
[ -n "${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
|
|
CLEAR_TC=$(added_param_value_yes CLEAR_TC $CLEAR_TC)
|
|
|
|
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
|
|
fatal_error "Invalid LOGFORMAT string: \"$LOGFORMAT\""
|
|
fi
|
|
else
|
|
temp=$(printf "$LOGFORMAT" fooxx barxx 2> /dev/null)
|
|
if [ $? -ne 0 ]; then
|
|
fatal_error "Invalid LOGFORMAT string: \"$LOGFORMAT\""
|
|
fi
|
|
fi
|
|
|
|
[ ${#temp} -le 29 ] || fatal_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)
|
|
[ -n "$DYNAMIC_ZONES" -a -n "$EXPORT" ] && fatal_error "DYNAMIC_ZONES=Yes is incompatible with the -e option"
|
|
STARTUP_ENABLED=$(added_param_value_yes STARTUP_ENABLED $STARTUP_ENABLED)
|
|
RETAIN_ALIASES=$(added_param_value_no RETAIN_ALIASES $RETAIN_ALIASES)
|
|
[ -n "${ADD_IP_ALIASES}${ADD_SNAT_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)
|
|
FASTACCEPT=$(added_param_value_no FASTACCEPT $FASTACCEPT)
|
|
IMPLICIT_CONTINUE=$(added_param_value_no IMPLICIT_CONTINUE $IMPLICIT_CONTINUE)
|
|
HIGH_ROUTE_MARKS=$(added_param_value_no HIGH_ROUTE_MARKS $HIGH_ROUTE_MARKS)
|
|
[ -n "$XCONNMARK_MATCH" ] || XCONNMARK=
|
|
[ -n "$XMARK" ] || XCONNMARK=
|
|
|
|
[ -n "$HIGH_ROUTE_MARKS" -a -z "$XCONNMARK" ] && fatal_error "HIGH_ROUTE_MARKS=Yes requires extended CONNMARK target, extended CONNMARK match support and extended MARK support"
|
|
|
|
case ${IPSECFILE:=ipsec} in
|
|
ipsec|zones)
|
|
;;
|
|
*)
|
|
fatal_error "Invalid value ($IPSECFILE) for IPSECFILE option"
|
|
;;
|
|
esac
|
|
|
|
case ${MACLIST_TABLE:=filter} in
|
|
filter)
|
|
;;
|
|
mangle)
|
|
[ $MACLIST_DISPOSITION = reject ] && fatal_error "MACLIST_DISPOSITION=REJECT is not allowed with MACLIST_TABLE=mangle"
|
|
;; *)
|
|
fatal_error "Invalid value ($MACLIST_TABLE) for MACLIST_TABLE option"
|
|
;;
|
|
esac
|
|
|
|
TC_SCRIPT=
|
|
|
|
if [ -n "$TC_ENABLED" ] ; then
|
|
case "$TC_ENABLED" in
|
|
[Yy][Ee][Ss])
|
|
TC_ENABLED=
|
|
TC_SCRIPT=$(find_file tcstart)
|
|
[ -f $TC_SCRIPT ] || fatal_error "Unable to find tcstart file"
|
|
;;
|
|
[Ii][Nn][Tt][Ee][Rr][Nn][Aa][Ll])
|
|
TC_ENABLED=Yes
|
|
;;
|
|
[Nn][Oo])
|
|
TC_ENABLED=
|
|
;;
|
|
esac
|
|
else
|
|
TC_ENABLED=Yes
|
|
fi
|
|
|
|
if [ -n "$TC_ENABLED" ];then
|
|
[ -n "$MANGLE_ENABLED" ] || fatal_error "Traffic Shaping requires mangle support in your kernel and iptables"
|
|
fi
|
|
|
|
[ "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:=/bin/sh}" ]
|
|
|
|
temp=$(decodeaddr 192.168.1.1)
|
|
if [ $(encodeaddr $temp) != 192.168.1.1 ]; then
|
|
fatal_error "Shell $SHOREWALL_SHELL is broken and may not be used with Shorewall"
|
|
fi
|
|
|
|
if [ -z "$KLUDGEFREE" ]; then
|
|
rm -f $TMP_DIR/physdev
|
|
rm -f $TMP_DIR/iprange
|
|
fi
|
|
|
|
qt mywhich awk && HAVEAWK=Yes || HAVEAWK=
|
|
}
|
|
|
|
#
|
|
# Give Usage Information
|
|
#
|
|
usage() {
|
|
echo "Usage: $0 [debug] check|compile <filename>}"
|
|
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 "exit 2" 1 2 3 4 5 6 9
|
|
|
|
COMMAND="$1"
|
|
|
|
case "$COMMAND" in
|
|
|
|
check)
|
|
[ $# -ne 1 ] && usage
|
|
do_initialize
|
|
compile_firewall
|
|
;;
|
|
|
|
compile)
|
|
[ $# -ne 2 ] && usage
|
|
do_initialize
|
|
compile_firewall $2
|
|
;;
|
|
|
|
call)
|
|
#
|
|
# Undocumented way to call functions in ${SHAREDIR}/compiler directly
|
|
#
|
|
shift
|
|
do_initialize
|
|
EMPTY=
|
|
$@
|
|
;;
|
|
|
|
*)
|
|
usage
|
|
;;
|
|
|
|
esac
|