#!/bin/bash # # Jim diGriz's QoS Scheduler (http://www.digriz.org.uk/jdg-qos-script/) # [remove 'junk-this' to mail me] # GPL V2 blar blar blar # Version: 20040823 (aka 23rd August 2004) # - more or less a complete rewrite and included use of WRR # # Dependencies, [...] marks optional extras # ----------------------------------------- # 1) HTB3 patch (if earlier than 2.4.18) and 'tc' patch # 2) RED (in kernel 2.4.x and 2.6.x) # 3) iptables (in kernel 2.4.x and 2.6.x) # 4) ESFQ patch (http://www.ssi.bg/~alex/esfq/index.html) and 'tc' patch # 5) WRR patch (http://wipl-wrr.sourceforge.net/wrr.html) and 'tc' patch # [ 6) IMQ patch (http://www.linuximq.net/) and 'tc' patch # IMQ is required if you are using NAT or QOS_GATEWAY_UPSTREAM] # [ 7) IPP2P patch (http://rnvs.informatik.uni-leipzig.de/ipp2p/index_en.html) # [ 8) CONNMARK patch (http://www.netfilter.org/) {if you need IPP2P} ] # # IPP2P is a nice feature that lets iptables identify P2P traffic. This really # is only useful to identify the headers and starts of data transfers which in # its-self is pretty useless, however if combined with CONNMARK you have a very # simple system which can mark all the P2P traffic as low priority; this # offloads all the difficult work onto the connecting tracking system. # debug on....comment out if you want BASH script debugging off set -x # external config file # to use this, point QOS_CONFIG to your required file and create a textfile # with all the tweakable variables below in. # QOS_CONFIG=/etc/jdg-qos-script/config SHAPER_OUT_RULES=/etc/jdg-qos-script/shaper-out SHAPER_IN_RULES=/etc/jdg-qos-script/shaper-in # local programs TC=/usr/local/sbin/tc IPTABLES=/usr/local/sbin/iptables IP=/sbin/ip MODPROBE=/sbin/modprobe # ballpark configuration details (all in kbit) DWIF=eth2 # LAN facing interface DWIFLIMIT=820 # Download Speed (512*0.8) UPIF=eth0 # WAN facing interface UPIFLIMIT=204 # Upload Speed (256*0.8) NAT=1 # if NATing then 1 else 0 QOS_GATEWAY_UPSTREAM=0 # 1 if upstream gw traffic LOCAL_TRAFFIC="10.128.0.0/16 213.162.126.176/29"# 'local' LAN address #ETHERNET_BRIDGE=0 # ethernet bridge setup REALTIME_BANDWIDTH=0 # value in kbit for guarentee FORCE_LOW_PRIORITY="6881 4662" # low priority traffic NETFILTER_P2P=1 # netfilter p2p matching support #BANDWIDTH_DIVIDE=1 # divide pipe exactly # -- start WRR bit -- WRR_CLIENTS=8 # maximum number of LAN clients WRR_WEIGHT1_MODE=3 WRR_WEIGHT1_INCR=0.000111111111 WRR_WEIGHT1_DECR=0.000000000636 WRR_WEIGHT1_MIN=0.2 WRR_WEIGHT1_MAX=1.0 WRR_WEIGHT1_VAL=1.0 WRR_WEIGHT2_MODE=0 WRR_WEIGHT2_INCR=0.0 WRR_WEIGHT2_DECR=0.0 WRR_WEIGHT2_MIN=0.01 WRR_WEIGHT2_MAX=1.0 WRR_WEIGHT2_VAL=0.1 # -- end WRR bit -- # do not change anything below here....I mean it.....I am talking to you buster! [ -e $QOS_CONFIG ] && . $QOS_CONFIG HTB_DEBUG=0000000000000000 # needs to be automagic from UPIF (ip addr show dev $UPIF) MTU=1478 REAL_UPIF=$UPIF REAL_DWIF=$DWIF if (( $NAT || $QOS_GATEWAY_UPSTREAM )) then UPIF=imq0 fi if (( $NAT )) then DWIF=imq1 fi if [ "$1" = "status" ] then $TC -s qdisc ls dev $DWIF $TC -s qdisc ls dev $UPIF $IPTABLES -t mangle -L SHAPER-OUT -v -n $IPTABLES -t mangle -L SHAPER-IN -v -n exit fi if [ "$1" = "pollbuckets" ] then watch -n1 "$TC -s qdisc show dev $DWIF; \ echo \"--------\"; $TC -s qdisc show dev $UPIF" exit fi if [ "$1" = "pollrules" ] then watch -n1 "$IPTABLES -t mangle -L SHAPER-OUT -v -n; \ echo \"--------\"; $IPTABLES -t mangle -L SHAPER-IN -v -n" exit fi if ( [ "$1" != "stop" ] && [ "$1" != "start" ] && [ "$1" != "restart" ] ) then exit fi $IPTABLES -t mangle -D PREROUTING -i $REAL_UPIF -j SHAPER-IN &> /dev/null $IPTABLES -t mangle -D POSTROUTING -o $REAL_UPIF -j SHAPER-OUT &> /dev/null $TC qdisc del dev $DWIF root &> /dev/null $TC qdisc del dev $UPIF root &> /dev/null if (( $NAT || $QOS_GATEWAY_UPSTREAM )) then if (( $NAT )) then $IP link set $DWIF down &> /dev/null $IPTABLES -t mangle -D PREROUTING -i $REAL_UPIF \ -j IMQ --todev 1 &> /dev/null & fi $IPTABLES -t mangle -D POSTROUTING -o $REAL_UPIF \ -j IMQ --todev 0 &> /dev/null & $IP link set $UPIF down &> /dev/null $MODPROBE -r imq &> /dev/null fi $IPTABLES -t mangle -F SHAPER-IN &> /dev/null $IPTABLES -t mangle -X SHAPER-IN &> /dev/null $IPTABLES -t mangle -F SHAPER-OUT &> /dev/null $IPTABLES -t mangle -X SHAPER-OUT &> /dev/null if ( [ "$1" = "stop" ] ) then exit fi ######## default SHAPER-IN and SHAPER-OUT rulesets ######## # `Advanced' users can tweak the iptables section at the end of the script. # I highly recommend you use the SHAPER_OUT_RULES and SHAPER_IN_RULES approach # where you create two external files that will override the defaults below. # # For upload you have ten `buckets' (numbered 20 to 29, 29 is low priority) # to pick from. You work out a chain of iptables rules to decide what type of # packet goes into which bucket. Treat the beginning of the chain to set a base # `MARK', and then later iptable rules adjust the packet so when you leave the # chain the packet has the desired MARKing (and so priority). For downloading, # you have only two buckets, number 20 where traffic cannot be dropped, such as # ICMP and UDP traffic (and also high priority); whilst number 21 is for bulk # traffic which can be dropped safely with knowledge that it is not going to # generate more traffic, such as TCP traffic. # # N.B. # shaping only works for OUTGOING packets simply so think which interface to use # when you apply shaping to incoming packets you really are saying which ones # are safe to drop (without generating more traffic, FTP data) and which are # unsafe (UDP as it will not slow down the data transfer and has already # consumed your bandwidth and TCP ACK's are examples). # # Useful Info # IPTables TOS Fields: # Minimize-Delay 16 (0x10) # Maximize-Throughput 8 (0x08) # Maximize-Reliability 4 (0x04) # Minimize-Cost 2 (0x02), # Normal-Service 0 (0x00). function shaper-out () { ########## upload (20->29) # REALTIME bandwidth handling, example is everything from 10.0.0.2 # $IPTABLES -t mangle -I SHAPER-OUT -s 10.0.0.2 -j MARK --set-mark 30 # $IPTABLES -t mangle -I SHAPER-OUT -s 10.0.0.2 -j RETURN $IPTABLES -t mangle -A SHAPER-OUT -p icmp \ -j MARK --set-mark 20 # icmp (ping) $IPTABLES -t mangle -A SHAPER-OUT -p tcp \ -m tcp --tcp-flags ! SYN,RST,ACK ACK \ -j MARK --set-mark 21 # 'normal' tcp control flags $IPTABLES -t mangle -A SHAPER-OUT -p tcp \ -m tcp --tcp-flags SYN,RST,ACK ACK \ -m length --length :128 -m tos --tos ! Normal-Service \ -j MARK --set-mark 21 # regular ACK $IPTABLES -t mangle -A SHAPER-OUT -p tcp \ -m tcp --tcp-flags SYN,RST,ACK ACK \ -m length --length 128: -j MARK --set-mark 29 # P2P traffic ACK $IPTABLES -t mangle -A SHAPER-OUT -p udp \ -j MARK --set-mark 24 # udp traffic $IPTABLES -t mangle -A SHAPER-OUT -p tcp -m tos --tos Minimize-Delay \ -m mark --mark 0 -j MARK --set-mark 23 # low latency traffic $IPTABLES -t mangle -A SHAPER-OUT -p tcp --sport ssh:telnet \ -j MARK --set-mark 22 # ssh/telnet $IPTABLES -t mangle -A SHAPER-OUT -p tcp --dport ssh:telnet \ -j MARK --set-mark 22 # ssh/telnet $IPTABLES -t mangle -A SHAPER-OUT -p tcp --sport http \ -j MARK --set-mark 26 # web traffic $IPTABLES -t mangle -A SHAPER-OUT -p tcp --dport http \ -j MARK --set-mark 26 # web traffic $IPTABLES -t mangle -A SHAPER-OUT -p tcp --sport smtp \ -j MARK --set-mark 27 # smtp traffic $IPTABLES -t mangle -A SHAPER-OUT -p tcp --dport smtp \ -j MARK --set-mark 27 # smtp traffic $IPTABLES -t mangle -A SHAPER-OUT -p tcp \ -m tos --tos Maximize-Throughput \ -j MARK --set-mark 28 # get bulk traffic $IPTABLES -t mangle -A SHAPER-OUT -p tcp -m tos --tos Minimize-Cost \ -j MARK --set-mark 28 # get bulk traffic for PORT in $FORCE_LOW_PRIORITY; # force into lowest priority bucket do $IPTABLES -t mangle -A SHAPER-OUT -p tcp --sport $PORT \ -j MARK --set-mark 29 $IPTABLES -t mangle -A SHAPER-OUT -p tcp --dport $PORT \ -j MARK --set-mark 29 done if (( $NETFILTER_P2P )) then $IPTABLES -t mangle -A SHAPER-OUT -p tcp -m connmark --mark 29 \ -j CONNMARK --restore-mark $IPTABLES -t mangle -A SHAPER-OUT -p tcp -m ipp2p --ipp2p \ -j CONNMARK --set-mark 29 fi # SSH sets TOS field to 0x0a (Max-Throughput and Min-Cost) which # iptables cannot pick up on. Search for port 22 and ! Minimize-Delay. $IPTABLES -t mangle -A SHAPER-OUT -p tcp -m tos --tos ! Minimize-Delay \ --sport ssh -j MARK --set-mark 28 $IPTABLES -t mangle -A SHAPER-OUT -p tcp -m tos --tos ! Minimize-Delay \ --dport ssh -j MARK --set-mark 28 $IPTABLES -t mangle -A SHAPER-OUT -m mark --mark 0 \ -p tcp --sport :1024 \ -j MARK --set-mark 25 # default for privilaged services $IPTABLES -t mangle -A SHAPER-OUT -m mark --mark 0 \ -p tcp --dport :1024 \ -j MARK --set-mark 25 # default for privilaged services $IPTABLES -t mangle -A SHAPER-OUT -m mark --mark 0 \ -j MARK --set-mark 27 # default for everything un-marked } function shaper-in () { ########## download (20->21) # REALTIME bandwidth handling, example is everything from 10.0.0.2 # $IPTABLES -t mangle -I SHAPER-IN -d 10.0.0.2 -j MARK --set-mark 30 # $IPTABLES -t mangle -I SHAPER-IN -d 10.0.0.2 -j RETURN $IPTABLES -t mangle -A SHAPER-IN -p udp \ -j MARK --set-mark 20 # non-tcp traffic $IPTABLES -t mangle -A SHAPER-IN -p icmp \ -j MARK --set-mark 20 # non-tcp traffic $IPTABLES -t mangle -A SHAPER-IN -p tcp -m tcp \ --tcp-flags ! SYN,RST,ACK ACK \ -j MARK --set-mark 20 # 'normal' tcp control flags $IPTABLES -t mangle -A SHAPER-IN -p tcp -m tcp \ --tcp-flags SYN,RST,ACK ACK \ -m length --length :128 -m tos --tos ! Normal-Service \ -j MARK --set-mark 20 # regular ACK $IPTABLES -t mangle -A SHAPER-IN -p tcp -m tos --tos Minimize-Delay \ -m mark --mark 0 -j MARK --set-mark 20 # low latency traffic $IPTABLES -t mangle -A SHAPER-IN -p tcp --sport ssh:telnet \ -j MARK --set-mark 20 # ssh/telnet $IPTABLES -t mangle -A SHAPER-IN -p tcp --dport ssh:telnet \ -j MARK --set-mark 20 # ssh/telnet # SSH sets TOS field to 0x0a (Max-Throughput and Min-Cost) which # iptables cannot pick up on. Search for port 22 and ! Minimize-Delay. $IPTABLES -t mangle -A SHAPER-IN -p tcp -m tos --tos ! Minimize-Delay \ --sport ssh -j MARK --set-mark 21 $IPTABLES -t mangle -A SHAPER-IN -p tcp -m tos --tos ! Minimize-Delay \ --dport ssh -j MARK --set-mark 21 for PORT in $FORCE_LOW_PRIORITY; # force into lowest priority bucket do $IPTABLES -t mangle -A SHAPER-IN -p tcp --sport $PORT \ -j MARK --set-mark 21 $IPTABLES -t mangle -A SHAPER-IN -p tcp --dport $PORT \ -j MARK --set-mark 21 done if (( $NETFILTER_P2P )) then $IPTABLES -t mangle -A SHAPER-IN -p tcp -m connmark --mark 21 \ -j CONNMARK --restore-mark $IPTABLES -t mangle -A SHAPER-IN -p tcp -m ipp2p --ipp2p \ -j CONNMARK --set-mark 21 fi $IPTABLES -t mangle -A SHAPER-IN -m mark --mark 0 \ -j MARK --set-mark 21 # default for the rest } ############# configuring QoS system ###################### $IPTABLES -t mangle -N SHAPER-OUT $IPTABLES -t mangle -N SHAPER-IN if (( $NAT || $QOS_GATEWAY_UPSTREAM )) then if (( $NAT )) then $MODPROBE imq numdevs=2 else $MODPROBE imq numdevs=1 # as $QOS_GATEWAY_UPSTREAM=1 fi fi ###### uplink # install root HTB, point default traffic to 1:27: $TC qdisc add dev $UPIF root handle 1: htb default 1 debug $HTB_DEBUG $TC class add dev $UPIF parent 1: classid 1:2 htb \ rate $[$UPIFLIMIT-$REALTIME_BANDWIDTH]kbit ceil ${UPIFLIMIT}kbit \ quantum $MTU if (( $REALTIME_BANDWIDTH )) then $TC class add dev $UPIF parent 1: classid 1:3 htb \ rate ${REALTIME_BANDWIDTH}kbit prio 0 $TC qdisc add dev $UPIF parent 1:3 handle 30: esfq perturb 10 hash src $TC filter add dev $UPIF parent 1: prio 0 \ protocol ip handle 30 fw flowid 1:3 fi # shape everything at $UPIFLIMIT speed - this prevents huge queues in your # DSL modem which destroy latency: for LOOP in `seq 0 9` do # high prio class 1:20 to low prio class 1:29: $TC class add dev $UPIF parent 1:2 classid 1:$[$LOOP+20] htb \ rate $[(5-($LOOP/2))*$UPIFLIMIT/10]kbit \ ceil ${UPIFLIMIT}kbit burst 6k prio $LOOP # all get Enchanced Stochastic Fairness (depending on src ip): $TC qdisc add dev $UPIF parent 1:$[$LOOP+20] handle $[$LOOP+20]: esfq \ perturb 10 hash src # setup $IPTABLES hooks $TC filter add dev $UPIF parent 1: prio $LOOP protocol ip \ handle $[$LOOP+20] fw flowid 1:$[$LOOP+20] done ########## downlink ############# # slow downloads down to somewhat less than the real speed to prevent # queuing at our ISP. Tune to see how high you can set it. # ISPs tend to have *huge* queues to make sure big downloads are fast # $TC qdisc add dev $DWIF root handle 1: htb default 1 debug $HTB_DEBUG $TC class add dev $DWIF parent 1: classid 1:2 htb \ rate $[$DWIFLIMIT-$REALTIME_BANDWIDTH]kbit ceil ${DWIFLIMIT}kbit if (( $REALTIME_BANDWIDTH )) then $TC class add dev $DWIF parent 1: classid 1:3 htb \ rate ${REALTIME_BANDWIDTH}kbit prio 0 $TC qdisc add dev $DWIF parent 1:3 handle 30: esfq perturb 10 hash dst $TC filter add dev $DWIF parent 1: prio 0 protocol ip handle 30 \ fw flowid 1:3 fi $TC class add dev $DWIF parent 1:2 classid 1:20 htb rate $[$DWIFLIMIT/2]kbit \ ceil ${DWIFLIMIT}kbit burst 6k prio 0 $TC class add dev $DWIF parent 1:2 classid 1:21 htb rate $[$DWIFLIMIT/2]kbit \ ceil ${DWIFLIMIT}kbit burst 6k prio 1 $TC qdisc add dev $DWIF parent 1:20 handle 20: esfq perturb 10 hash dst # create WRR class $TC qdisc add dev $DWIF parent 1:21 handle 1000: \ wrr dest ip $WRR_CLIENTS 0 $TC qdisc change handle 1000: dev $DWIF wrr qdisc \ wmode1=$WRR_WEIGHT1_MODE wmode2=$WRR_WEIGHT2_MODE # Attach RED queues to WRR RED_MAX=$[(($DWIFLIMIT-$REALTIME_BANDWIDTH)*1000/8)/2] RED_MIN=$[$RED_MAX/3] RED_LIMIT=$[$RED_MAX*8] RED_AVPKT=1000 for LOOP in `seq 1 $WRR_CLIENTS` do LOOP_HEX=$(printf %X $LOOP) $TC qdisc add dev $DWIF parent 1000:$LOOP_HEX handle $[1000+$LOOP]: red \ bandwidth $[$DWIFLIMIT-$REALTIME_BANDWIDTH] probability 0.02 \ limit ${RED_LIMIT} min ${RED_MIN} max ${RED_MAX} avpkt ${RED_AVPKT} \ burst $[((2*$RED_MIN)+($RED_MAX))/(3*$RED_AVPKT)+1] ecn $TC class change classid 1000:$LOOP_HEX dev $DWIF \ wrr min1=$WRR_WEIGHT1_MIN max1=$WRR_WEIGHT1_MAX \ decr1=$WRR_WEIGHT1_DECR incr1=$WRR_WEIGHT1_INCR \ weight1=$WRR_WEIGHT1_VAL \ min2=$WRR_WEIGHT2_MIN max2=$WRR_WEIGHT2_MAX \ decr2=$WRR_WEIGHT2_DECR incr2=$WRR_WEIGHT2_INCR \ weight2=$WRR_WEIGHT2_VAL done $TC filter add dev $DWIF parent 1: prio 0 protocol ip handle 20 fw flowid 1:20 $TC filter add dev $DWIF parent 1: prio 1 protocol ip handle 21 fw flowid 1:21 ################## finish QoS configuration ######################### ################# begin packet classification ####################### for SUBNET in $LOCAL_TRAFFIC; # local traffic bucket do $IPTABLES -t mangle -A SHAPER-OUT -d $SUBNET -j RETURN $IPTABLES -t mangle -A SHAPER-IN -s $SUBNET -j RETURN done [ -e $SHAPER_OUT_RULES ] && . $SHAPER_OUT_RULES || shaper-out [ -e $SHAPER_IN_RULES ] && . $SHAPER_IN_RULES || shaper-in if (( $NAT || $QOS_GATEWAY_UPSTREAM )) then if (( $NAT )) then $IPTABLES -t mangle -I PREROUTING -i $REAL_UPIF \ -j IMQ --todev 1 $IP link set $DWIF up fi $IPTABLES -t mangle -I POSTROUTING -o $REAL_UPIF -j IMQ --todev 0 $IP link set $UPIF up fi $IPTABLES -t mangle -I POSTROUTING -o $REAL_UPIF -j SHAPER-OUT $IPTABLES -t mangle -I PREROUTING -i $REAL_UPIF -j SHAPER-IN # UP as PREROUTING ################### end packet classification #######################