forked from extern/shorewall_code
46364902e3
git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@5817 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
355 lines
10 KiB
Bash
355 lines
10 KiB
Bash
#!/bin/sh
|
|
#
|
|
# Shorewall 3.4 -- /usr/share/shorewall/lib.tc
|
|
#
|
|
# 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)
|
|
#
|
|
# 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
|
|
#
|
|
# This library is loaded by /usr/share/shorewall/compiler when TC_ENABLED=Internal
|
|
# and the tcdevices and/or the tcclasses file is non-empty. It is also loaded under
|
|
# the same circumstances by the compiled firewall script when processing the
|
|
# 'refresh' command.
|
|
#
|
|
|
|
#
|
|
# 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 indent= prefix=1
|
|
|
|
rate_to_kbit() {
|
|
local rateunit rate
|
|
rate=$1
|
|
rateunit=$( echo $rate | sed -e 's/[0-9]*//')
|
|
rate=$( echo $rate | sed -e 's/[a-z]*//gi')
|
|
|
|
case $rateunit in
|
|
kbit|Kbit)
|
|
rate=$rate
|
|
;;
|
|
mbit|Mbit)
|
|
rate=$(expr $rate \* 1024)
|
|
;;
|
|
mbps|Mbps)
|
|
rate=$(expr $rate \* 8192)
|
|
;;
|
|
kbps|Kbps)
|
|
rate=$(expr $rate \* 8)
|
|
;;
|
|
*)
|
|
[ -n "$rateunit" ] && fatal_error "Invalid Rate ($1)"
|
|
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
|
|
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
|
|
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
|
|
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"
|
|
inband=$(rate_to_kbit $inband)
|
|
outband=$(rate_to_kbit $outband)
|
|
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
|
|
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
|
|
|
|
dev=$(chain_base $device)
|
|
|
|
save_command "if interface_is_usable $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
|
|
|
|
defmark=$(get_defmark_for_dev $device)
|
|
|
|
run_tc qdisc add dev $device root handle $devnum: htb default ${prefix}${defmark}
|
|
|
|
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"
|
|
|
|
if [ $(rate_to_kbit ${inband}) -gt 0 ]; then
|
|
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
|
|
fi
|
|
|
|
eval ${dev}_devnum=$devnum
|
|
devnum=$(($devnum + 1))
|
|
|
|
save_progress_message_short " TC Device $tcdev defined."
|
|
INDENT="$indent"
|
|
save_command else
|
|
INDENT="$INDENT "
|
|
save_command error_message "\"WARNING: Device $device not up and configured -- traffic-shaping configuration skipped\""
|
|
save_command "${dev}_exists="
|
|
INDENT="$indent"
|
|
save_command "fi"
|
|
save_command
|
|
|
|
return 0
|
|
}
|
|
|
|
add_tc_class() {
|
|
local full classid tospair tosmask quantum
|
|
|
|
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:${prefix}${mark}
|
|
|
|
[ -n "$devnum" ] || fatal_error "Device $device not defined in $devfile"
|
|
|
|
quantum=$(calculate_quantum $rate)
|
|
|
|
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"
|
|
|
|
run_tc qdisc add dev $device parent $classid handle ${prefix}${mark}: sfq perturb 10
|
|
#
|
|
# add filters
|
|
#
|
|
if [ -n "$CLASSIFY_TARGET" ] && known_interface $device; then
|
|
run_iptables -t mangle -A tcpost -o $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
|
|
|
|
save_progress_message_short " TC Class $tcdev defined."
|
|
|
|
return 0
|
|
}
|
|
|
|
finish_device() {
|
|
INDENT="$indent"
|
|
save_command fi
|
|
save_command
|
|
}
|
|
|
|
validate_tcdevices_file
|
|
validate_tcclasses_file
|
|
|
|
cat >&3 << __EOF__
|
|
|
|
#
|
|
# Set up Traffic Shaping
|
|
#
|
|
setup_traffic_shaping()
|
|
{
|
|
__EOF__
|
|
|
|
INDENT=" "
|
|
|
|
if [ -s $TMP_DIR/tcdevices ]; then
|
|
[ $(list_count1 $all_tc_devices) -gt 10 ] && prefix=10
|
|
|
|
save_progress_message "Setting up Traffic Control..."
|
|
progress_message2 "$DOING $devfile..."
|
|
|
|
while read device inband outband; do
|
|
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..."
|
|
|
|
last_device=
|
|
|
|
while read device mark rate ceil prio options; do
|
|
tcdev="$device $mark $rate $ceil $prio $options"
|
|
options=$(separate_list $options | tr '[A-Z]' '[a-z]')
|
|
|
|
dev=$(chain_base $device)
|
|
|
|
if [ "$device" != "$last_device" ]; then
|
|
|
|
[ -n "$last_device" ] && finish_device
|
|
|
|
save_command "if [ -n \"\$${dev}_exists\" ] ; then"
|
|
indent="$INDENT"
|
|
INDENT="$INDENT "
|
|
last_device=$device
|
|
else
|
|
save_command
|
|
fi
|
|
|
|
add_tc_class && progress_message " TC Class $tcdev defined."
|
|
done < $TMP_DIR/tcclasses
|
|
|
|
[ -n "$last_device" ] && finish_device
|
|
|
|
fi
|
|
|
|
INDENT=
|
|
|
|
save_command "}"
|
|
save_command
|
|
}
|