#! /bin/sh # tcstart from tc4shorewall Version 0.5 # (c) 2005 Arne Bernin # published under GPL Version 2 if [ "$SHOREWALL_LIBRARY" != Loaded ]; then SHOREWALL_DIR=. SHARED_DIR=/usr/share/shorewall FUNCTIONS=$SHARED_DIR/functions . $FUNCTIONS ensure_and_save_command() { if ! eval $* ; then [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR exit 2 fi } run_and_save_command() { eval $*; } save_progress_message() { echo $@; } fatal_error() { echo " ERROR: $@" >&2 [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR exit 2 } TMP_DIR= TMP_DIR=$(mktempdir) fi setup_traffic_shaping() { local mtu r2q tc_all_devices device mark rate ceil prio options mtu=1500 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=$1 rate=$(rate_to_kbit $rate) rate=$(expr $rate \* 128 / $r2q ) if [ $rate -lt $mtu ] ; then echo $mtu else echo $rate fi } # 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) ;; *) 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() { echo "Validating tcdevices file..." 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() { echo "Validating tcclasses file..." 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 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" classlist="$classlist $device-$mark" done < $TMP_DIR/tcclasses } add_root_tc() { local defmark defmark=$(get_defmark_for_dev $device) run_and_save_command qt tc qdisc del dev $device root run_and_save_command qt tc qdisc del dev $device ingress ensure_and_save_command tc qdisc add dev $device root handle 1: htb default 1$defmark ensure_and_save_command tc class add dev $device parent 1: classid 1:1 htb rate $outband ensure_and_save_command tc qdisc add dev $device handle ffff: ingress ensure_and_save_command 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 } add_tc_class() { local full full=$(get_outband_for_dev $device) full=$(rate_to_kbit $full) #set -x 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 ensure_and_save_command tc class add dev $device parent 1:1 classid 1:1$mark htb rate $rate ceil $ceil prio $prio quantum $(calculate_quantum $rate) ensure_and_save_command tc qdisc add dev $device parent 1:1$mark handle 1$mark: sfq perturb 10 # add filters ensure_and_save_command tc filter add dev $device protocol ip parent 1:0 prio 1 handle $mark fw classid 1:1$mark # options list_search "tcp-ack" $options && ensure_and_save_command tc filter add dev $device parent 1: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 1:1$mark list_search "tos-minimize-delay" $options && ensure_and_save_command tc filter add dev $device parent 1:0 protocol ip prio 10 u32 match ip tos 0x10 0xff flowid 1:1$mark list_search "tos-minimize-cost" $options && ensure_and_save_command tc filter add dev $device parent 1:0 protocol ip prio 10 u32 match ip tos 0x02 0xff flowid 1:1$mark list_search "tos-maximize-troughput" $options && ensure_and_save_command tc filter add dev $device parent 1:0 protocol ip prio 10 u32 match ip tos 0x08 0xff flowid 1:1$mark list_search "tos-minimize-reliability" $options && ensure_and_save_command tc filter add dev $device parent 1:0 protocol ip prio 10 u32 match ip tos 0x04 0xff flowid 1:1$mark list_search "tos-normal-service" $options && ensure_and_save_command tc filter add dev $device parent 1:0 protocol ip prio 10 u32 match ip tos 0x00 0xff flowid 1:1$mark # tcp } strip_file tcdevices strip_file tcclasses validate_tcdevices_file validate_tcclasses_file if [ -s $TMP_DIR/tcdevices ]; then save_progress_message "Restoring Traffic Control..." echo "Processing tcdevices..." while read device inband outband defmark ackmark; do expandv device inband outband defmark ackmark tcdev="$device $inband $outband" add_root_tc progress_message " TC Device $tcdev Added." done < $TMP_DIR/tcdevices fi if [ -s $TMP_DIR/tcclasses ]; then echo "Processing tcclasses..." 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\" Added." done < $TMP_DIR/tcclasses fi } setup_traffic_shaping [ -n "$COMMAND" ] || rm -rf $TMP_DIR