2002-07-24 17:03:06 +02:00
#!/bin/sh
2002-05-01 01:13:15 +02:00
#
2006-01-16 16:15:43 +01:00
# Shorewall 3.2 -- /usr/share/shorewall/functions
2006-05-06 18:26:05 +02:00
#
# 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
2005-07-09 07:45:05 +02:00
2006-05-06 18:22:53 +02:00
LIBVERSION=30191
2006-02-03 22:11:02 +01:00
2006-01-12 19:21:16 +01:00
#
# Message to stderr
#
error_message() # $* = Error Message
{
echo " $@" >&2
}
2005-07-09 07:45:05 +02:00
# Function to truncate a string -- It uses 'cut -b -<n>'
# rather than ${v:first:last} because light-weight shells like ash and
# dash do not support that form of expansion.
#
truncate() # $1 = length
{
cut -b -${1}
}
#
# Split a colon-separated list into a space-separated list
#
split() {
local ifs=$IFS
IFS=:
set -- $1
echo $*
IFS=$ifs
}
2005-07-09 06:45:32 +02:00
#
# Search a list looking for a match -- returns zero if a match found
# 1 otherwise
#
list_search() # $1 = element to search for , $2-$n = list
{
local e=$1
while [ $# -gt 1 ]; do
shift
[ "x$e" = "x$1" ] && return 0
done
return 1
}
2006-02-08 23:33:13 +01:00
#
# Return a space separated list of values matching
#
list_walk() # $1 = element to search for, $2-$n = list
{
local e=$1 result=
while [ $# -gt 1 ]; do
shift
case $1 in
$e*)
result="$result ${1##$e}"
;;
esac
done
echo $result
}
2005-07-09 06:45:32 +02:00
#
# Functions to count list elements
# - - - - - - - - - - - - - - - -
# Whitespace-separated list
#
list_count1() {
echo $#
}
#
# Comma-separated list
#
list_count() {
list_count1 $(separate_list $1)
}
#
# Conditionally produce message
#
progress_message() # $* = Message
{
2006-02-15 16:20:17 +01:00
local timestamp=
if [ $VERBOSE -gt 1 ]; then
[ -n "$TIMESTAMP" ] && timestamp="$(date +%H:%M:%S) "
echo "${timestamp}$@"
fi
2006-01-09 18:11:30 +01:00
}
progress_message2() # $* = Message
{
2006-02-15 16:20:17 +01:00
local timestamp=
if [ $VERBOSE -gt 0 ]; then
[ -n "$TIMESTAMP" ] && timestamp="$(date +%H:%M:%S) "
echo "${timestamp}$@"
fi
2005-07-09 06:45:32 +02:00
}
2002-05-01 01:13:15 +02:00
2006-01-23 21:44:31 +01:00
progress_message3() # $* = Message
{
2006-02-15 16:20:17 +01:00
local timestamp=
if [ $VERBOSE -ge 0 ]; then
[ -n "$TIMESTAMP" ] && timestamp="$(date +%H:%M:%S) "
echo "${timestamp}$@"
fi
2006-01-23 21:44:31 +01:00
}
2002-05-01 01:13:15 +02:00
#
# Suppress all output for a command
2003-02-23 15:10:37 +01:00
#
qt()
{
2002-05-01 01:13:15 +02:00
"$@" >/dev/null 2>&1
}
2005-08-01 22:35:28 +02:00
#
# Determine if Shorewall is "running"
#
shorewall_is_started() {
qt $IPTABLES -L shorewall -n
}
2002-05-01 01:13:15 +02:00
#
2005-07-09 06:45:32 +02:00
# Perform variable substitution on the passed argument and echo the result
2002-05-01 01:13:15 +02:00
#
2005-07-09 06:45:32 +02:00
expand() # $@ = contents of variable which may be the name of another variable
2002-05-01 01:13:15 +02:00
{
2005-07-09 06:45:32 +02:00
eval echo \"$@\"
}
#
# Perform variable substitition on the values of the passed list of variables
#
expandv() # $* = list of variable names
{
local varval
while [ $# -gt 0 ]; do
eval varval=\$${1}
eval $1=\"$varval\"
shift
done
}
#
2006-01-29 21:49:16 +01:00
# Add whitespace after "!"
2005-07-09 06:45:32 +02:00
#
2006-01-29 21:49:16 +01:00
fix_bang()
{
local result=
2005-07-09 06:45:32 +02:00
2006-01-29 21:49:16 +01:00
while [ $# -gt 0 ]; do
case $1 in
2006-01-14 19:35:50 +01:00
!*)
2006-01-29 21:49:16 +01:00
result="$result ! ${1#!}"
2006-01-14 19:35:50 +01:00
;;
*)
2006-01-29 21:49:16 +01:00
result="$result $1"
2006-01-14 19:35:50 +01:00
;;
esac
2006-01-29 21:49:16 +01:00
shift
2006-01-14 19:35:50 +01:00
done
2006-01-29 21:49:16 +01:00
echo $result
2005-07-09 06:45:32 +02:00
}
2006-01-25 18:33:38 +01:00
#
# Echos the fully-qualified name of the calling shell program
#
my_pathname() {
cd $(dirname $0)
echo $PWD/$(basename $0)
}
2005-07-09 06:45:32 +02:00
#
# Set default config path
#
ensure_config_path() {
local F=/usr/share/shorewall/configpath
if [ -z "$CONFIG_PATH" ]; then
[ -f $F ] || { echo " ERROR: $F does not exist"; exit 2; }
. $F
2002-05-01 01:13:15 +02:00
fi
}
2005-08-02 18:46:30 +02:00
2005-07-09 06:45:32 +02:00
#
# Find a File -- For relative file name, look first in $SHOREWALL_DIR then in /etc/shorewall
#
find_file()
{
local saveifs= directory
case $1 in
/*)
echo $1
;;
*)
if [ -n "$SHOREWALL_DIR" -a -f $SHOREWALL_DIR/$1 ]; then
echo $SHOREWALL_DIR/$1
else
saveifs=$IFS
IFS=:
for directory in $CONFIG_PATH; do
if [ -f $directory/$1 ]; then
echo $directory/$1
IFS=$saveifs
return
fi
done
2005-09-30 18:54:24 +02:00
2005-07-09 06:45:32 +02:00
IFS=$saveifs
2005-09-30 18:54:24 +02:00
2005-07-09 06:45:32 +02:00
echo /etc/shorewall/$1
fi
;;
esac
}
2002-05-01 01:13:15 +02:00
2005-09-09 18:52:10 +02:00
#
# Get fully-qualified name of file
#
resolve_file() # $1 = file name
{
local pwd=$PWD
case $1 in
/*)
echo $1
;;
./*)
echo ${pwd}${1#.}
;;
../*)
cd ..
echo ${PWD}${1#..}
cd $pwd
;;
*)
echo $pwd/$1
;;
esac
2005-09-30 18:54:24 +02:00
}
2005-09-09 18:52:10 +02:00
2006-01-17 21:03:00 +01:00
##
# Source a user exit file if it exists
#
run_user_exit() # $1 = file name
{
local user_exit=$(find_file $1)
if [ -f $user_exit ]; then
progress_message "Processing $user_exit ..."
. $user_exit
fi
}
2002-05-01 01:13:15 +02:00
#
# Replace commas with spaces and echo the result
#
2003-02-23 15:10:37 +01:00
separate_list() {
2005-07-09 07:55:29 +02:00
local list="$@"
2002-12-06 01:31:49 +01:00
local part
local newlist
2005-07-09 07:55:29 +02:00
local firstpart
local lastpart
local enclosure
2005-07-31 19:12:04 +02:00
2005-07-09 07:55:29 +02:00
case "$list" in
2003-02-23 16:56:45 +01:00
*,|,*|*,,*|*[[:space:]]*)
2005-07-31 19:12:04 +02:00
#
# There's been whining about us not catching embedded white space in
# comma-separated lists. This is an attempt to snag some of the cases.
#
2005-08-01 19:17:24 +02:00
# The 'TERMINATOR' function will be set by the 'firewall' script to
2005-07-31 19:12:04 +02:00
# either 'startup_error' or 'fatal_error' depending on the command and
# command phase
#
2005-08-01 19:17:24 +02:00
[ -n "$TERMINATOR" ] && \
$TERMINATOR "Invalid comma-separated list \"$@\""
2005-07-31 19:15:37 +02:00
echo "WARNING -- invalid comma-separated list \"$@\"" >&2
2003-02-23 15:10:37 +01:00
;;
2005-07-09 07:55:29 +02:00
*\[*\]*)
#
# Where we need to embed comma-separated lists within lists, we enclose them
2006-04-02 17:17:41 +02:00
# within square brackets (extra 'evals' are to keep my text editor (kate) from getting lost).
2005-07-09 07:55:29 +02:00
#
2006-04-02 17:17:41 +02:00
eval 'firstpart=${list%%\[*}'
eval 'lastpart=${list#*\[}'
eval 'enclosure=${lastpart%%\]*}'
eval 'lastpart=${lastpart#*\]}'
2005-07-09 07:55:29 +02:00
case $lastpart in
\,*)
2005-07-31 19:12:04 +02:00
case $firstpart in
*\,)
echo "$(separate_list ${firstpart%,}) [$enclosure] $(separate_list ${lastpart#,})"
;;
*)
echo "$(separate_list $firstpart)[$enclosure] $(separate_list ${lastpart#,})"
;;
esac
2005-07-09 07:55:29 +02:00
;;
*)
2005-07-31 19:12:04 +02:00
case $firstpart in
2005-09-30 18:54:24 +02:00
*\,)
2005-07-31 19:12:04 +02:00
echo "$(separate_list ${firstpart%,}) [$enclosure]$(separate_list $lastpart)"
;;
*)
echo "$(separate_list $firstpart)[$enclosure]$(separate_list $lastpart)"
;;
esac
;;
2005-07-09 07:55:29 +02:00
esac
return
;;
2003-02-23 15:10:37 +01:00
esac
2002-12-06 01:31:49 +01:00
list="$@"
part="${list%%,*}"
newlist="$part"
2003-02-23 15:10:37 +01:00
2002-12-06 01:31:49 +01:00
while [ "x$part" != "x$list" ]; do
list="${list#*,}";
part="${list%%,*}";
newlist="$newlist $part";
done
echo "$newlist"
2002-05-01 01:13:15 +02:00
}
2005-07-09 06:45:32 +02:00
#
# Load a Kernel Module
#
loadmodule() # $1 = module name, $2 - * arguments
{
local modulename=$1
local modulefile
local suffix
moduleloader=modprobe
2005-08-02 18:46:30 +02:00
if ! qt mywhich modprobe; then
2005-07-09 06:45:32 +02:00
moduleloader=insmod
fi
if [ -z "$(lsmod | grep $modulename)" ]; then
shift
2005-09-30 18:54:24 +02:00
2005-07-09 06:45:32 +02:00
for suffix in $MODULE_SUFFIX ; do
modulefile=$MODULESDIR/${modulename}.${suffix}
if [ -f $modulefile ]; then
case $moduleloader in
insmod)
insmod $modulefile $*
;;
*)
modprobe $modulename $*
;;
esac
return
fi
done
fi
}
#
# Reload the Modules
#
reload_kernel_modules() {
[ -z "$MODULESDIR" ] && MODULESDIR=/lib/modules/$(uname -r)/kernel/net/ipv4/netfilter
2005-08-02 18:46:30 +02:00
while read command; do
2005-07-09 06:45:32 +02:00
eval $command
done
}
2006-01-12 19:21:16 +01:00
#
# Load kernel modules required for Shorewall
#
load_kernel_modules()
{
save_modules_dir=$MODULESDIR
[ -z "$MODULESDIR" ] && \
MODULESDIR=/lib/modules/$(uname -r)/kernel/net/ipv4/netfilter
modules=$(find_file modules)
if [ -f $modules -a -d $MODULESDIR ]; then
progress_message "Loading Modules..."
. $modules
fi
MODULESDIR=$save_modules_dir
}
2002-10-23 18:48:40 +02:00
#
2002-05-01 01:13:15 +02:00
# Call this function to assert MUTEX with Shorewall. If you invoke the
# /sbin/shorewall program while holding MUTEX, you should pass "nolock" as
# the first argument. Example "shorewall nolock refresh"
#
# This function uses the lockfile utility from procmail if it exists.
# Otherwise, it uses a somewhat race-prone algorithm to attempt to simulate the
# behavior of lockfile.
2002-10-23 18:48:40 +02:00
#
2002-05-01 01:13:15 +02:00
mutex_on()
{
local try=0
2005-07-26 01:08:09 +02:00
local lockf=/var/lib/shorewall/lock
2002-05-01 01:13:15 +02:00
2002-07-24 05:47:34 +02:00
MUTEX_TIMEOUT=${MUTEX_TIMEOUT:-60}
2002-07-24 17:03:06 +02:00
if [ $MUTEX_TIMEOUT -gt 0 ]; then
2002-07-24 05:47:34 +02:00
2005-07-26 01:08:09 +02:00
[ -d /var/lib/shorewall ] || mkdir -p /var/lib/shorewall
2002-05-01 01:13:15 +02:00
2005-07-27 22:30:16 +02:00
if qt mywhich lockfile; then
2002-07-24 17:03:06 +02:00
lockfile -${MUTEX_TIMEOUT} -r1 ${lockf}
else
while [ -f ${lockf} -a ${try} -lt ${MUTEX_TIMEOUT} ] ; do
sleep 1
try=$((${try} + 1))
2002-05-01 01:13:15 +02:00
done
2002-07-24 17:03:06 +02:00
if [ ${try} -lt ${MUTEX_TIMEOUT} ] ; then
# Create the lockfile
echo $$ > ${lockf}
else
echo "Giving up on lock file ${lockf}" >&2
fi
2002-05-01 01:13:15 +02:00
fi
fi
}
2002-10-23 18:48:40 +02:00
#
2002-05-01 01:13:15 +02:00
# Call this function to release MUTEX
2002-10-23 18:48:40 +02:00
#
2002-05-01 01:13:15 +02:00
mutex_off()
{
2005-07-26 01:08:09 +02:00
rm -f /var/lib/shorewall/lock
2002-05-01 01:13:15 +02:00
}
2005-07-09 06:45:32 +02:00
#
# Determine which version of mktemp is present (if any) and set MKTEMP accortingly:
#
# None - No mktemp
# BSD - BSD mktemp (Mandrake)
# STD - mktemp.org mktemp
#
find_mktemp() {
2005-07-27 22:30:16 +02:00
local mktemp=`mywhich mktemp 2> /dev/null`
2005-07-09 06:45:32 +02:00
if [ -n "$mktemp" ]; then
if qt mktemp -V ; then
MKTEMP=STD
else
MKTEMP=BSD
fi
else
MKTEMP=None
fi
}
#
# create a temporary file. If a directory name is passed, the file will be created in
# that directory. Otherwise, it will be created in a temporary directory.
#
mktempfile() {
[ -z "$MKTEMP" ] && find_mktemp
if [ $# -gt 0 ]; then
case "$MKTEMP" in
BSD)
mktemp $1/shorewall.XXXXXX
;;
STD)
mktemp -p $1 shorewall.XXXXXX
;;
None)
> $1/shorewall-$$ && echo $1/shorewall-$$
;;
*)
2005-07-09 07:45:05 +02:00
echo " ERROR:Internal error in mktempfile" >&2
2005-07-09 06:45:32 +02:00
;;
esac
else
case "$MKTEMP" in
BSD)
mktemp /tmp/shorewall.XXXXXX
;;
STD)
mktemp -t shorewall.XXXXXX
;;
None)
rm -f /tmp/shorewall-$$
> /tmp/shorewall-$$ && echo /tmp/shorewall-$$
;;
*)
2005-07-09 07:45:05 +02:00
echo " ERROR:Internal error in mktempfile" >&2
2005-07-09 06:45:32 +02:00
;;
esac
fi
}
#
# create a temporary directory
#
mktempdir() {
2005-08-02 18:46:30 +02:00
[ -z "$MKTEMP" ] && find_mktemp
2005-07-09 06:45:32 +02:00
case "$MKTEMP" in
STD)
mktemp -td shorewall.XXXXXX
;;
None|BSD)
#
# Not all versions of the BSD mktemp support the -d option under Linux
#
2006-04-04 21:45:42 +02:00
qt rm -rf /tmp/shorewall-$$
mkdir -p /tmp/shorewall-$$ chmod 700 /tmp/shorewall-$$ && echo /tmp/shorewall-$$
2005-07-09 06:45:32 +02:00
;;
*)
2005-07-09 07:45:05 +02:00
echo " ERROR:Internal error in mktempdir" >&2
2005-07-09 06:45:32 +02:00
;;
esac
2005-09-30 18:54:24 +02:00
}
2005-07-09 06:45:32 +02:00
2003-03-29 15:37:50 +01:00
#
# Read a file and handle "INCLUDE" directives
#
2003-03-29 16:15:05 +01:00
read_file() # $1 = file name, $2 = nest count
2003-03-29 15:37:50 +01:00
{
local first rest
2005-07-09 06:45:32 +02:00
if [ -f $1 ]; then
while read first rest; do
if [ "x$first" = "xINCLUDE" ]; then
if [ $2 -lt 4 ]; then
read_file $(find_file $(expand ${rest%#*})) $(($2 + 1))
else
echo " WARNING: INCLUDE in $1 ignored (nested too deeply)" >&2
fi
2003-03-29 16:15:05 +01:00
else
2005-07-09 06:45:32 +02:00
echo "$first $rest"
2003-03-29 16:15:05 +01:00
fi
2005-07-09 06:45:32 +02:00
done < $1
else
2005-08-01 19:17:24 +02:00
[ -n "$TERMINATOR" ] && $TERMINATOR "No such file: $1"
2005-07-31 19:15:37 +02:00
echo "WARNING -- No such file: $1"
2005-07-09 06:45:32 +02:00
fi
2003-03-29 15:37:50 +01:00
}
#
# Function for including one file into another
#
INCLUDE() {
2005-07-09 06:45:32 +02:00
. $(find_file $(expand $@))
2003-03-29 15:37:50 +01:00
}
2002-10-23 18:48:40 +02:00
#
# Strip comments and blank lines from a file and place the result in the
# temporary directory
#
2002-05-01 01:13:15 +02:00
strip_file() # $1 = Base Name of the file, $2 = Full Name of File (optional)
{
local fname
2005-07-09 06:45:32 +02:00
[ $# = 1 ] && fname=$(find_file $1) || fname=$2
2002-05-01 01:13:15 +02:00
if [ -f $fname ]; then
2003-03-29 16:15:05 +01:00
read_file $fname 0 | cut -d'#' -f1 | grep -v '^[[:space:]]*$' > $TMP_DIR/$1
2002-05-01 01:13:15 +02:00
else
> $TMP_DIR/$1
fi
}
2003-07-05 18:24:41 +02:00
2003-07-05 19:41:28 +02:00
#
# Note: The following set of IP address manipulation functions have anomalous
# behavior when the shell only supports 32-bit signed arithmatic and
# the IP address is 128.0.0.0 or 128.0.0.1.
#
2003-07-05 18:24:41 +02:00
2006-05-10 17:20:36 +02:00
LEFTSHIFT='<<'
2003-07-05 18:24:41 +02:00
#
# Convert an IP address in dot quad format to an integer
#
decodeaddr() {
local x
local temp=0
local ifs=$IFS
2005-08-02 18:46:30 +02:00
2003-07-05 18:24:41 +02:00
IFS=.
for x in $1; do
2006-05-10 17:20:36 +02:00
temp=$(( $(( $temp $LEFTSHIFT 8 )) | $x ))
2003-07-05 18:24:41 +02:00
done
echo $temp
IFS=$ifs
}
#
# convert an integer to dot quad format
#
encodeaddr() {
addr=$1
local x
local y=$(($addr & 255))
for x in 1 2 3 ; do
addr=$(($addr >> 8))
y=$(($addr & 255)).$y
done
echo $y
}
#
# Enumerate the members of an IP range -- When using a shell supporting only
# 32-bit signed arithmetic, the range cannot span 128.0.0.0.
#
2003-08-28 20:02:59 +02:00
# Comes in two flavors:
#
2005-08-02 18:46:30 +02:00
# ip_range() - produces a mimimal list of network/host addresses that spans
2003-08-28 20:02:59 +02:00
# the range.
#
# ip_range_explicit() - explicitly enumerates the range.
#
2003-07-05 18:24:41 +02:00
ip_range() {
2003-07-06 15:24:23 +02:00
local first last l x y z vlsm
2003-07-05 18:24:41 +02:00
2005-07-09 07:45:05 +02:00
case $1 in
!*)
#
# Let iptables complain if it's a range
#
echo $1
return
;;
[0-9]*.*.*.*-*.*.*.*)
;;
*)
echo $1
return
;;
2003-07-05 18:24:41 +02:00
esac
2005-07-09 06:45:32 +02:00
first=$(decodeaddr ${1%-*})
last=$(decodeaddr ${1#*-})
2003-07-05 18:24:41 +02:00
2003-07-06 15:24:23 +02:00
if [ $first -gt $last ]; then
2003-07-05 18:24:41 +02:00
fatal_error "Invalid IP address range: $1"
fi
2003-07-06 15:24:23 +02:00
l=$(( $last + 1 ))
2003-07-05 18:24:41 +02:00
while [ $first -le $last ]; do
2003-07-06 15:24:23 +02:00
vlsm=
x=31
y=2
z=1
while [ $(( $first % $y )) -eq 0 -a $(( $first + $y )) -le $l ]; do
2003-07-06 16:34:46 +02:00
vlsm=/$x
2003-07-06 15:24:23 +02:00
x=$(( $x - 1 ))
z=$y
y=$(( $y * 2 ))
done
2005-07-09 06:45:32 +02:00
echo $(encodeaddr $first)$vlsm
2003-07-06 15:24:23 +02:00
first=$(($first + $z))
2003-07-05 18:24:41 +02:00
done
}
2003-08-27 17:13:03 +02:00
ip_range_explicit() {
local first last
2005-08-02 18:46:30 +02:00
case $1 in
2003-08-27 17:13:03 +02:00
[0-9]*.*.*.*-*.*.*.*)
;;
*)
echo $1
return
;;
esac
2005-07-09 06:45:32 +02:00
first=$(decodeaddr ${1%-*})
last=$(decodeaddr ${1#*-})
2003-08-27 17:13:03 +02:00
if [ $first -gt $last ]; then
fatal_error "Invalid IP address range: $1"
fi
while [ $first -le $last ]; do
2005-07-09 06:45:32 +02:00
echo $(encodeaddr $first)
2003-08-27 17:13:03 +02:00
first=$(($first + 1))
done
}
2003-07-05 18:24:41 +02:00
#
# Netmask from CIDR
#
ip_netmask() {
2003-07-05 19:41:28 +02:00
local vlsm=${1#*/}
2006-05-10 17:20:36 +02:00
[ $vlsm -eq 0 ] && echo 0 || echo $(( -1 $LEFTSHIFT $(( 32 - $vlsm )) ))
2003-07-05 18:24:41 +02:00
}
#
# Network address from CIDR
#
ip_network() {
2005-07-09 06:45:32 +02:00
local decodedaddr=$(decodeaddr ${1%/*})
local netmask=$(ip_netmask $1)
2003-07-05 18:24:41 +02:00
2005-07-09 06:45:32 +02:00
echo $(encodeaddr $(($decodedaddr & $netmask)))
2003-07-05 18:24:41 +02:00
}
#
# The following hack is supplied to compensate for the fact that many of
# the popular light-weight Bourne shell derivatives don't support XOR ("^").
#
ip_broadcast() {
2003-08-07 19:31:47 +02:00
local x=$(( 32 - ${1#*/} ))
2003-07-05 18:24:41 +02:00
2006-05-10 17:20:36 +02:00
[ $x -eq 0 ] && echo -1 || echo $(( $(( 1 $LEFTSHIFT $x )) - 1 ))
2003-07-05 18:24:41 +02:00
}
#
# Calculate broadcast address from CIDR
#
broadcastaddress() {
2005-07-09 06:45:32 +02:00
local decodedaddr=$(decodeaddr ${1%/*})
local netmask=$(ip_netmask $1)
local broadcast=$(ip_broadcast $1)
2003-07-05 18:24:41 +02:00
2005-07-09 06:45:32 +02:00
echo $(encodeaddr $(( $(($decodedaddr & $netmask)) | $broadcast )))
2003-07-05 18:24:41 +02:00
}
#
2005-07-09 06:45:32 +02:00
# Test for network membership
2003-07-05 18:24:41 +02:00
#
2005-07-09 06:45:32 +02:00
in_network() # $1 = IP address, $2 = CIDR network
2003-07-05 18:24:41 +02:00
{
2005-07-09 06:45:32 +02:00
local netmask=$(ip_netmask $2)
2003-07-05 18:24:41 +02:00
2005-07-09 06:45:32 +02:00
test $(( $(decodeaddr $1) & $netmask)) -eq $(( $(decodeaddr ${2%/*}) & $netmask ))
2003-07-05 18:24:41 +02:00
}
#
2003-07-05 19:23:53 +02:00
# Netmask to VLSM
2003-07-05 18:24:41 +02:00
#
2003-07-05 19:23:53 +02:00
ip_vlsm() {
2005-07-09 06:45:32 +02:00
local mask=$(decodeaddr $1)
2003-07-05 19:23:53 +02:00
local vlsm=0
2006-04-02 17:17:41 +02:00
local x=$(( 128 << 24 )) # 0x80000000
2003-07-05 18:24:41 +02:00
while [ $(( $x & $mask )) -ne 0 ]; do
2006-05-10 17:20:36 +02:00
[ $mask -eq $x ] && mask=0 || mask=$(( $mask $LEFTSHIFT 1 )) # Not all shells shift 0x80000000 left properly.
2003-07-05 19:23:53 +02:00
vlsm=$(($vlsm + 1))
2003-07-05 18:24:41 +02:00
done
2005-07-09 06:45:32 +02:00
if [ $(( $mask & 2147483647 )) -ne 0 ]; then # 2147483647 = 0x7fffffff
2003-07-05 19:14:21 +02:00
echo "Invalid net mask: $1" >&2
2003-07-05 18:24:41 +02:00
else
2003-07-05 19:23:53 +02:00
echo $vlsm
2003-07-05 18:24:41 +02:00
fi
}
2003-07-06 01:41:41 +02:00
2003-07-22 00:02:34 +02:00
#
2003-08-22 17:27:08 +02:00
# Chain name base for an interface -- replace all periods with underscores in the passed name.
2005-07-09 06:45:32 +02:00
# The result is echoed (less trailing "+").
2003-07-22 00:02:34 +02:00
#
chain_base() #$1 = interface
{
2005-07-09 06:45:32 +02:00
local c=${1%%+}
2003-07-22 00:02:34 +02:00
2003-08-21 16:48:49 +02:00
while true; do
case $c in
*.*)
c="${c%.*}_${c##*.}"
;;
2003-10-25 02:54:01 +02:00
*-*)
c="${c%-*}_${c##*-}"
;;
2005-07-09 07:45:05 +02:00
*%*)
c="${c%\%*}_${c##*%}"
;;
2003-08-21 16:48:49 +02:00
*)
echo ${c:=common}
return
;;
esac
done
2003-07-22 00:02:34 +02:00
}
#
# Loosly Match the name of an interface
#
if_match() # $1 = Name in interfaces file - may end in "+"
2005-07-09 06:45:32 +02:00
# $2 = Full interface name - may also end in "+"
2003-07-22 00:02:34 +02:00
{
2005-07-09 06:45:32 +02:00
local pattern=${1%+}
2005-08-02 18:46:30 +02:00
2005-07-09 06:45:32 +02:00
case $1 in
2003-07-22 00:02:34 +02:00
*+)
2005-07-09 07:45:05 +02:00
test "x$(echo $2 | truncate ${#pattern} )" = "x${pattern}"
2003-07-22 00:02:34 +02:00
;;
*)
2005-07-09 06:45:32 +02:00
test "x$1" = "x$2"
2003-07-22 00:02:34 +02:00
;;
esac
}
2006-01-17 21:03:00 +01:00
#
# Source IP range
#
source_ip_range() # $1 = Address or Address Range
{
2006-04-02 17:17:41 +02:00
[ $# -gt 0 ] && case $1 in
2006-01-17 21:03:00 +01:00
*.*.*.*-*.*.*.*)
case $1 in
!*)
iprange_echo "! --src-range ${1#!}"
;;
*)
iprange_echo "--src-range $1"
;;
esac
;;
!+*)
echo "-m set ! $(get_set_flags ${1#!} src)"
;;
+*)
echo "-m set $(get_set_flags $1 src)"
;;
*)
echo "-s $1"
;;
esac
}
#
# Destination IP range
#
dest_ip_range() # $1 = Address or Address Range
{
2006-04-02 17:17:41 +02:00
[ $# -gt 0 ] && case $1 in
2006-01-17 21:03:00 +01:00
*.*.*.*-*.*.*.*)
case $1 in
!*)
iprange_echo "! --dst-range ${1#!}"
;;
*)
iprange_echo "--dst-range $1"
;;
esac
;;
!+*)
echo "-m set ! $(get_set_flags ${1#!} dst)"
;;
+*)
echo "-m set $(get_set_flags $1 dst)"
;;
*)
echo "-d $1"
;;
esac
}
both_ip_ranges() # $1 = Source address or range, $2 = dest address or range
{
local rangeprefix= setprefix= rangematch= setmatch=
case $1 in
*.*.*.*-*.*.*.*)
rangeprefix="-m iprange"
rangematch="--src-range $1"
;;
!+*)
setprefix="-m set"
setmatch="! $(get_set_flags ${1#!} src)"
;;
+*)
setprefix="-m set"
setmatch="$(get_set_flags $1 src)"
;;
*)
rangematch="-s $1"
;;
esac
case $2 in
*.*.*.*-*.*.*.*)
rangeprefix="-m iprange"
rangematch="$rangematch --dst-range $2"
;;
!+*)
setprefix="-m set"
match="$setmatch ! $(get_set_flags ${2#!} dst)"
;;
+*)
setprefix="-m set"
setmatch="$setmatch $(get_set_flags $2 dst)"
;;
*)
rangematch="$rangematch -d $2"
;;
esac
echo "$rangeprefix $rangematch $setprefix $setmatch"
}
2003-07-22 00:02:34 +02:00
#
# Find the value 'dev' in the passed arguments then echo the next value
#
find_device() {
while [ $# -gt 1 ]; do
[ "x$1" = xdev ] && echo $2 && return
shift
done
}
2005-08-02 18:46:30 +02:00
2005-07-09 07:55:29 +02:00
#
# Find the value 'via' in the passed arguments then echo the next value
#
find_gateway() {
while [ $# -gt 1 ]; do
[ "x$1" = xvia ] && echo $2 && return
shift
done
}
2005-08-02 18:46:30 +02:00
2006-05-10 18:21:31 +02:00
#
# Find the value 'mtu' in the passed arguments then echo the next value
#
find_mtu() {
while [ $# -gt 1 ]; do
[ "x$1" = xmtu ] && echo $2 && return
shift
done
}
2005-07-09 07:55:29 +02:00
#
# Find the value 'peer' in the passed arguments then echo the next value up to
# "/"
#
find_peer() {
while [ $# -gt 1 ]; do
[ "x$1" = xpeer ] && echo ${2%/*} && return
shift
done
}
2005-08-02 18:46:30 +02:00
2003-07-22 00:02:34 +02:00
#
# Find the interfaces that have a route to the passed address - the default
# route is not used.
#
find_rt_interface() {
ip route ls | while read addr rest; do
case $addr in
*/*)
2005-07-09 06:45:32 +02:00
in_network ${1%/*} $addr && echo $(find_device $rest)
2003-07-22 00:02:34 +02:00
;;
default)
;;
*)
if [ "$addr" = "$1" -o "$addr/32" = "$1" ]; then
2005-07-09 06:45:32 +02:00
echo $(find_device $rest)
2003-07-22 00:02:34 +02:00
fi
;;
esac
done
}
2005-07-09 07:55:29 +02:00
#
2005-08-02 18:46:30 +02:00
# Try to find the gateway through an interface looking for 'nexthop'
2005-07-09 07:55:29 +02:00
find_nexthop() # $1 = interface
{
echo $(find_gateway `ip route ls | grep "[[:space:]]nexthop.* $1"`)
2005-08-02 18:46:30 +02:00
}
2005-07-09 07:55:29 +02:00
2003-07-22 00:02:34 +02:00
#
# Find the default route's interface
#
find_default_interface() {
ip route ls | while read first rest; do
2005-07-09 06:45:32 +02:00
[ "$first" = default ] && echo $(find_device $rest) && return
2003-07-22 00:02:34 +02:00
done
}
#
# Echo the name of the interface(s) that will be used to send to the
# passed address
#
find_interface_by_address() {
2005-07-09 06:45:32 +02:00
local dev="$(find_rt_interface $1)"
2003-07-22 00:02:34 +02:00
local first rest
2005-07-09 06:45:32 +02:00
[ -z "$dev" ] && dev=$(find_default_interface)
2003-07-22 00:02:34 +02:00
[ -n "$dev" ] && echo $dev
}
2005-08-17 22:07:47 +02:00
#
# Find the interface with the passed MAC address
#
find_interface_by_mac() {
local mac=$1 first second rest dev
ip link ls | while read first second rest; do
case $first in
*:)
dev=$second
;;
*)
if [ "$second" = $mac ]; then
echo ${dev%:}
return
fi
esac
done
}
2006-01-12 19:21:16 +01:00
#
# Find interface address--returns the first IP address assigned to the passed
# device
#
find_first_interface_address() # $1 = interface
{
#
# get the line of output containing the first IP address
#
addr=$(ip -f inet addr show $1 2> /dev/null | grep 'inet .* global' | head -n1)
#
# If there wasn't one, bail out now
#
[ -n "$addr" ] || fatal_error "Can't determine the IP address of $1"
#
# Strip off the trailing VLSM mask (or the peer IP in case of a P-t-P link)
# along with everything else on the line
#
echo $addr | sed 's/inet //;s/\/.*//;s/ peer.*//'
}
2006-04-26 00:46:36 +02:00
find_first_interface_address_if_any() # $1 = interface
{
#
# get the line of output containing the first IP address
#
addr=$(ip -f inet addr show $1 2> /dev/null | grep 'inet .* global' | head -n1)
#
# Strip off the trailing VLSM mask (or the peer IP in case of a P-t-P link)
# along with everything else on the line
#
[ -n "$addr" ] && echo $addr | sed 's/inet //;s/\/.*//;s/ peer.*//' || echo 0.0.0.0
}
2005-07-09 07:45:05 +02:00
#
# Find interface addresses--returns the set of addresses assigned to the passed
# device
#
find_interface_addresses() # $1 = interface
{
2006-02-21 17:46:21 +01:00
ip -f inet addr show $1 | grep inet\ | sed 's/inet //;s/\/.*//;s/ peer.*//'
2005-07-09 07:45:05 +02:00
}
2005-07-27 22:30:16 +02:00
2006-01-14 01:04:00 +01:00
#
# echo the list of networks routed out of a given interface
#
get_routed_networks() # $1 = interface name
{
local address
local rest
ip route show dev $1 2> /dev/null |
while read address rest; do
if [ "x$address" = xdefault ]; then
error_message "WARNING: default route ignored on interface $1"
else
[ "$address" = "${address%/*}" ] && address="${address}/32"
echo $address
fi
done
}
2005-07-27 22:30:16 +02:00
#
# Internal version of 'which'
#
mywhich() {
local dir
for dir in $(split $PATH); do
if [ -x $dir/$1 ]; then
echo $dir/$1
return 0
fi
done
return 2
}
2005-07-29 20:32:50 +02:00
#
# Set the Shorewall state
#
set_state () # $1 = state
{
echo "$1 ($(date))" > /var/lib/shorewall/state
}
2005-09-09 00:30:32 +02:00
2005-09-30 18:54:24 +02:00
#
# Determine which optional facilities are supported by iptables/netfilter
#
determine_capabilities() {
qt $IPTABLES -t nat -L -n && NAT_ENABLED=Yes || NAT_ENABLED=
qt $IPTABLES -t mangle -L -n && MANGLE_ENABLED=Yes || MANGLE_ENABLED=
CONNTRACK_MATCH=
MULTIPORT=
XMULTIPORT=
POLICY_MATCH=
PHYSDEV_MATCH=
IPRANGE_MATCH=
RECENT_MATCH=
OWNER_MATCH=
IPSET_MATCH=
CONNMARK=
2006-04-04 21:45:42 +02:00
XCONNMARK=
2005-09-30 18:54:24 +02:00
CONNMARK_MATCH=
2006-04-04 21:45:42 +02:00
XCONNMARK_MATCH=
2005-09-30 18:54:24 +02:00
RAW_TABLE=
2005-10-04 18:20:30 +02:00
IPP2P_MATCH=
2006-01-15 23:54:12 +01:00
LENGTH_MATCH=
2005-10-06 01:22:39 +02:00
CLASSIFY_TARGET=
2006-01-07 00:02:55 +01:00
ENHANCED_REJECT=
2006-01-21 00:34:19 +01:00
USEPKTTYPE=
2006-03-29 21:24:33 +02:00
KLUDGEFREE=
2006-04-05 04:08:33 +02:00
MARK=
XMARK=
2006-04-13 17:00:42 +02:00
MANGLE_FORWARD=
2005-09-30 18:54:24 +02:00
qt $IPTABLES -N fooX1234
qt $IPTABLES -A fooX1234 -m conntrack --ctorigdst 192.168.1.1 -j ACCEPT && CONNTRACK_MATCH=Yes
qt $IPTABLES -A fooX1234 -p tcp -m multiport --dports 21,22 -j ACCEPT && MULTIPORT=Yes
qt $IPTABLES -A fooX1234 -p tcp -m multiport --dports 21:22 -j ACCEPT && XMULTIPORT=Yes
2006-01-20 16:03:35 +01:00
qt $IPTABLES -A fooX1234 -m policy --pol ipsec --mode tunnel --dir in -j ACCEPT && POLICY_MATCH=Yes
2006-03-29 21:24:33 +02:00
if qt $IPTABLES -A fooX1234 -m physdev --physdev-in eth0 -j ACCEPT; then
PHYSDEV_MATCH=Yes
qt $IPTABLES -A fooX1234 -m physdev --physdev-in eth1 -m physdev --physdev-out eth1 -j ACCEPT && KLUDGEFREE=Yes
fi
if qt $IPTABLES -A fooX1234 -m iprange --src-range 192.168.1.5-192.168.1.124 -j ACCEPT; then
IPRANGE_MATCH=Yes
if [ -z "${KLUDGEFREE}${PHYSDEV_MATCH}" ]; then
qt $IPTABLES -A fooX1234 -m iprange --src-range 192.168.1.5-192.168.1.124 -m iprange --dst-range 192.168.1.5-192.168.1.124 -j ACCEPT && KLUDGEFREE=Yes
fi
fi
2006-04-13 17:00:42 +02:00
qt $IPTABLES -A fooX1234 -m recent --update -j ACCEPT && RECENT_MATCH=Yes
qt $IPTABLES -A fooX1234 -m owner --uid-owner 0 -j ACCEPT && OWNER_MATCH=Yes
2006-04-04 21:45:42 +02:00
if qt $IPTABLES -A fooX1234 -m connmark --mark 2 -j ACCEPT; then
CONNMARK_MATCH=Yes
qt $IPTABLES -A fooX1234 -m connmark --mark 2/0xFF -j ACCEPT && XCONNMARK_MATCH=Yes
fi
2006-04-13 17:00:42 +02:00
qt $IPTABLES -A fooX1234 -p tcp -m ipp2p --ipp2p -j ACCEPT && IPP2P_MATCH=Yes
qt $IPTABLES -A fooX1234 -m length --length 10:20 -j ACCEPT && LENGTH_MATCH=Yes
qt $IPTABLES -A fooX1234 -j REJECT --reject-with icmp-host-prohibited && ENHANCED_REJECT=Yes
2005-09-30 18:54:24 +02:00
2006-04-13 17:00:42 +02:00
if [ -n "$MANGLE_ENABLED" ]; then
qt $IPTABLES -t mangle -N fooX1234
2006-04-04 21:45:42 +02:00
2006-04-13 17:00:42 +02:00
if qt $IPTABLES -t mangle -A fooX1234 -j MARK --set-mark 1; then
MARK=Yes
qt $IPTABLES -t mangle -A fooX1234 -j MARK --and-mark 0xFF && XMARK=Yes
fi
2006-04-05 04:08:33 +02:00
2006-04-13 17:00:42 +02:00
if qt $IPTABLES -t mangle -A fooX1234 -j CONNMARK --save-mark; then
CONNMARK=Yes
qt $IPTABLES -t mangle -A fooX1234 -j CONNMARK --save-mark --mask 0xFF && XCONNMARK=Yes
fi
2006-04-04 21:45:42 +02:00
2006-04-13 17:00:42 +02:00
qt $IPTABLES -t mangle -A fooX1234 -j CLASSIFY --set-class 1:1 && CLASSIFY_TARGET=Yes
qt $IPTABLES -t mangle -F fooX1234
qt $IPTABLES -t mangle -X fooX1234
qt $IPTABLES -t mangle -L FORWARD -n && MANGLE_FORWARD=Yes
fi
2005-09-30 18:54:24 +02:00
2006-04-13 17:00:42 +02:00
qt $IPTABLES -t raw -L -n && RAW_TABLE=Yes
2005-09-30 18:54:24 +02:00
if qt mywhich ipset; then
qt ipset -X fooX1234 # Just in case something went wrong the last time
if qt ipset -N fooX1234 iphash ; then
if qt $IPTABLES -A fooX1234 -m set --set fooX1234 src -j ACCEPT; then
qt $IPTABLES -D fooX1234 -m set --set fooX1234 src -j ACCEPT
IPSET_MATCH=Yes
fi
qt ipset -X fooX1234
fi
fi
qt $IPTABLES -A fooX1234 -m pkttype --pkt-type broadcast -j ACCEPT && USEPKTTYPE=Yes
qt $IPTABLES -F fooX1234
qt $IPTABLES -X fooX1234
}
report_capability() # $1 = Capability Description , $2 Capability Setting (if any)
{
local setting=
[ "x$2" = "xYes" ] && setting="Available" || setting="Not available"
echo " " $1: $setting
}
report_capabilities() {
2006-01-23 02:41:24 +01:00
if [ $VERBOSE -gt 1 ]; then
2006-01-09 18:11:30 +01:00
echo "Shorewall has detected the following iptables/netfilter capabilities:"
report_capability "NAT" $NAT_ENABLED
report_capability "Packet Mangling" $MANGLE_ENABLED
report_capability "Multi-port Match" $MULTIPORT
[ -n "$MULTIPORT" ] && report_capability "Extended Multi-port Match" $XMULTIPORT
report_capability "Connection Tracking Match" $CONNTRACK_MATCH
report_capability "Packet Type Match" $USEPKTTYPE
report_capability "Policy Match" $POLICY_MATCH
report_capability "Physdev Match" $PHYSDEV_MATCH
2006-01-15 23:54:12 +01:00
report_capability "Packet length Match" $LENGTH_MATCH
2006-01-09 18:11:30 +01:00
report_capability "IP range Match" $IPRANGE_MATCH
report_capability "Recent Match" $RECENT_MATCH
report_capability "Owner Match" $OWNER_MATCH
report_capability "Ipset Match" $IPSET_MATCH
report_capability "CONNMARK Target" $CONNMARK
2006-04-05 04:08:33 +02:00
[ -n "$CONNMARK" ] && report_capability "Extended CONNMARK Target" $XCONNMARK
2006-01-09 18:11:30 +01:00
report_capability "Connmark Match" $CONNMARK_MATCH
2006-04-05 04:08:33 +02:00
[ -n "$CONNMARK_MATCH" ] && report_capability "Extended Connmark Match" $XCONNMARK_MATCH
2006-01-09 18:11:30 +01:00
report_capability "Raw Table" $RAW_TABLE
2006-01-21 00:34:19 +01:00
report_capability "IPP2P Match" $IPP2P_MATCH
2006-01-09 18:11:30 +01:00
report_capability "CLASSIFY Target" $CLASSIFY_TARGET
report_capability "Extended REJECT" $ENHANCED_REJECT
2006-03-29 21:24:33 +02:00
report_capability "Repeat match" $KLUDGEFREE
2006-04-05 04:08:33 +02:00
report_capability "MARK Target" $MARK
[ -n "$MARK" ] && report_capability "Extended MARK Target" $XMARK
2006-04-13 17:00:42 +02:00
report_capability "Mangle FORWARD Chain" $MANGLE_FORWARD
2006-01-09 18:11:30 +01:00
fi
2005-09-30 18:54:24 +02:00
[ -n "$PKTTYPE" ] || USEPKTTYPE=
2005-10-04 18:20:30 +02:00
2005-09-30 18:54:24 +02:00
}
2006-04-26 00:46:36 +02:00
#
# Delete IP address
#
del_ip_addr() # $1 = address, $2 = interface
{
[ $(find_first_interface_address_if_any $2) = $1 ] || qt ip addr del $1 dev $2
}
2006-01-12 19:21:16 +01:00
# Add IP Aliases
#
2006-01-15 03:19:04 +01:00
add_ip_aliases() # $* = List of addresses
2006-01-12 19:21:16 +01:00
{
local addresses external interface inet cidr rest val arping=$(mywhich arping)
address_details()
{
#
# Folks feel uneasy if they don't see all of the same
# decoration on these IP addresses that they see when their
# distro's net config tool adds them. In an attempt to reduce
# the anxiety level, we have the following code which sets
# the VLSM and BRD from an existing address in the same networks
#
# Get all of the lines that contain inet addresses with broadcast
#
ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | while read inet cidr rest ; do
case $cidr in
*/*)
if in_network $external $cidr; then
echo "/${cidr#*/} brd $(broadcastaddress $cidr)"
break
fi
;;
esac
done
}
do_one()
{
val=$(address_details)
ip addr add ${external}${val} dev $interface $label
[ -n "$arping" ] && qt $arping -U -c 2 -I $interface $external
echo "$external $interface" >> $STATEDIR/nat
[ -n "$label" ] && label="with $label"
progress_message " IP Address $external added to interface $interface $label"
}
progress_message "Adding IP Addresses..."
while [ $# -gt 0 ]; do
external=$1
interface=$2
label=
if [ "$interface" != "${interface%:*}" ]; then
label="${interface#*:}"
interface="${interface%:*}"
label="label $interface:$label"
fi
shift 2
list_search $external $(find_interface_addresses $interface) || do_one
done
}
detect_gateway() # $1 = interface
{
local interface=$1
#
# First assume that this is some sort of point-to-point interface
#
gateway=$( find_peer $(ip addr ls $interface ) )
#
# Maybe there's a default route through this gateway already
#
[ -n "$gateway" ] || gateway=$(find_gateway $(ip route ls dev $interface))
#
# Last hope -- is there a load-balancing route through the interface?
#
[ -n "$gateway" ] || gateway=$(find_nexthop $interface)
#
# Be sure we found one
#
[ -n "$gateway" ] && echo $gateway
}
2005-09-30 18:54:24 +02:00
2006-01-13 00:26:37 +01:00
#
# Disable IPV6
#
disable_ipv6() {
local foo="$(ip -f inet6 addr ls 2> /dev/null)"
if [ -n "$foo" ]; then
if qt mywhich ip6tables; then
2006-01-13 00:45:33 +01:00
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
ip6tables -F
ip6tables -X
ip6tables -A OUTPUT -o lo -j ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT
2006-01-13 00:26:37 +01:00
else
error_message "WARNING: DISABLE_IPV6=Yes in shorewall.conf but this system does not appear to have ip6tables"
fi
fi
}
2006-01-13 20:29:23 +01:00
#
# Add a logging rule.
#
log_rule_limit() # $1 = log level, $2 = chain, $3 = display Chain $4 = disposition , $5 = rate limit $6=log tag $7=command $... = predicates for the rule
{
local level=$1
local chain=$2
local displayChain=$3
local disposition=$4
local rulenum=
local limit="${5:-$LOGLIMIT}"
local tag=${6:+$6 }
local command=${7:--A}
local prefix
local base=$(chain_base $displayChain)
shift 7
if [ -n "$tag" -a -n "$LOGTAGONLY" ]; then
displayChain=$tag
tag=
fi
if [ -n "$LOGRULENUMBERS" ]; then
eval rulenum=\$${base}_logrules
rulenum=${rulenum:-1}
prefix="$(printf "$LOGFORMAT" $displayChain $rulenum $disposition)${tag}"
rulenum=$(($rulenum + 1))
eval ${base}_logrules=$rulenum
else
prefix="$(printf "$LOGFORMAT" $displayChain $disposition)${tag}"
fi
if [ ${#prefix} -gt 29 ]; then
prefix="$(echo $prefix | truncate 29)"
error_message "WARNING: Log Prefix shortened to \"$prefix\""
fi
case $level in
ULOG)
run_iptables $command $chain $@ $limit -j ULOG $LOGPARMS --ulog-prefix "$prefix"
;;
*)
2006-05-10 17:20:36 +02:00
run_iptables $command $chain $@ $limit -j LOG $LOGPARMS --log-level $level --log-prefix "$prefix"
2006-01-13 20:29:23 +01:00
;;
esac
if [ $? -ne 0 ] ; then
[ -z "$STOPPING" ] && { stop_firewall; exit 2; }
fi
}
log_rule() # $1 = log level, $2 = chain, $3 = disposition , $... = predicates for the rule
{
local level=$1
local chain=$2
local disposition=$3
shift 3
log_rule_limit $level $chain $chain $disposition "$LOGLIMIT" "" -A $@
}
2006-05-06 18:15:33 +02:00
#
# Check that a mark value or mask is less that 256 or that it is less than 65536 and
# that it's lower 8 bits are zero.
#
verify_mark() # $1 = value to test
{
verify_mark2()
{
case $1 in
0*)
[ $(($1)) -lt 256 ] && return 0
[ -n "$HIGH_ROUTE_MARKS" ] || return 1
[ $(($1)) -gt 65535 ] && return 1
return $(($1 & 0xFF))
;;
[1-9]*)
[ $1 -lt 256 ] && return 0
[ -n "$HIGH_ROUTE_MARKS" ] || return 1
[ $1 -gt 65535 ] && return 1
return $(($1 & 0xFF))
;;
*)
return 2
;;
esac
}
verify_mark2 $1 || fatal_error "Invalid Mark or Mask value: $1"
}
2006-05-10 18:21:31 +02:00
#
# Detect a device's MTU
#
get_device_mtu() # $1 = device
{
local output=$(ip link ls dev $1 2> /dev/null)
if [ -n "$output" ]; then
echo $(find_mtu $output)
else
echo 1500
fi
}
2006-05-06 18:15:33 +02:00
#
# Arne Bernin's 'tc4shorewall'
#
setup_traffic_shaping()
{
2006-05-10 20:26:40 +02:00
local mtu r2q tc_all_devices device mark rate ceil prio options devfile=$(find_file tcdevices) classfile=$(find_file tcclasses) devnum=1 last_device=
2006-05-06 18:15:33 +02:00
r2q=10
rate_to_kbit() {
local rateunit rate
rate=$1
rateunit=$( echo $rate | sed -e 's/[0-9]*//')
rate=$( echo $rate | sed -e 's/[a-z]*//g')
case $rateunit in
kbit)
rate=$rate
;;
mbit)
rate=$(expr $rate \* 1024)
;;
mbps)
rate=$(expr $rate \* 8192)
;;
kbps)
rate=$(expr $rate \* 8)
;;
*)
rate=$(expr $rate / 128)
;;
esac
echo $rate
}
calculate_quantum() {
2006-05-10 18:21:31 +02:00
local rate=$(rate_to_kbit $1)
echo $(( $rate * ( 128 / $r2q ) ))
2006-05-06 18:15:33 +02:00
}
# get given outbandwidth for device
get_outband_for_dev() {
local device inband outband
while read device inband outband; do
expandv device inband outband
tcdev="$device $inband $outband"
if [ "$1" = "$device" ] ; then
echo $outband
return
fi
done < $TMP_DIR/tcdevices
}
check_tcclasses_options() {
while [ $# -gt 1 ]; do
shift
case $1 in
default|tcp-ack|tos-minimize-delay|tos-maximize-throughput|tos-maximize-reliability|tos-minimize-cost|tos-normal-service)
;;
tos=0x[0-9a-f][0-9a-f]|tos=0x[0-9a-f][0-9a-f]/0x[0-9a-f][0-9a-f])
;;
*)
echo $1
return 1
;;
esac
done
return 0
}
get_defmark_for_dev() {
local searchdev searchmark device ceil prio options
searchdev=$1
while read device mark rate ceil prio options; do
expandv device mark rate ceil prio options
options=$(separate_list $options | tr '[A-Z]' '[a-z]')
tcdev="$device $mark $rate $ceil $prio $options"
if [ "$searchdev" = "$device" ] ; then
list_search "default" $options && echo $mark &&return 0
fi
done < $TMP_DIR/tcclasses
return 1
}
check_defmark_for_dev() {
get_defmark_for_dev $1 >/dev/null
}
validate_tcdevices_file() {
progress_message2 "Validating $devfile..."
local device local device inband outband
while read device inband outband; do
expandv device inband outband
tcdev="$device $inband $outband"
check_defmark_for_dev $device || fatal_error "Option default is not defined for any class in tcclasses for interface $device"
case $interface in
*:*|+)
fatal_error "Invalid Interface Name: $interface"
;;
esac
list_search $device $devices && fatal_error "Interface $device is defined more than once in tcdevices"
tc_all_devices="$tc_all_devices $device"
done < $TMP_DIR/tcdevices
}
validate_tcclasses_file() {
progress_message2 "Validating $classfile..."
local classlist device mark rate ceil prio bandw wrongopt allopts opt
allopts=""
while read device mark rate ceil prio options; do
expandv device mark rate ceil prio options
tcdev="$device $mark $rate $ceil $prio $options"
ratew=$(get_outband_for_dev $device)
options=$(separate_list $options | tr '[A-Z]' '[a-z]')
for opt in $options; do
case $opt in
tos=0x??)
opt="$opt/0xff"
;;
esac
list_search "$device-$opt" $allopts && fatal_error "option $opt already defined in a chain for interface $device in tcclasses"
allopts="$allopts $device-$opt"
done
wrongopt=$(check_tcclasses_options $options) || fatal_error "unknown option $wrongopt for class iface $device mark $mark in tcclasses file"
if [ -z "$ratew" ] ; then
fatal_error "device $device seems not to be configured in tcdevices"
fi
list_search "$device-$mark" $classlist && fatal_error "Mark $mark for interface $device defined more than once in tcclasses"
#
# Convert HEX/OCTAL mark representation to decimal
#
mark=$(($mark))
verify_mark $mark
[ $mark -lt 256 ] || fatal_error "Invalid Mark Value"
classlist="$classlist $device-$mark"
done < $TMP_DIR/tcclasses
}
add_root_tc() {
2006-05-10 16:34:50 +02:00
local defmark dev
dev=$(chain_base $device)
2006-05-06 18:15:33 +02:00
defmark=$(get_defmark_for_dev $device)
2006-05-07 00:13:57 +02:00
if [ $COMMAND = compile ]; then
save_command qt tc qdisc del dev $device root
save_command qt tc qdisc del dev $device ingress
fi
2006-05-06 18:15:33 +02:00
run_tc qdisc add dev $device root handle $devnum: htb default 1$defmark
2006-05-10 18:21:31 +02:00
if [ $COMMAND = compile ]; then
2006-05-10 20:26:40 +02:00
save_command "${dev}_mtu=\$(get_device_mtu $device)"
run_tc "class add dev $device parent $devnum: classid $devnum:1 htb rate $outband mtu \$${dev}_mtu"
2006-05-10 18:21:31 +02:00
else
run_tc class add dev $device parent $devnum: classid $devnum:1 htb rate $outband mtu $(get_device_mtu $device)
fi
2006-05-06 18:15:33 +02:00
run_tc qdisc add dev $device handle ffff: ingress
run_tc filter add dev $device parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${inband} burst 10k drop flowid :1
2006-05-10 16:34:50 +02:00
eval ${dev}_devnum=$devnum
2006-05-10 17:20:36 +02:00
devnum=$(($devnum + 1))
2006-05-06 18:15:33 +02:00
}
add_tc_class() {
2006-05-10 18:21:31 +02:00
local full classid tospair tosmask quantum
2006-05-06 18:15:33 +02:00
full=$(get_outband_for_dev $device)
full=$(rate_to_kbit $full)
if [ -z "$prio" ] ; then
prio=1
fi
case $rate in
*full*)
rate=$(echo $rate | sed -e "s/full/$full/")
rate="$(($rate))kbit"
;;
esac
case $ceil in
*full*)
ceil=$(echo $ceil | sed -e "s/full/$full/")
ceil="$(($ceil))kbit"
;;
esac
2006-05-10 16:34:50 +02:00
dev=$(chain_base $device)
eval devnum=\$${dev}_devnum
2006-05-06 18:15:33 +02:00
#
# Convert HEX/OCTAL mark representation to decimal
#
mark=$(($mark))
classid=$devnum:1$mark
[ -n "$devnum" ] || fatal_error "Device $device not defined in $devfile"
2006-05-10 18:21:31 +02:00
quantum=$(calculate_quantum $rate)
if [ $COMMAND = compile ]; then
2006-05-10 20:26:40 +02:00
save_command "[ \$${dev}_mtu -gt $quantum ] && quantum=\$${dev}_mtu || quantum=$quantum"
run_tc "class add dev $device parent $devnum:1 classid $classid htb rate $rate ceil $ceil prio $prio mtu \$${dev}_mtu quantum \$quantum"
2006-05-10 18:21:31 +02:00
else
2006-05-10 20:26:40 +02:00
[ "$last_device" = $device ] || mtu=$(get_device_mtu $device)
2006-05-10 18:21:31 +02:00
[ $mtu -gt $quantum ] && quantum=$mtu
run_tc class add dev $device parent $devnum:1 classid $classid htb rate $rate ceil $ceil prio $prio mtu $mtu quantum $quantum
fi
2006-05-06 18:15:33 +02:00
run_tc qdisc add dev $device parent $classid handle 1$mark: sfq perturb 10
# add filters
if [ -n "$CLASSIFY_TARGET" ]; then
run_iptables -t mangle -A tcpost $(match_dest_dev $device) -m mark --mark $mark/0xFF -j CLASSIFY --set-class $classid
else
run_tc filter add dev $device protocol ip parent $devnum:0 prio 1 handle $mark fw classid $classid
fi
#options
list_search "tcp-ack" $options && run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid $classid
list_search "tos-minimize-delay" $options && options="$options tos=0x10/0x10"
list_search "tos-maximize-throughput" $options && options="$options tos=0x08/0x08"
list_search "tos-maximize-reliability" $options && options="$options tos=0x04/0x04"
list_search "tos-minimize-cost" $options && options="$options tos=0x02/0x02"
list_search "tos-normal-service" $options && options="$options tos=0x00/0x1e"
for tospair in $(list_walk "tos=" $options) ; do
case $tospair in
*/*)
tosmask=${tospair##*/}
;;
*)
tosmask=0xff
;;
esac
run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip tos ${tospair%%/*} $tosmask flowid $classid
done
}
strip_file tcdevices $devfile
strip_file tcclasses $classfile
validate_tcdevices_file
validate_tcclasses_file
if [ -s $TMP_DIR/tcdevices ]; then
2006-05-07 00:13:57 +02:00
[ $COMMAND = compile ] && save_progress_message "Setting up Traffic Control..."
2006-05-06 18:15:33 +02:00
progress_message2 "$DOING $devfile..."
2006-05-10 18:21:31 +02:00
while read device inband outband; do
expandv device inband outband
tcdev="$device $inband $outband"
2006-05-06 18:15:33 +02:00
add_root_tc
progress_message_and_save " TC Device $tcdev defined."
done < $TMP_DIR/tcdevices
fi
if [ -s $TMP_DIR/tcclasses ]; then
progress_message2 "$DOING $classfile..."
while read device mark rate ceil prio options; do
expandv device mark rate ceil prio options
tcdev="$device $mark $rate $ceil $prio $options"
options=$(separate_list $options | tr '[A-Z]' '[a-z]')
add_tc_class
progress_message_and_save " TC Class $tcdev defined."
2006-05-10 20:26:40 +02:00
last_device=$device
2006-05-06 18:15:33 +02:00
done < $TMP_DIR/tcclasses
fi
}
#
# Process a TC Rule - $MARKING_CHAIN is assumed to contain the name of the
# default marking chain
#
process_tc_rule()
{
local did_connmark=
chain=$MARKING_CHAIN target="MARK --set-mark" marktest=
verify_designator() {
[ "$chain" = tcout ] && \
fatal_error "Chain designator not allowed when source is \$FW; rule \"$rule\""
chain=$1
mark="${mark%:*}"
}
do_ipp2p()
{
[ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. Rule: \"$rule\""
[ "x$port" = "x-" ] && port="ipp2p"
case $proto in
*:*)
proto=${proto#*:}
;;
*)
proto=tcp
;;
esac
r="${r}-p $proto -m ipp2p --${port} "
}
verify_small_mark()
{
verify_mark $1
[ $(($1)) -lt 256 ] || fatal_error "Mark Value ($1) too larg, rule \"$rule\""
}
do_connmark()
{
target="CONNMARK --set-mark"
mark=$mark/0xff
did_connmark=Yes
}
validate_mark()
{
case $1 in
*/*)
verify_mark ${1%/*}
verify_mark ${1#*/}
;;
*)
verify_mark $1
;;
esac
}
add_a_tc_rule() {
r=
if [ "x$source" != "x-" ]; then
case $source in
$FW:*)
[ $chain = tcpost ] || chain=tcout
r="$(source_ip_range ${source#*:}) "
;;
*:*)
interface=${source%:*}
verify_interface $interface || fatal_error "Unknown interface $interface in rule \"$rule\""
r="$(match_source_dev $interface) $(source_ip_range ${source#*:}) "
;;
*.*.*|+*|!+*)
r="$(source_ip_range $source) "
;;
~*)
r="$(mac_match $source) "
;;
$FW)
[ $chain = tcpost ] || chain=tcout
;;
*)
verify_interface $source || fatal_error "Unknown interface $source in rule \"$rule\""
r="$(match_source_dev $source) "
;;
esac
fi
if [ "x${user:--}" != "x-" ]; then
[ "$chain" != tcout ] && \
fatal_error "Invalid use of a user/group: rule \"$rule\""
r="$r-m owner"
case "$user" in
*+*)
r="$r --cmd-owner ${user#*+} "
user=${user%+*}
;;
esac
case "$user" in
*:*)
temp="${user%:*}"
[ -n "$temp" ] && r="$r --uid-owner $temp "
temp="${user#*:}"
[ -n "$temp" ] && r="$r --gid-owner $temp "
;;
*)
[ -n "$user" ] && r="$r --uid-owner $user "
;;
esac
fi
[ -n "$marktest" ] && r="${r}-m ${marktest}--mark $testval "
if [ "x$dest" != "x-" ]; then
case $dest in
*:*)
[ "$chain" = tcpre ] && fatal_error "Destination interface is not allowed in the PREROUTING chain - rule \"$rule\""
interface=${dest%:*}
verify_interface $interface || fatal_error "Unknown interface $interface in rule \"$rule\""
r="$(match_dest_dev $interface) $(dest_ip_range ${dest#*:}) "
;;
*.*.*|+*|!+*)
r="${r}$(dest_ip_range $dest) "
;;
*)
[ "$chain" = tcpre ] && fatal_error "Destination interface is not allowed in the PREROUTING chain - rule \"$rule\""
verify_interface $dest || fatal_error "Unknown interface $dest in rule \"$rule\""
r="${r}$(match_dest_dev $dest) "
;;
esac
fi
if [ "x${length:=-}" != "x-" ]; then
[ -n "$LENGTH_MATCH" ] || fatal_error "Your kernel and/or iptables does not have length match support. Rule: \"$rule\""
r="${r}-m length --length ${length} "
fi
if [ "x${tos:=-}" != "x-" ]; then
r="${r}-m tos --tos ${tos} "
fi
multiport=
case $proto in
ipp2p|IPP2P|ipp2p:*|IPP2P:*)
do_ipp2p
;;
icmp|ICMP|1)
r="${r}-p icmp "
[ "x$port" = "x-" ] || r="${r}--icmp-type $port"
;;
*)
[ "x$proto" = "x-" ] && proto=all
[ "x$proto" = "x" ] && proto=all
[ "$proto" = "all" ] || r="${r}-p $proto "
[ "x$port" = "x-" ] || r="${r}--dport $port "
;;
esac
[ "x$sport" = "x-" ] || r="${r}--sport $sport "
if [ -n "${excludesources}${excludedests}" ]; then
build_exclusion_chain chain1 mangle "$excludesources" "$excludedests"
run_iptables2 -t mangle -A $chain $r -j $chain1
run_iptables -t mangle -A $chain1 -j $target $mark
else
run_iptables2 -t mangle -A $chain $r -j $target $mark
fi
}
if [ "$mark" != "${mark%:*}" ]; then
case "${mark#*:}" in
p|P)
verify_designator tcpre
;;
cp|CP)
verify_designator tcpre
do_connmark
;;
f|F)
verify_designator tcfor
;;
cf|CF)
verify_designator tcfor
do_connmark
;;
c|C)
mark=${mark%:*}
do_connmark
;;
*)
chain=tcpost
target="CLASSIFY --set-class"
;;
esac
fi
mask=0xffff
case $mark in
SAVE)
[ -n "$did_connmark" ] && fatal_error "SAVE not valid with :C[FP]"
target="CONNMARK --save-mark --mask 0xFF"
mark=
;;
SAVE/*)
[ -n "$did_connmark" ] && fatal_error "SAVE not valid with :C[FP]"
target="CONNMARK --save-mark --mask"
mark=${mark#*/}
verify_small_mark $mark
;;
RESTORE)
[ -n "$did_connmark" ] && fatal_error "RESTORE not valid with :C[FP]"
target="CONNMARK --restore-mark --mask 0xFF"
mark=
;;
RESTORE/*)
[ -n "$did_connmark" ] && fatal_error "RESTORE not valid with :C[FP]"
target="CONNMARK --restore-mark --mask"
mark=${mark#*/}
verify_small_mark $mark
;;
CONTINUE)
[ -n "$did_connmark" ] && fatal_error "CONTINUE not valid with :C[FP]"
target=RETURN
mark=
;;
*)
if [ "$chain" != tcpost ]; then
validate_mark $mark
if [ $((${mark%/*})) -gt 255 ]; then
case $chain in
tcpre|tcout)
target="MARK --or-mark"
;;
*)
fatal_error "Invalid mark value ($mark) in rule \"$rule\""
;;
esac
elif [ $((${mark%/*})) -ne 0 -a -n "$HIGH_ROUTE_MARKS" -a $chain = tcpre ]; then
fatal_error "Marks < 256 may not be set in the PREROUTING chain when HIGH_ROUTE_MARKS=Yes"
fi
fi
;;
esac
case $testval in
-)
;;
!*:C)
marktest="connmark ! "
testval=${testval%:*}
testval=${testval#!}
;;
*:C)
marktest="connmark "
testval=${testval%:*}
;;
!*)
marktest="mark ! "
testval=${testval#!}
;;
*)
[ -n "$testval" ] && marktest="mark "
;;
esac
if [ -n "$marktest" ] ; then
case $testval in
*/*)
verify_mark ${testval%/*}
verify_mark ${testval#*/}
;;
*)
verify_mark $testval
testval=$testval/$mask
;;
esac
fi
excludesources=
case ${sources:=-} in
*!*!*)
fatal_error "Invalid SOURCE in rule \"$rule\""
;;
!*)
if [ $(list_count $sourcess) -gt 1 ]; then
excludesources=${sources#!}
sources=-
fi
;;
*!*)
excludesources=${sources#*!}
sources=${sources%!*}
;;
esac
excludedests=
case ${dests:=-} in
*!*!*)
fatal_error "Invalid DEST in rule \"$rule\""
;;
!*)
if [ $(list_count $dests) -gt 1 ]; then
excludedests=${dests#*!}
dests=-
fi
;;
*!*)
excludedests=${dests#*!}
dests=${dests%!*}
;;
esac
for source in $(separate_list $sources); do
for dest in $(separate_list $dests); do
for port in $(separate_list ${ports:=-}); do
for sport in $(separate_list ${sports:=-}); do
add_a_tc_rule
done
done
done
done
progress_message " TC Rule \"$rule\" $DONE"
2006-05-07 00:13:57 +02:00
[ $COMMAND = compile ] && save_progress_message " TC Rule \"$rule\" Added"
2006-05-06 18:15:33 +02:00
}
2006-01-17 21:03:00 +01:00
delete_tc1()
{
clear_one_tc() {
tc qdisc del dev $1 root 2> /dev/null
tc qdisc del dev $1 ingress 2> /dev/null
}
run_user_exit tcclear
run_ip link list | \
while read inx interface details; do
case $inx in
[0-9]*)
clear_one_tc ${interface%:}
;;
*)
;;
esac
done
}
2005-09-09 00:30:32 +02:00
SHOREWALL_LIBRARY=Loaded