HFSC traffic shaping for QOS on DD-WRT: New test
After reading more about HFSC, I modified the original script I posted here.
The main changes are:
- Used a different syntax to specify the different classes.
- Changed P2P ports to include most high-numbered ports
- Updated iptables rules to only use destination port, not source port. This is because we are only filtering outgoing packets, there is an assumption no services are being hosted on the connection, and the high port numbers in P2P ports matches most all connections because the source port typically is a very high number.
#!/bin/ash
# Modified to run under DD-WRT
# http://www.morph3ous.net/2009/11/20/beta-hfsc-traffic-shaping-for-qos-on-dd-wrt/
# Go to Administration and then commands
# Paste script in and click on the save firewall button
# Reboot router and test
#
###### Script originally from
# 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
######
# Specify the uplink as 85 or 90 percent of your actual upload speed
# Uplink and downlink speeds
# removed throttling downlink
UPLINK=425
###### From original script, but code has been disabled further below
# IP addresses of the VoIP phones,
# if none, set VOIPIPS=""
VOIPIPS=""
######
# Interactive class: SSH Terminal, DNS, RDP
INTERACTIVEPORTS="22 23 53 3389"
# VoIP telephony
#VOIPPORTS="5060:5100 10000:11000 5000:5059 8000:8016 5004 1720 1731"
VOIPPORTS=""
# WWW, jabber and IRC
BROWSINGPORTS="80 443 8080"
# The lowest priority traffic: IMAP, SMTP, FTP, IMAP, IMAP/S, high port numbers likely to be P2P
P2PPORTS="110 25 21 143 993 1024:65535"
# Device that connects you to the Internet
#DEV=$(nvram get wan_ifname)
DEV=ppp0
# clean up in case re-running
# Reset everything to a known state (cleared)
tc qdisc del dev $DEV root > /dev/null 2>&1
tc qdisc del dev $DEV ingress > /dev/null 2>&1
# Flush and delete tables
iptables -t mangle --delete POSTROUTING -o $DEV -j THESHAPER > /dev/null 2>&1
iptables -t mangle --flush THESHAPER 2> /dev/null > /dev/null
iptables -t mangle --delete-chain THESHAPER 2> /dev/null > /dev/null
# start setting up QOS
# Traffic classes:
# 1:2 Interactive (SSH, DNS, ACK, Quake)
# 1:3 Low latency (VoIP) < - currently being used for Netflix movie streaming from Roku box
# 1:4 Browsing (HTTP, HTTPs)
# 1:5 Default
# 1:6 Low priority (p2p, pop3, smtp, etc)
# 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
# Interactive traffic
tc class add dev $DEV parent 1:1 classid 1:2 hfsc sc umax 1500b dmax 30ms rate $((5*$UPLINK/10))kbit ul rate ${UPLINK}kbit
# Netflix
tc class add dev $DEV parent 1:1 classid 1:3 hfsc sc umax 1500b dmax 75ms rate $((2*$UPLINK/10))kbit ul rate ${UPLINK}kbit
# Browsing
tc class add dev $DEV parent 1:1 classid 1:4 hfsc sc rate $((1*$UPLINK/10))kbit ul rate ${UPLINK}kbit
# Default traffic
tc class add dev $DEV parent 1:1 classid 1:5 hfsc sc rate $((1*$UPLINK/20))kbit ul rate ${UPLINK}kbit
# Low priority/Bulk
tc class add dev $DEV parent 1:1 classid 1:6 hfsc sc rate 500bit 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
# had to change all iptables rules below to use mark instead of classify. tc filters then pick these up and move to the proper queue
# in the future this should all be done with tc filters as this is somewhat of a hack and there is presumably at least slightly more overhead
# put packets marked by iptables in the right queues using tc filters
tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 2 fw flowid 1:2
tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 3 fw flowid 1:3
tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 4 fw flowid 1:4
tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 5 fw flowid 1:5
tc filter add dev $DEV parent 1:0 prio 0 protocol ip handle 6 fw flowid 1:6
## Note that iptables rules are being appended to the end of the chain. It appears that in the mangle
## table when using mark, all rules are processed
## put more specific rules lower down in the script otherwise more general rules below them may
## re-mark them and cause unexpected behavior
### UNLESS ##
## You add another rule with -j RETURN
## I'm doing this with some rules because I want them to be processed first. It also
## helps keep the packet statistics cleaner when issuing the iptables -t mangle -L -v command (otherwise some traffic is double-counted)
# 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 MARK --set-mark 2
iptables -t mangle -A THESHAPER -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK ACK -m length --length :64 -j RETURN
# put large (512+) icmp packets in browsing category
iptables -t mangle -A THESHAPER -p icmp -m length --length 512: -j MARK --set-mark 4
iptables -t mangle -A THESHAPER -p icmp -m length --length 512: -j RETURN
# ICMP (ip protocol 1) in the interactive class
iptables -t mangle -A THESHAPER -p icmp -m length --length :512 -j MARK --set-mark 2
iptables -t mangle -A THESHAPER -p icmp -m length --length :512 -j RETURN
# Send traffic with destination to Backblaze IPs and put into the Default Traffic class
# higher priority than P2P, but lower than browsing. Got IPs from arin.net
iptables -t mangle -A THESHAPER -p tcp -d 70.42.234.64/26 -j MARK --set-mark 5
iptables -t mangle -A THESHAPER -p tcp -d 70.42.234.64/26 -j RETURN
iptables -t mangle -A THESHAPER -p tcp -d 770.42.186.64/26 -j MARK --set-mark 5
iptables -t mangle -A THESHAPER -p tcp -d 770.42.186.64/26 -j RETURN
# for BackBlaze as well, but this IP not verified
iptables -t mangle -A THESHAPER -p tcp -d 204.11.104.0/22 -j MARK --set-mark 5
iptables -t mangle -A THESHAPER -p tcp -d 204.11.104.0/22 -j RETURN
# Prioritize Netflix streaming from Roku by IP address and put into the VoIP class
# note that device's IP must either be set manually or a static DHCP reservation set on the router
iptables -t mangle -A THESHAPER -p tcp --dport 80 -s 192.168.1.15 -j MARK --set-mark 3
iptables -t mangle -A THESHAPER -p tcp --dport 80 -s 192.168.1.15 -j RETURN
iptables -t mangle -A THESHAPER -p tcp --dport 443 -s 192.168.1.15 -j MARK --set-mark 3
iptables -t mangle -A THESHAPER -p tcp --dport 443 -s 192.168.1.15 -j RETURN
setclassbyport() {
port=$1
CLASS=$2
#iptables -t mangle -A THESHAPER -p udp --sport $port -j MARK --set-mark $CLASS
iptables -t mangle -A THESHAPER -p udp --dport $port -j MARK --set-mark $CLASS
#iptables -t mangle -A THESHAPER -p tcp --sport $port -j MARK --set-mark $CLASS
iptables -t mangle -A THESHAPER -p tcp --dport $port -j MARK --set-mark $CLASS
}
for port in $INTERACTIVEPORTS; do setclassbyport $port 2; done
for port in $VOIPPORTS; do setclassbyport $port 3; done
for port in $BROWSINGPORTS; do setclassbyport $port 4; done
for port in $P2PPORTS; do setclassbyport $port 6; done
for VOIP in $VOIPIPS
do
iptables -t mangle -A THESHAPER --src $VOIP -j MARK --set-mark 3
iptables -t mangle -A THESHAPER --dst $VOIP -j MARK --set-mark 3
done