#!/bin/bash #---------------------------------------------------- # File: hfsc_shaper.sh # Version: 0.3 # Edited: Florian "Bluewind" Pritz # Author: Maciej Bliziński, http://automatthias.wordpress.com/ #---------------------------------------------------- # # Special Thanks to # Maciej Bliziński, http://automatthias.wordpress.com/ # # References: # http://www.voip-info.org/wiki/view/QoS+Linux+with+HFSC # http://www.nslu2-linux.org/wiki/HowTo/EnableTrafficShaping # http://www.cs.cmu.edu/~hzhang/HFSC/main.html ######################################################################## # CONFIGURATION ######################################################################## # Uplink and downlink speeds # Normally use a bit lower values than your real speed, but # you should experiment a bit DOWNLINK=100000 UPLINK=20500 # Device that connects you to the Internet DEV="extern0" # IP addresses of the VoIP phones, # if none, set VOIPIPS="" VOIPIPS=''; DATAIPS=''; source /etc/qos_ports.sh # Interactive class: SSH, DNS and gaming (csgo) # INTERACTIVEPORTS="22 18765 1200 3478 4379 4380 " # VoIP / gaming #VOIPPORTS="5060:5100 10000:11000 5000:5059 8000:8016 5004 1720 1731" # VOIPPORTS="53 27000:27036 " # WWW, jabber and IRC # BROWSINGPORTS="80 443 8080 " # Everything unspecified will be here (inbetween Browsing and Data) # FTP, Mail... # DATAPORTS="21 25 110 137:139 143 993 995 4662 4664 27014:27050" # The lowest priority traffic: eDonkey, Bittorrent, etc. #P2PPORTS="6881:6999 36892 8333" # P2PPORTS="" ######################################################################## # CONFIGURATION ENDS HERE ######################################################################## function check_device() { if [ -z "$DEV" ] ; then echo "$0: stop requires a device, aborting." exit -1 fi } function stop() { check_device # Reset everything to a known state (cleared) tc qdisc del dev $DEV root &> /dev/null tc qdisc del dev $DEV ingress &> /dev/null # Flush and delete tables iptables -t mangle --delete POSTROUTING -o $DEV -j THESHAPER &> /dev/null iptables -t mangle --flush THESHAPER &> /dev/null iptables -t mangle --delete-chain THESHAPER &> /dev/null echo "Shaping removed on $DEV." } function start() { check_device if [ -z "$DOWNLINK" ] ; then echo "$0: start requires a downlink speed, aborting." exit -1 fi if [ -z "$UPLINK" ] ; then echo "$0: start requires an uplink speed, aborting." exit -1 fi # Traffic classes: # 1:2 Low latency (VoIP) # 1:3 Interactive (SSH, DNS, ACK, Quake) # 1:4 Browsing (HTTP, HTTPs) # 1:5 Default # 1:6 Middle-low priority # 1:7 Lowest priority # add HFSC root qdisc tc qdisc add dev $DEV root handle 1: hfsc default 5 # add main rate limit class tc class add dev $DEV parent 1: classid 1:1 hfsc \ sc rate ${UPLINK}kbit ul rate ${UPLINK}kbit # VoIP: guarantee full uplink for 200ms, then 6/10 tc class add dev $DEV parent 1:1 classid 1:2 hfsc \ sc m1 ${UPLINK}kbit d 200ms m2 $((6*$UPLINK/10))kbit \ ul rate ${UPLINK}kbit # Interactive traffic: guarantee 8/10 uplink for 50ms, then # 5/10 of the uplink tc class add dev $DEV parent 1:1 classid 1:3 hfsc \ sc m1 $((8*$UPLINK/10))kbit d 50ms m2 $((5*$UPLINK/10))kbit \ ul rate ${UPLINK}kbit # Browsing: guarantee 2/10 uplink for 200ms, then # guarantee 1/10 tc class add dev $DEV parent 1:1 classid 1:4 hfsc \ sc m1 $((2*$UPLINK/10))kbit d 200ms m2 $((1*$UPLINK/10))kbit \ ul rate ${UPLINK}kbit # Default traffic: guarantee 1/10 uplink for 100ms, # then guarantee 1/10 tc class add dev $DEV parent 1:1 classid 1:5 hfsc \ sc m1 $((1*$UPLINK/10))kbit d 100ms m2 $((1*$UPLINK/10))kbit \ ul rate ${UPLINK}kbit # Middle-low taffic: don't guarantee anything for the first 5 seconds, # then guarantee 1/10 tc class add dev $DEV parent 1:1 classid 1:6 hfsc \ sc m1 0 d 5s m2 $((1*$UPLINK/10))kbit \ ul rate ${UPLINK}kbit # Lowest taffic: don't guarantee anything for the first 10 seconds, # then guarantee 1/20 tc class add dev $DEV parent 1:1 classid 1:7 hfsc \ sc m1 0 d 10s m2 $((1*$UPLINK/40))kbit \ ul rate ${UPLINK}kbit # add THESHAPER chain to the mangle table in iptables iptables -t mangle --new-chain THESHAPER iptables -t mangle --insert POSTROUTING -o $DEV -j THESHAPER setclassbyport_udp() { port=$1 CLASS=$2 iptables -t mangle -A THESHAPER -p udp --sport $port -j CLASSIFY --set-class $CLASS iptables -t mangle -A THESHAPER -p udp --dport $port -j CLASSIFY --set-class $CLASS } setclassbyport_tcp() { port=$1 CLASS=$2 iptables -t mangle -A THESHAPER -p tcp --sport $port -j CLASSIFY --set-class $CLASS iptables -t mangle -A THESHAPER -p tcp --dport $port -j CLASSIFY --set-class $CLASS } setclassbyport_both() { port=$1 CLASS=$2 iptables -t mangle -A THESHAPER -p udp --sport $port -j CLASSIFY --set-class $CLASS iptables -t mangle -A THESHAPER -p udp --dport $port -j CLASSIFY --set-class $CLASS iptables -t mangle -A THESHAPER -p tcp --sport $port -j CLASSIFY --set-class $CLASS iptables -t mangle -A THESHAPER -p tcp --dport $port -j CLASSIFY --set-class $CLASS } # for port in $VOIPPORTS; do setclassbyport_udp $port 1:2; done for port in $VOIPPORTS; do setclassbyport_both $port 1:2; done for port in $INTERACTIVEPORTS; do setclassbyport_both $port 1:3; done for port in $BROWSINGPORTS; do setclassbyport_both $port 1:4; done for port in $DATAPORTS; do setclassbyport_tcp $port 1:6; done # for port in $DATAPORTS; do setclassbyport_both $port 1:6; done for port in $P2PPORTS; do setclassbyport_both $port 1:7; done if [[ -n "$VOIPIPS" ]]; then echo "enabling ip-based VOIP class 1:2.." for VOIP in $VOIPIPS; do echo " [+] $VOIP" iptables -t mangle -A THESHAPER --src $VOIP -j CLASSIFY --set-class 1:2 iptables -t mangle -A THESHAPER --dst $VOIP -j CLASSIFY --set-class 1:2 done fi if [[ -n "$DATAIPS" ]]; then echo "enabling ip-based DATA class 1:7.." for DATAIP in $DATAIPS; do echo " [+] $DATAIP" iptables -t mangle -A THESHAPER --src $DATAIP -j CLASSIFY --set-class 1:7 iptables -t mangle -A THESHAPER --dst $DATAIP -j CLASSIFY --set-class 1:7 done fi # put large (1024+) https packets in default category iptables -t mangle -A THESHAPER \ -p tcp --dport 443 \ -m length --length 1024: \ -j CLASSIFY --set-class 1:5 # put large (1024+) http packets in default category iptables -t mangle -A THESHAPER \ -p tcp --dport 80 \ -m length --length 1024: \ -j CLASSIFY --set-class 1:5 iptables -t mangle -A THESHAPER \ -p tcp --dport 8080 \ -m length --length 1024: \ -j CLASSIFY --set-class 1:5 # Type of service filters (see /etc/iproute2/rt_dsfield) iptables -t mangle -A THESHAPER \ -m tos --tos 0x08 \ -j CLASSIFY --set-class 1:7 iptables -t mangle -A THESHAPER \ -m tos --tos 0x10 \ -j CLASSIFY --set-class 1:2 # put large (1024+) ssh packets in default category iptables -t mangle -A THESHAPER \ -p tcp --dport 22 \ -m length --length 1024: \ -j CLASSIFY --set-class 1:6 # put small ssh packets in interactive category. they should be here, but # ssh sets ToS fields for non-interactive sessions to 0x08 and multiplexing # counts as that iptables -t mangle -A THESHAPER \ -p tcp --dport 22 \ -m length --length :1024 \ -j CLASSIFY --set-class 1:2 # To speed up downloads while an upload is going on, put short ACK # packets in the interactive class iptables -t mangle -A THESHAPER \ -p tcp \ -m tcp --tcp-flags FIN,SYN,RST,ACK ACK \ -m length --length :64 \ -j CLASSIFY --set-class 1:3 # put large (512+) icmp packets in default category iptables -t mangle -A THESHAPER \ -p icmp \ -m length --length 512: \ -j CLASSIFY --set-class 1:5 # ICMP (ip protocol 1) in the voip class iptables -t mangle -A THESHAPER \ -p icmp \ -m length --length :512 \ -j CLASSIFY --set-class 1:2 # put all traffic from user torrent into p2p category (only works for the host this script runs on) #iptables -t mangle -A THESHAPER \ # --match owner --uid-owner 169 -j CLASSIFY --set-class 1:7 # Try to control the incoming traffic as well. # Set up ingress qdisc #tc qdisc add dev $DEV handle ffff: ingress # Filter everything that is coming in too fast # It's mostly HTTP downloads that keep jamming the downlink, so try to restrict # them to 90/100 of the downlink. # FIXME: slows down too much #tc filter add dev $DEV parent ffff: protocol ip prio 50 \ #u32 match ip src 0.0.0.0/0 \ #match ip protocol 6 0xff \ #match ip sport 80 0xffff \ #police rate $((90*${DOWNLINK}/100))kbit \ #burst 10k drop flowid :1 #tc filter add dev $DEV parent ffff: protocol ip prio 50 \ #u32 match ip src 0.0.0.0/0 \ #police rate $((90*${DOWNLINK}/100))kbit \ #burst $((90*${DOWNLINK}/100*2)) drop flowid :1 #=========== downstream stuff =========== # "${tc_cake}" qdisc del dev $DEV root # "${tc_cake}" qdisc add dev $DEV root cake bandwidth $UPLINK diffserv8 # "${ip_cake}" link del name dev ifb4$DEV # "${ip_cake}" link add name ifb4$DEV type ifb # "${tc_cake}" qdisc del dev $DEV ingress # "${tc_cake}" qdisc add dev $DEV handle ffff: ingress # "${tc_cake}" qdisc del dev ifb4$DEV root # "${tc_cake}" qdisc add dev ifb4$DEV root cake bandwidth ${DOWNLINK}kbit besteffort # if you dont bring the device up your connection will lock up on the next step. # "${ip_cake}" link set dev ifb4$DEV up # "${tc_cake}" filter add dev $DEV parent ffff: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev ifb4$DEV echo "Shaping added on $DEV." } function status() { check_device echo "[qdisc]" tc -s qdisc show dev $DEV echo "" echo "[class]" tc -s class show dev $DEV echo "" echo "[filter]" tc -s filter show dev $DEV echo "" echo "[iptables]" iptables -n -t mangle -L THESHAPER -v -x 2> /dev/null } case "$1" in status) status ;; stop) stop ;; start) start ;; restart) stop start ;; *) echo "$0 [ACTION] [device]" echo "ACTION := { start | stop | status | restart }" exit ;; esac