Script for advanced QoS control

Discussion in 'HyperWRT Firmware' started by dolly_oops, Aug 10, 2006.

  1. dolly_oops

    dolly_oops Network Guru Member


    I've written a script which provides a few commands to easily allow you to define rules for QOS control. Hope you find it useful...


    I have only tried this script on my own WRT54G v1.1 running HyperWRT Thibor 15c. I make no claims that it will work for your own setup - even if you are running the same hardware and firmware as I am. So good luck. :)

    I'm not claiming what I've written is necessary, you can certainly get by without it, I'm just trying to make it a bit friendlier for people who want to do this sort of thing.

    The script - why it was written

    I was in the position where I was quite happy with the QOS controls that were on offer with HyperWRT, until my ISP started strangling BitTorrent traffic. I encrypted it to bypass it, but then ended up with the problem that the QOS rule to mark all BT traffic as low priority was no longer working.

    I figured out a solution of how I could identify the traffic (I could force all incoming and outgoing traffic to be in a particular port range), but the GUI for HyperWRT didn't give me that option - port ranges only apply to the destination port, rather than the source port. I'm not critising HyperWRT here - I think the work that Thibor and others have put into it is great, it's just that I needed something specific which hasn't been put into the nice web interface.

    So I started looking into defining QOS controls manually, reading about queueing disciplines and how to use the "tc" command to set things up. It took me a while to get through it, and I tried experimenting with it... but failed. I was having all sorts of problems getting it to work. It was taking a lot to set up the sort of QOS traffic control I wanted, before I could even get to the point where I was flagging the BT traffic.

    What I ideally wanted was the QOS mechanism set up by HyperWRT, but the ability to manually set up filters to tag traffic and force it to prioritised in a certain way.

    When I figured out how to do that, I started sticking it in a few little scripts, and kept improving and improving it, and I ended up with this. It's also the first shell script I've written for a Linux platform, so I'm quite pleased with it (it might also be complete rubbish too). :)

    The script - what does it offer

    The main thing it does is provide a command called "qos_add_rule". It provides a nice frontend to iptables (fills in some of the values for you) to add a particular rule. You should be able to use it even if you've never used iptables before, but it can be helpful if you want to add rules for matching that aren't nicely catered for here.

    The other main thing it does is allow you to add rules even if you have other QOS rules in place defined by the GUI. That in itself isn't too hard, but if you when QOS is enabled in HyperWRT, there is a rule added which handles all traffic not handled by other QOS rules, to mark all traffic as "medium-low" priority. If you just try to add rules to the table, they will be added (by default) to the end of the table, after this "catch-all" default rule, and so no traffic will get matched by your rules.

    But if you run the "qos_edit_start" command before entering all your rules, and then "qos_edit_end" when you're done, all your rules will be placed before this default rule.


    You can't specify things like what ethernet port to filter on like you can in the GUI. But that doesn't really matter, since you can still use HyperWRT's GUI to add those rules in.

    Any custom rules you add in won't show up in the QoS page either.

    Available commands
      qos_add_rule - Add a filter for matching traffic for QoS purposes.
      qos_edit_start - Call this before adding all of your QoS rules.
      qos_edit_end - Call this after adding all of your QoS rules.
      qos_table_list - Shows you the list of filters which are in place.
      qos_table_clear - Clears the list of filters which are in place.
      qos_table_stats - Displays the traffic control statistics.
                        See script comments for an explanation.
    How to use

    First of all, enable QoS through HyperWRT web interface.
    Secondly, add any QoS rules you want in the web interface. You don't have to add all QoS rules through qos_add_rule, you can still define whatever rules you want through the interface if you want.

    If you want to play around with the commands, then SSH into the router, paste the script into the shell, and then they should be available to you.

    If you want to define rules which are added when your router boots, go to the Administration page, choose Edit Firewall Script, paste in the script, and then after it, add something like this:

    # Add rules.


    qos_add_rule is self-documenting, but here are examples of how to use it anyway:

      # Highest priority SSH traffic, match by protocol
      qos_add_rule highest proto ssh
      # High priority traffic for a certain MAC address.
      # Also insert this rule at the front of the chain before other rules.
      qos_add_rule high start mac 12:34:56:78:90:AB
      # Medium priority traffic from
      # Set the rule at position 2.
      qos_add_rule medium at 2 srcip
      # Medium priority for all traffic destined for port 4095.
      # Only applies if it originates from
      qos_add_rule medium dstport 4095 --source
      # Low priority traffic for all traffic arriving from ports 4683 to 4685.
      # UDP traffic only.
      qos_add_rule low udp srcport 4683:4685

    Feel free to suggest improvements to this. I'll try and keep an up-to-date version on here. Ideally, Thibor will put this in HyperWRT, and these commands will be available for everyone to use *hint hint*... :)
  2. dolly_oops

    dolly_oops Network Guru Member

    The script itself....

    # Script to present a simpler interface to add additional QOS rules.
    qos_add_rule() {
        if [ $# -gt 0 ]; then
            if [ "$1" == "-t" ]; then
            if [ "$1" == "--examples" ]; then
                echo 1>&2 'Examples:'
                echo 1>&2
                echo 1>&2 '  Highest priority SSH traffic, match by protocol:'
                echo 1>&2 '      qos_add_rule highest proto ssh'
                echo 1>&2
                echo 1>&2 '  High priority traffic for a certain MAC address.'
                echo 1>&2 '  Also insert this rule at the front of the chain before other rules.'
                echo 1>&2 '      qos_add_rule high start mac 12:34:56:78:90:AB'
                echo 1>&2
                echo 1>&2 '  Medium priority traffic from'
                echo 1>&2 '  Set the rule at position 2.'
                echo 1>&2 '      qos_add_rule medium at 2 srcip'
                echo 1>&2
                echo 1>&2 '  Medium priority for all traffic destined for port 4095.'
                echo 1>&2 '  Only applies if it originates from'
                echo 1>&2 '      qos_add_rule medium dstport 4095 --source'
                echo 1>&2
                echo 1>&2 '  Low priority traffic for all traffic arriving from ports 4683 to 4685.'
                echo 1>&2 '  UDP traffic only.'
                echo 1>&2 '      qos_add_rule low udp srcport 4683:4685'
        # You need at least one value indicating the priority.
        if [ $# -lt 2 ]; then
            echo 1>&2 'Usage:'
            echo 1>&2 '  qos_add_rule prio [insert_at] [protocol] [matchtype] args...'
            echo 1>&2
            echo 1>&2 'Arguments:'
            echo 1>&2 '  prio: highest (0x2e), high (0x0a), medium (0x12), low (0x22)'
            echo 1>&2 '  insert_at: start, end, at X'
            echo 1>&2 '  protocol: ip, tcp, udp, tcpudp'
            echo 1>&2 '  matchtype: proto, mac, srcip, dstip, srcport, dstport'
            echo 1>&2
            echo 1>&2 'To see what iptables command is generated without executing it:'
            echo 1>&2 '  qos_add_rule -t [arguments]'
            echo 1>&2
            echo 1>&2 'For examples, run as:'
            echo 1>&2 '  qos_add_rule --examples'
            echo 1>&2
        case "$1" in
          highest)  shift; dscp="2e";;
          high)     shift; dscp="0a";;
          medium)   shift; dscp="12";;
          low)      shift; dscp="22";;
          *)        echo 1>&2 "No priority setting given."; return;;
        addto="-A PREROUTING"
        case "$1" in
          start)  shift; addto="-I PREROUTING 1";;
          end)    shift; addto="-A PREROUTING";;
          at)     shift; addto="-I PREROUTING $1"; shift;;
        if [ "$1" = "tcpudp" -o "$1" = "tcp" -o "$1" = "udp" -o "$1" = "ip" ]; then
        case "$1" in
          proto)    shift; match_type="-m layer7 --l7proto ";;
          mac)      shift; match_type="-m mac --mac-source ";;
          srcip)    shift; match_type="--source ";;
          dstip)    shift; match_type="--destination ";;
          srcport)  shift; match_type="--source-port "; need_separate_rules=1;;
          dstport)  shift; match_type="--destination-port "; need_separate_rules=1;;
        # If there's a src or dst port specified, we will have to change
        # the "default" protocol to be "tcpudp" (set up separate rules for
        # tcp or udp).
        if [ ${need_separate_rules} = 1 ]; then
            if [ ${proto} = "ip" ]; then
                echo 1>&2 You cannot choose ip as the protocol when specifying ports.
        # Set what the protocol should be now, if not explicitly set.
        if [ ${proto} = "default" ]; then
            if [ ${need_separate_rules} = 1 ]; then
        # Construct the arguments for iptables (exclude the protocol bit).
        iptargs="-t mangle ${addto} ${match_type}$* -j DSCP --set-dscp 0x${dscp}"
        if [ ${test} = 0 ]; then
            iptcmd="echo iptables"
        # Let's run the iptables commands.
        if  [ ${proto} = "tcpudp" ]; then
            `echo ${iptcmd} -p tcp "${iptargs}"`
            `echo ${iptcmd} -p udp "${iptargs}"`
            `echo ${iptcmd} -p "${proto}" "${iptargs}"`
    # Useful for debugging.
    alias qos_table_clear="iptables --flush PREROUTING --table mangle"
    alias qos_table_list="iptables --list PREROUTING --table mangle"
    # I believe this is how traffic is grouped:
    #   qdisc pfifo 240 <- Low priority traffic.
    #   qdisc pfifo 230 <- Default priority traffic.
    #   qdisc pfifo 220 <- Medium priority traffic.
    #   qdisc pfifo 210 <- High priority traffic.
    #   qdisc pfifo 100 <- Highest priority traffic.
    alias qos_table_stats="tc -s qdisc"
    # "Starting" and "stopping" commands.
    # The start command locates the last rule (based on the format of the table printed by iptables) and
    # removes the last rule, which we will put back when we call qos_edit_end.
    qos_edit_start() {
        for i in `iptables --list PREROUTING -t mangle | wc -l`
            do iptables --table mangle -D PREROUTING `expr $i - 2`
    alias qos_edit_end="iptables -t mangle -p ip -A PREROUTING -j MARK --set-mark=0x100"
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice