OpenWRT QoS – a Simple Recipe

Low priority traffic is clogging your Internet connection? Read on.

This is NOT a definitive guide, as tc is very complex powerful – what follows is merely a simple working example on how to prioritize traffic in OpenWRT (or, actually, any Linux box) using tc.

First of all, traffic prioritization happens only when there is traffic to be sent and only if the pipe is too small, so that the packets have to wait in queue, i.e. when traffic control can choose from multiple packets the packet to send first. It’s not possible to directly prioritize traffic being received (although, for TCP connections, delaying ACK packets will make sender slow down).qos

That said, here is how the script attached below works:

  1. It deletes all queuing disciplines from the internet interface $IF_DSL
  2. It defines root queue 1, specifying that by default traffic will be sent with class 20 (1:20 below)
  3. It creates a sub queue/class 1:1 clamping down traffic at ISP upload speed. This is needed to make sure the queue is created in the router, not in the cable/DSL modem, connected to the router by much faster connection
  4. It creates 3 sub-queues/classes:
    1. 1:10 for real-time priority traffic. The parameters mean that it can send at speed limit for up to 60ms, and after that send at 20% of the speed limit, if there is a queue.
    2. 1:30 for bulk traffic – 10% of the speed limit if there is a queue, otherwise full speed
    3. 1:20 for everything else – 70% of interface speed if there is a queue, otherwise full speed
  5. If all classes are present, they will share the link at 20%:10%:70%, if one is absent, remaining classes will share the link in proportion, e.g. 10%:70%
  6. Now we use iptables to mark the traffic destined for each queue:
    1. Everything coming from the home server $IP_SERVER is considered bulk (e.g. cloud backup)
    2. TCP SYN, RST, ACK are sent in real-time
    3. SSH is sent in real-time
    4. Small TCP (not sustained transmission) and UDP (including VoIP) packets are sent in real-time
    5. ICMP is sent in real-time
    6. Everything else is normal, as specified in the step 2 above

To run the script, make sure you install the following packages: tc, kmod-ipt-ipopt, iptables-mod-ipopt. Upload the script to your router and set it up as a startup script:

  1. Use you favorite ssh FTP app, e.g. winscp on windows or Nemo on linux
  2. Put the file below to /root – your home folder as tc.sh
  3. In LuCi >> System >> Startup page, put a command “/bin/sh /root/tc.sh” without quotes somewhere before last line “exit 0”
#!/bin/sh
 
##############################################################
# Variables
TC=/usr/sbin/tc #-------- location of traffic control
IPT=/usr/sbin/iptables #- location of iptables
IF_DSL=eth0 #------- interface to dsl
UP_RATE=6400 #------------ available bandwidth in kilobits/sec
IP_SERVER=192.168.1.195
 
MODULES='xt_length sch_hfsc sch_ingress'
 
##############################################################
# Load modules
for i in $MODULES ; do
        insmod $i
done
 
##############################################################
# Manipulating qdiscs
$TC qdisc del dev $IF_DSL root    2> /dev/null > /dev/null
$TC qdisc del dev $IF_DSL ingress 2> /dev/null > /dev/null
###
# set root default bucket
$TC qdisc add dev $IF_DSL root       handle  1:   hfsc default 20
$TC class add dev $IF_DSL parent 1:  classid 1:1  hfsc sc rate ${UP_RATE}kbit ul rate ${UP_RATE}kbit
# rt
$TC class add dev $IF_DSL parent 1:1 classid 1:10 hfsc rt m1 ${UP_RATE}kbit d 60ms m2 $((20*$UP_RATE/100))kbit
# normal
$TC class add dev $IF_DSL parent 1:1 classid 1:20 hfsc ls rate $((70*$UP_RATE/100))kbit ul rate ${UP_RATE}kbit
# bulk
$TC class add dev $IF_DSL parent 1:1 classid 1:30 hfsc ls rate $((10*$UP_RATE/100))kbit ul rate ${UP_RATE}kbit

##################################################################
# drop existing mangle rules in POSTROUTING
$IPT -t mangle -F POSTROUTING
 
IPTMOD="$IPT -t mangle -A POSTROUTING -o $IF_DSL"
 
##################################################################################
# server upload - bulk
$IPTMOD -s $IP_SERVER -j CLASSIFY --set-class 1:30
# real-time
$IPTMOD -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j CLASSIFY --set-class 1:10
$IPTMOD -p tcp --dport 22 -j CLASSIFY --set-class 1:10
$IPTMOD -p udp -m length --length :170 -j CLASSIFY --set-class 1:10
$IPTMOD -p tcp -m length --length :148 -j CLASSIFY --set-class 1:10
$IPTMOD -p icmp -j CLASSIFY --set-class 1:10
# everything else is normal traffic
# tc qdisc del dev eth0 root
# tc qdisc del dev eth0 ingress

Download script as a text file: ts-example.sh.txt

Credits:

  1. http://www.linuxquestions.org/questions/linux-networking-3/expanding-my-tc-script-to-include-prioritizing-for-tcp-syn-ack-etc-4175479817/
  2. https://wiki.openwrt.org/doc/howto/packet.scheduler/packet.scheduler.example3
  3. http://man7.org/linux/man-pages/man7/tc-hfsc.7.html

Update:

It makes sense to separate the classify rules into a custom firewall script,because firewall script is reloaded more often, or at least each time fw rules are updated. To do so, remove the portion of the tca.sh script above starting with “drop existing mangle rules” and place something like the following into Network->Firewall->Custom rules tab in luci. Note that I expanded the path to iptables:

/usr/sbin/iptables -t mangle -A POSTROUTING -o eth0 -s 192.168.1.195 -j CLASSIFY --set-class 1:30
/usr/sbin/iptables -t mangle -A POSTROUTING -o eth0 -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j CLASSIFY --set-class 1:10
/usr/sbin/iptables -t mangle -A POSTROUTING -o eth0 -p tcp --dport 22 -j CLASSIFY --set-class 1:10
/usr/sbin/iptables -t mangle -A POSTROUTING -o eth0 -p udp -m length --length :170 -j CLASSIFY --set-class 1:10
/usr/sbin/iptables -t mangle -A POSTROUTING -o eth0 -p tcp -m length --length :148 -j CLASSIFY --set-class 1:10
/usr/sbin/iptables -t mangle -A POSTROUTING -o eth0 -p icmp -j CLASSIFY --set-class 1:10

2 thoughts on “OpenWRT QoS – a Simple Recipe

  1. hfsc-convert

    just wanted to say: this script is absolutely excellent. an excellent balance of simplicity and effectiveness, really solved my limited bandwidth problems, and made learning about hfsc much easier than some of the absurdly complex hfsc scripts people post online.

    i made some changes to it: one class for email (via ports 465 and 993), one class for VOIP (via ports used by facetime, whatsapp, etc.) and small packets (which also makes a big difference to web browsing when uploading) and another class for everything else. note that you should not try to match all packets explicitly into the ‘everything else’ class using iptables, you should instead rely on the `tc` line which features `hfsc default 20`.

    tc also has a way to produce ASCII diagrams which show where the traffic is actually going, which is very useful for testing. i also made it into a full openwrt init script as openwrt does not have a full bash shell, so it is useful to be able to write short commands to reload rules and show traffic analysis (as you will probably have to fiddle with it alot).

    i will try to follow up this post with more explicit details, including some good documentation i also found, in the future.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *