shorewall_code/Shorewall-common/lib.tcrules
2007-04-03 01:33:28 +00:00

492 lines
11 KiB
Bash

#!/bin/sh
#
# Shorewall 3.4 -- /usr/share/shorewall/lib.tcrules
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 1999,2000,2001,2002,2003,2004,2005,2006,2007 - Tom Eastep (teastep@shorewall.net)
#
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This library is loaded by /usr/share/shorewall/compiler when the tcrules file is
# non-empty. It is also loaded by the compiled firewall script under the same
# condition when the script is processing the 'refresh' command.
#
#
# Process a TC Rule - $MARKING_CHAIN is assumed to contain the name of the
# default marking chain
#
# The caller has established values for the following variables:
#
# mark - MARK column
# sources - SOURCE column
# dests - DEST column
# proto - PROTO column
# ports - PORT(S) column
# sports - CLIENT PORT(S) column
# user - USER column
# testval - TEST column
# length - LENGTH column
# tos - TOS column
#
process_tc_rule()
{
local did_connmark= multiport= classid=
chain=$MARKING_CHAIN target="MARK --set-mark" marktest=
handle_designator() {
chain=$1
mark="${mark%:*}"
}
do_ipp2p()
{
[ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. TC 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 large, 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:*)
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)
;;
*)
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
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}$multiport $port "
;;
esac
[ "x$sport" = "x-" ] || r="${r}--sport $sport "
if [ -n "${excludesources}${excludedests}" ]; then
[ $target = RETURN ] && \
fatal_error "Exclusion is currently not supported with CONTINUE"
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
}
#
# E x e c u t i o n B e g i n s H e r e
#
case $sources in
$FW|$FW:*)
chain=tcout
if [ "x$mark" != "x${mark%:*}" ]; then
case "${mark#*:}" in
t|T)
handle_designator tcpost
;;
ct|CT)
handle_designator tcpost
do_connmark
;;
c|C)
mark=${mark%:*}
do_connmark
;;
p|P|cp|CP|f|F|cf|CF)
fatal_error "Invalid chain designator for source \$FW; rule \"$rule\""
;;
*)
chain=tcpost
target="CLASSIFY --set-class"
classid=Yes
;;
esac
fi
;;
*)
if [ "x$mark" != "x${mark%:*}" ]; then
case "${mark#*:}" in
p|P)
handle_designator tcpre
;;
cp|CP)
handle_designator tcpre
do_connmark
;;
f|F)
handle_designator tcfor
;;
cf|CF)
handle_designator tcfor
do_connmark
;;
t|T)
handle_designator tcpost
;;
ct|CT)
handle_designator tcpost
do_connmark
;;
c|C)
mark=${mark%:*}
do_connmark
;;
*)
chain=tcpost
classid=Yes
target="CLASSIFY --set-class"
;;
esac
fi
;;
esac
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=
;;
\|*)
[ -n "$classid" ] && fatal_error "Invalid class ID: $mark"
[ -n "$did_connmark" ] && fatal_error "Logical OR not valid with :C[FP]"
target="MARK --or-mark"
mark=${mark#|}
validate_mark $mark
if [ $((${mark%/*})) -lt 256 -a $((${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
;;
\&*)
[ -n "$classid" ] && fatal_error "Invalid class ID: $mark"
[ -n "$did_connmark" ] && fatal_error "Logical AND not valid with :C[FP]"
target="MARK --and-mark"
mark=${mark#&}
validate_mark $mark
if [ $((${mark%/*})) -lt 256 -a $((${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
;;
*)
if [ -z "$classid" ]; 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 $sources) -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
#
# This isn't as fancy as the code in the rules file processor. It only uses multiport match
# if the extended variety is available and then only on the destination port(s).
#
if [ -n "$XMULTIPORT" ]; then
#
# Extended Multiport match can handle port ranges
#
multiport="-m multiport --dports"
for source in $(separate_list $sources); do
for dest in $(separate_list $dests); do
port=${ports:=-}
for sport in $(separate_list ${sports:=-}); do
add_a_tc_rule
done
done
done
else
multiport=--dport
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
fi
progress_message " TC Rule \"$rule\" $DONE"
save_progress_message_short " TC Rule \\\"$rule\\\" Added"
}
#
# Process the tcrules file
#
process_tc_rules()
{
cat >&3 << __EOF__
#
# Create Marking Rules from the tcrules file
#
setup_tc_rules()
{
__EOF__
INDENT=" "
while read mark sources dests proto ports sports user testval length tos; do
if [ "x$mark" = xCOMMENT ]; then
if [ -n "$COMMENTS" ]; then
comment=$(echo $sources $dests $proto $ports $sports $user $testval $length $tos)
save_command COMMENT=\"$comment\"
else
error_message "COMMENT ignored -- requires comment support in iptables/Netfilter"
fi
else
rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval $length $tos")
process_tc_rule
fi
done < $TMP_DIR/tcrules
INDENT=""
save_command "}"
save_command
}