DSCP based QoS with HTB
Contents
DSCP based QoS with HTB
About
This page tries to describe a way to prioritize traffic by using DSCP tags. The DiffServ Code Point is a field in the IP header that allows you to classify traffic. DSCP is meant to be administered in a per-hob-based way, allowing each router on a path to determine how each traffic class should be prioritized. The solution described in this document is built around the Hierarchical Token Bucket queuing algorithm in RouterOS, dividing the 64 possible DSCP code values into the 8 queues available. This solution also utilizes the tree-based queuing, in order to have a parent queue do bandwidth control, with sub-queues for each possible DSCP value.
The actual queuing is done as per this table:
| Name | Precendence | DSCP Range | HTB Priority |
|---|---|---|---|
| Routing (default) | 000 (0) | 000000(0) – 000111 (7) | 8 |
| Priority | 001 (1) | 001000 (8) – 001111 (15) | 7 |
| Immediate | 010( (2) | 010000 (16) – 010111 (23) | 6 |
| Flash | 011 (3) | 011000 (24) – 011111 (31) | 5 |
| Flash Override | 100 (4) | 100000 (32) – 100111 (39) | 4 |
| Critical | 101 (5) | 101000 (40) – 101111 (47) | 3 |
| Internetwork Control | 110 (6) | 111000 (48) – 110111 (55) | 2 |
| Network Control | 111 (7) | 111000 (56) – 111111 (63) | 1 |
This solution has been tested on RB450, RB600 and RB1000 using any 3.x interface.
DSCP marking/mangling
In order to match DSCP values in your queues, it is necessary to mark the packets using firewall mangling. This is best done with this command:
:for x from 0 to 63 do={/ip firewall mangle add action=mark-packet chain=postrouting \
comment=("dscp_" . $x . "_eth") disabled=no dscp=$x new-packet-mark=("dscp_" . $x . "_eth") passthrough=no}
This command creates 64 lines under /ip firewall mangle, that simply marks each packet with a DSCP value to be processed later.
Having that done, it's time to move on to the actual queues.
Set up the queue tree
The next example assumes that ether1 is the wan interface, and your available bandwidth is 5Mbit/s.
/queue tree
add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=5000000 name=ether1 \
parent=ether1 queue=default
#prio8
:for z from 0 to 7 do={/queue tree add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=0 \
name=("routine_" . $z . "_ether1") packet-mark=("dscp_" . $z . "_eth") parent=ether1 priority=8 queue=ethernet-default}
#prio7
:for z from 8 to 15 do={/queue tree add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=0 \
name=("priority_" . $z . "_ether1") packet-mark=("dscp_" . $z . "_eth") parent=ether1 priority=7 queue=ethernet-default}
#prio 6
:for z from 16 to 23 do={/queue tree add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=0 \
name=("immediate_" . $z . "_ether1") packet-mark=("dscp_" . $z . "_eth") parent=ether1 priority=6 queue=ethernet-default}
#prio 5
:for z from 24 to 31 do={/queue tree add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=0 \
name=("flash_" . $z . "_ether1") packet-mark=("dscp_" . $z . "_eth") parent=ether1 priority=5 queue=ethernet-default}
#prio 4
:for z from 32 to 39 do={/queue tree add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=0 \
name=("flash_override_" . $z . "_ether1") packet-mark=("dscp_" . $z . "_eth") parent=ether1 priority=4 queue=ethernet-default}
#prio 3
:for z from 40 to 47 do={/queue tree add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=0 \
name=("critical_" . $z . "_ether1") packet-mark=("dscp_" . $z . "_eth") parent=ether1 priority=3 queue=ethernet-default}
#prio 2
:for z from 48 to 55 do={/queue tree add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=0 \
name=("intercon_" . $z . "_ether1") packet-mark=("dscp_" . $z . "_eth") parent=ether1 priority=2 queue=ethernet-default}
#prio 1
:for z from 56 to 63 do={/queue tree add burst-limit=0 burst-threshold=0 burst-time=0s disabled=no limit-at=0 max-limit=0 \
name=("netcon_" . $z . "_ether1") packet-mark=("dscp_" . $z . "_eth") parent=ether1 priority=1 queue=ethernet-default}
Remarks
This solution is the most flexible solution I could come up with. It is built around the philosophy that highest DSCP marking is served first. The actual shaping of the interface could be moved to a simple queue in order to be able to police differently on upstream and downstream, but I prefer to shape in both ends of a circuit, so when you have different upload and download speed, you should shape in according to the upload speed.