#!/bin/bash # This is L7-Netfilter-example modifed for non-bridges # Somewhat simple script that controls bandwidth usage. # # This script assumes that the following are installed. # Userspace tools: iptables, ebtables, tc. # Kernel stuff: Netfilter with layer7 patch, QoS, Ethernet bridging tables # # It also assumes (naturally) that this computer is acting as a bridge, # although it should be very easy modify it to handle other situations. # # By Matthew Strait, 2003. May be distributed under the GPL version 2, # http://www.gnu.org/licenses/gpl.txt # tc needs to be told about the physical devices, even if you're a bridge physdevs="eth0 eth1" # syntax: " = , ". # Match types are "layer7" and "port". # "port" matches source or destination for tcp or udp. # "kbps" means "KBytes/second". This is tc's fault. actions=( "layer7 = http, 20kbps" "layer7 = ftp, 20kbps" "layer7 = aim, 20kbps" ) # Nothing below this line needs to be edited ######################################################################### # count commas... lastaction=`echo ${actions[*]} | tr \, '\n' | wc -l` # extra comma and array starts at zero... let lastaction-=2 stop-tc() { if ! [ $1 ]; then echo "specify a device!" exit 1 fi for dev in $@; do if tc qdisc del dev $dev root &> /dev/null; then echo "tc has now stopped for $dev" else echo "stopping tc for $dev failed (was probably already stopped)" fi done } # If these aren't loaded manually, shaping of the child connections will # not work. for m in ip_conntrack_ftp ip_conntrack_irc ip_conntrack_tftp ip_conntrack_amanda; do if ! lsmod | grep $m > /dev/null; then if ! modprobe $m; then echo failed to load module $m fi fi done # Flush the whole mangle table. iptables -t mangle -F if ! [ $? = "0" ]; then echo iptables failed at line $LINENO; exit 1; fi #ebtables -F #ebtables -t nat -F # stop traffic control completely. stop-tc $physdevs # set up basic traffic control magic for dev in $physdevs; do tc qdisc add dev $dev root handle 1: htb default 10 if ! [ $? = "0" ]; then echo tc failed at line $LINENO; exit 1; fi tc class add dev $dev parent 1: classid 1:1 htb rate 1mbit burst 15k if ! [ $? = "0" ]; then echo tc failed at line $LINENO; exit 1; fi tc class add dev $dev parent 1:1 classid 1:2 htb rate 1mbit burst 15k if ! [ $? = "0" ]; then echo tc failed at line $LINENO; exit 1; fi tc qdisc add dev $dev parent 1:2 handle 2: sfq perturb 10 if ! [ $? = "0" ]; then echo tc failed at line $LINENO; exit 1; fi done # the mark number and also the queue number. Must start at 3 so it doesn't # collide with ones we've already set up. n=3 # index into the speeds array index=0 for m in `seq 0 $lastaction`; do match=`echo ${actions[$m]} | cut -d\, -f1` speed=`echo ${actions[$m]} | cut -d\, -f2` type=`echo $match | cut -d\= -f1` arg=`echo $match | cut -d\= -f2` echo Packets matching \"$match\" \($type, $arg\) will be shaped to $speed. if [ $type = "layer7" ]; then iptables -t mangle -A POSTROUTING -m layer7 --l7proto $arg -j MARK --set-mark $n elif [ $type = "port" ]; then iptables -t mangle -A POSTROUTING --protocol tcp --source-port $arg -j MARK --set-mark $n iptables -t mangle -A POSTROUTING --protocol udp --source-port $arg -j MARK --set-mark $n iptables -t mangle -A POSTROUTING --protocol tcp --destination-port $arg -j MARK --set-mark $n iptables -t mangle -A POSTROUTING --protocol udp --destination-port $arg -j MARK --set-mark $n else echo "failed to parse \"$match\"" exit 1 fi if ! [ $? = "0" ]; then echo iptables failed at line $LINENO; exit 1; fi for dev in $physdevs; do # per class traffic control black magic tc class add dev $dev parent 1:1 classid 1:$n htb rate $speed burst 1k if ! [ $? = "0" ]; then echo tc failed at line $LINENO; exit 1; fi tc qdisc add dev $dev parent 1:$n handle $n: sfq perturb 10 if ! [ $? = "0" ]; then echo tc failed at line $LINENO; exit 1; fi tc filter add dev $dev protocol ip parent 1:0 prio 1 handle $n fw flowid 1:$n if ! [ $? = "0" ]; then echo tc failed at line $LINENO; exit 1; fi done let n++ let index++ done