Experimental modularization of Shorewall functions

git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@4380 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
This commit is contained in:
teastep 2006-08-18 16:33:33 +00:00
parent 234aa082e5
commit bc3054b011
2 changed files with 53 additions and 1792 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1588,676 +1588,14 @@ get_device_mtu() # $1 = device
fi
}
#
# Arne Bernin's 'tc4shorewall'
#
setup_traffic_shaping()
{
local mtu r2q tc_all_devices device mark rate ceil prio options devfile=$(find_file tcdevices) classfile=$(find_file tcclasses) devnum=1 last_device=
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() {
local rate=$(rate_to_kbit $1)
echo $(( $rate * ( 128 / $r2q ) ))
}
# 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() {
local defmark dev indent
dev=$(chain_base $device)
if [ "$COMMAND" = compile ]; then
save_command "if qt ip link ls dev $device; then"
indent="$INDENT"
INDENT="$INDENT "
save_command ${dev}_exists=Yes
save_command qt tc qdisc del dev $device root
save_command qt tc qdisc del dev $device ingress
elif ! qt ip link ls dev $device; then
error_message "WARNING: Device $device not found -- traffic-shaping configuration skipped"
return 1
fi
defmark=$(get_defmark_for_dev $device)
run_tc qdisc add dev $device root handle $devnum: htb default 1$defmark
if [ "$COMMAND" = compile ]; then
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"
else
run_tc class add dev $device parent $devnum: classid $devnum:1 htb rate $outband mtu $(get_device_mtu $device)
fi
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
eval ${dev}_devnum=$devnum
devnum=$(($devnum + 1))
if [ "$COMMAND" = compile ]; then
save_progress_message_short " TC Device $tcdev defined."
INDENT="$indent"
save_command else
INDENT="$INDENT "
save_command error_message "\"WARNING: Device $device not found -- traffic-shaping configuration skipped\""
save_command "${dev}_exists="
INDENT="$indent"
save_command "fi"
save_command
fi
return 0
}
add_tc_class() {
local full classid tospair tosmask quantum indent
dev=$(chain_base $device)
if [ "$COMMAND" = compile ]; then
save_command "if [ -n \"\$${dev}_exists\" ] ; then"
indent="$INDENT"
INDENT="$INDENT "
else
qt ip link ls dev $device || return 1
fi
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
eval devnum=\$${dev}_devnum
#
# Convert HEX/OCTAL mark representation to decimal
#
mark=$(($mark))
classid=$devnum:1$mark
[ -n "$devnum" ] || fatal_error "Device $device not defined in $devfile"
quantum=$(calculate_quantum $rate)
if [ "$COMMAND" = compile ]; then
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"
else
[ "$last_device" = $device ] || mtu=$(get_device_mtu $device)
[ $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
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
if [ "$COMMAND" = compile ]; then
save_progress_message_short " TC Class $tcdev defined."
INDENT="$indent"
save_command fi
save_command
fi
return 0
}
strip_file tcdevices $devfile
strip_file tcclasses $classfile
validate_tcdevices_file
validate_tcclasses_file
if [ -s $TMP_DIR/tcdevices ]; then
[ $COMMAND = compile ] && save_progress_message "Setting up Traffic Control..."
progress_message2 "$DOING $devfile..."
while read device inband outband; do
expandv device inband outband
tcdev="$device $inband $outband"
add_root_tc && progress_message " 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 " TC Class $tcdev defined."
last_device=$device
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=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=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 $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
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"
[ $COMMAND = compile ] && save_progress_message " TC Rule \"$rule\" Added"
}
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
}
SHOREWALL_LIBRARY=Loaded
for lib in ${SHAREDIR}/lib.*; do
case $lib in
${SHAREDIR}/lib.\*)
;;
*)
. $clib
;;
esac
done