#! /bin/bash # # htb-script; start and stop traffic shaping with HTB # # chkconfig: - 45 55 # description: this assumes server and router functionality on the box # on which the script runs. It sets up traffic control to # and from the Internet and internal network without # limiting traffic to/from the server itself. # # # -- this script is not as well-conceived and researched as the # wondershaper: http://lartc.org/wondershaper/ # If you have a small LAN of users behind a "broadband" link (ADSL # or cable) and are tired of sacrificing interactivity to your # large downloads, go fetch the wondershaper. # # -- Here is an English summary of the script. # # Remember: we can only shape what we transmit. # # Definitions: # # - $REALDOWN and $REALUP represent the Internet bandwidth # available to the router; in good QoS fashion, this should # be capped so that this host is the bottleneck. # - $SERVERS are space-separated CIDR addresses; IPs from which # never to limit the download rate; intended to be locally # hosted IP addresses on the server # - $USERS are space-separated CIDR addresses; internal networks # - $USERDOWN and $USERUP represent the aggregate capped bandwidth # allowed to the $USERS # - $EXCEPTIONS are space-separated IPs (or CIDR networks) # which have access to the $REALUP and $REALDOWN bandwidth. # # Goals (in order of importance): # # - never limit from $SERVERS to $USERS # - limit $USERS to max Internet upload of $USERUP on $INET # - limit $USERS to max Internet download of $USERDOWN on $LAN # - limit $EXCEPTIONS to max Internet upload of $REALUP on $INET # - limit $EXCEPTIONS to max Internet upload of $REALDOWN on $LAN # # Assumptions: # # - HTB 3 capable kernel (2.4.20 works) # - tc with HTB support; see http://luxik.cdi.cz/~devik/qos/htb/ # - iptables; not ipchains; maybe a future option? # - mangle table PREROUTING and POSTROUTING can be flushed # and used with impunity # # Notes: # # - outbound traffic uses fwmark to differentiate traffic classes # and apply traffic control after packets have been SNATted or # MASQUERADEd # - inbound traffic knows the destination IP, so uses the u32 # classifier instead of the fw classifier (because I couldn't # get it to work!!) # - using IMQ and/or ingress policers would probably be better than # the crude hackery employed in this script. All we can do once # we have the packets is delay them from reaching the client, so # it's a good thing that HTB is not a work-conserving scheduler! # Use this as a last resort. # # -- written initially, 2003-03-02; -MAB # # -- commented heavily, 2003-03-03; -MAB # # -- if you need to modify PATH so that tc with HTB support can be # found in the path, do it here: # # PATH=/usr/local/bin:$PATH # # -- Why not? # tc=$( which tc ) iptables=$( which iptables ) # -- or you could always hardcode your tc here: # # tc=/path/to/your/tc-htb # -- INTERFACES ON THE SERVER # # INET = Internet-connected interface on your server # LAN = internal interface/LAN connected interface # # INET=wan0 # -- Oooh. Sangoma. Yummy. # INET=ppp0 # -- cheap ADSL; probably rp-pppoe INET=eth1 # -- pretty common outside interface name # -- LAN is the interface on which your internal users are reachable # LAN=eth0 # -- real bandwidth on Internet INTERFACE = INET # This could also be the max speed you want the INET interface # to download/upload # # REALDOWN=1536kbit # -- T1 speed; some xDSL lines # REALDOWN=1mbit # -- many cable lines; xDSL # REALDOWN=768kbit # -- xDSL and cable # REALDOWN=384kbit # -- xDSL # REALDOWN=256kbit # -- xDSL # REALDOWN=128kbit # -- Alright! Who's still using ISDN? :) # REALDOWN=600kbit # REALUP=384kbit # -- for an asynchronous line, specify # REALUP=256kbit # your upload speed separately # REALUP=128kbit # from the download speed # REALUP=128kbit # from the download speed REALUP=$REALDOWN # -- synchronous connections, ISDN, T1, SDSL # -- LAN real speed # # LANSPEED=10mbit # -- 10baseT; Yeah. Right. You're reading THIS! LANSPEED=100mbit # -- 100BaseT; Good bet. # -- make a space separated list of the IPs you never want to # limit. If you have a DMZ just outside this routing device, # be sure to add that network. Also add the IPs bound to $LAN # if you are using any services on this box. # # SERVERS="192.168.20.1 192.168.14.2 200.220.113.0/24" # SERVERS="" # -- LAN user definetions: Max download/upload speed # # USERS = Subnet where all lan users are # USERDOWN = Max Download speed for users # USERUP = Max Upload speed for users # USERMARK = arbitrary fwmark applied to all user traffic # # USERS="192.168.20.0/24 192.168.0.0/24" # USERS="" # -- Do not set $USERDOWN and $USERUP below $REALUP and $REALDOWN. # Things could get very strange. # USERDOWN=100kbit USERUP=100kbit USERMARK=5 # -- Exceptions to the $USERS; these IPs will have access # to $REALUP and $REALDOWN # # EXCEPTIONS = space separated list of IPs to exclude from # $USERUP and $USERDOWN bandwidth caps # EXCEPTMARK = arbitrary fwmark applied to all packets from # $EXCEPTIONS must be different from $USERMARK # # EXCEPTIONS="192.168.20.10 192.168.20.9" # EXCEPTIONS="" EXCEPTMARK=6 # - - - - - - - - - - - - removeqdisc () { # - - - - - - - - - - - - # # -- clean out the qdiscs, entirely # $tc qdisc del dev $1 root > /dev/null 2>&1 $tc qdisc del dev $1 ingress > /dev/null 2>&1 } # - - - - - - - - - - - - outboundshaping () { # - - - - - - - - - - - - # DEV=$1 # -- set up the HTB and make 10 the default class; fast class! # $tc qdisc add dev $DEV root handle 1: htb default 10 # -- top level class; all are children of this class # $tc class add dev $DEV parent 1: classid 1:1 htb rate $REALUP # -- put an SFQ in the leaf class for the server so that no single # conversation is allowed to dominate give this class priority # $tc class add dev $DEV parent 1:1 classid 1:10 \ htb rate $REALUP burst 20k $tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10 # -- also put the exceptions in this class; users like MIGUEL and BOSS # $tc filter add dev $DEV parent 1:0 protocol ip handle $EXCEPTMARK \ fw classid 1:10 # -- Oh yes, well....we do actually have users, so make a class for # them; give them an SFQ, so that no conversation dominates. # $tc class add dev $DEV parent 1:1 classid 1:7 \ htb rate $USERUP ceil $USERUP $tc qdisc add dev $DEV parent 1:7 handle 70: sfq perturb 10 $tc filter add dev $DEV parent 1:0 protocol ip handle $USERMARK \ fw classid 1:7 } # - - - - - - - - - - - - inboundshaping () { # - - - - - - - - - - - - # DEV=$1 # -- set up the HTB and make 10 the default class to limit downstream # bandwidth # $tc qdisc add dev $DEV root handle 1: htb default 10 # -- top level class; all are children of this class # $tc class add dev $DEV parent 1: classid 1:1 htb rate $LANSPEED # -- default class for all downloads; with an SFQ inside so # nobody can dominate # $tc class add dev $DEV parent 1:1 classid 1:10 \ htb rate $USERDOWN ceil $USERDOWN $tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10 for USERNET in $USERS ; do $tc filter add dev $DEV parent 1:0 protocol ip \ u32 match ip dst $USERNET flowid 1:10 done # -- put an SFQ in the leaf class for the real download speed # conversation is allowed to dominate give this class priority # $tc class add dev $DEV parent 1:1 classid 1:7 \ htb rate $REALDOWN ceil $REALDOWN burst 20k $tc qdisc add dev $DEV parent 1:7 handle 7: sfq perturb 10 # -- also put the exceptions in this class; users like MIGUEL and BOSS # so they get the full download speed of the link; "prio 8" is important # this selects the priority of the filter with respect to other filters # we want this to be a higher priority than the $USERS, but a lower # priority than $SERVERS traffic # for smartuser in $EXCEPTIONS; do $tc filter add dev $DEV parent 1:0 protocol ip \ u32 match ip dst $smartuser/32 flowid 1:7 done # -- we MUST make sure that packets to/from the server do not get # captured in the default class! $tc class add dev $DEV parent 1:1 classid 1:8 \ htb rate $LANSPEED burst 50k $tc qdisc add dev $DEV parent 1:8 handle 8: sfq perturb 10 for SERVERNET in $SERVERS ; do for USERNET in $USERS ; do $tc filter add dev $DEV parent 1:0 protocol ip \ u32 match ip src $SERVERNET \ match ip dst $USERNET flowid 1:8 done done } # - - - - - - - - - - - - marking () { # - - - - - - - - - - - - # # -- we're, rather rudely, going to empty the mangling tables # $iptables -t mangle -F PREROUTING $iptables -t mangle -F POSTROUTING # -- add some rules so that our "smartusers" get more bandwidth # for smartuser in $EXCEPTIONS ; do $iptables -t mangle -I PREROUTING -i $LAN -s $smartuser \ -j MARK --set-mark $EXCEPTMARK $iptables -t mangle -I POSTROUTING -o $LAN -d $smartuser \ -j MARK --set-mark $EXCEPTMARK done # -- here we set up the marking; ALL internal IPs go in one group # for USERNET in $USERS ; do $iptables -t mangle -I PREROUTING -i $LAN -s $USERNET \ -j MARK --set-mark $USERMARK $iptables -t mangle -I POSTROUTING -o $LAN -d $USERNET \ -j MARK --set-mark $USERMARK done } # - - - - - - - - - - - - htbstatus () { # - - - - - - - - - - - - # output=$1 test "$output" != "class" && echo " -- UPLOAD queuing disciplines --" test "$output" != "class" && $tc -s qdisc show dev $INET test "$output" != "qdisc" && echo ; echo " -- UPLOAD classes --" test "$output" != "qdisc" && $tc -s class show dev $INET test "$output" != "class" && echo " -- DOWNLOAD queuing disciplines --" test "$output" != "class" && $tc -s qdisc show dev $LAN test "$output" != "qdisc" && echo ; echo " -- DOWNLOAD classes --" test "$output" != "qdisc" && $tc -s class show dev $LAN } # - - - - - - - - - - - - # main () # - - - - - - - - - - - - # # see how we were called case "$1" in start) removeqdisc $INET removeqdisc $LAN outboundshaping $INET inboundshaping $LAN marking ;; stop) $iptables -t mangle -F PREROUTING $iptables -t mangle -F POSTROUTING removeqdisc $LAN removeqdisc $INET ;; restart) $0 stop $0 start ;; status) shift htbstatus $@ ;; *) echo "usage: $0 {start|stop|restart|status}" ;; esac # ]]> # - end of htb-script