Phil Sutter (phil@nwl.cc)
January 2016
Standard practice when transmitting packets over a medium which may block (due to congestion, e.g.) is to use a queue which temporarily holds these packets. In Linux, this queueing approach is where QoS happens: A Queueing Discipline (qdisc) holds multiple packet queues with different priorities for dequeueing to the network driver. The classification (i.e. deciding which queue a packet should go into) is typically done based on Type Of Service (IPv4) or Traffic Class (IPv6) header fields but depending on qdisc implementation, might be controlled by the user as well.
Qdiscs come in two flavors, classful or classless. While classless qdiscs are not as flexible as classful ones, they also require much less customizing. Often it is enough to just attach them to an interface, without exact knowledge of what is done internally. Classful qdiscs are the exact opposite: flexible in application, they are often not even usable without insightful configuration.
As the name implies, classful qdiscs provide configurable classes to sort traffic into. In it's basic form, this is not much different than, say, the classless pfifo_fast which holds three queues and classifies per packet upon priority field. Though typically classes go beyond that by supporting nesting and additional characteristics like e.g. maximum traffic rate or quantum.
When it comes to controlling the classification process, filters come into play. They attach to the parent of a set of classes (i.e. either the qdisc itself or a parent class) and specify how a packet (or it's associated flow) has to look like in order to suit a given class. To overcome this simplification, it is possible to attach multiple filters to the same parent, which then consults each of them in row until the first one accepts the packet.
Before getting into detail about what filters there are and how to use them, a simple setup of a qdisc with classes is necessary:
(1) # tc qdisc replace dev eth0 root handle 1: htb default 30 (2) # tc class add dev eth0 parent 1: classid 1:1 htb rate 95mbit (3) # alias tclass='tc class add dev eth0 parent 1:1' (4) # tclass classid 1:10 htb rate 1mbit ceil 20mbit prio 1 (4) # tclass classid 1:20 htb rate 90mbit ceil 95mbit prio 2 (4) # tclass classid 1:30 htb rate 1mbit ceil 95mbit prio 3 (5) # tc qdisc add dev eth0 parent 1:10 fq_codel (5) # tc qdisc add dev eth0 parent 1:20 fq_codel (5) # tc qdisc add dev eth0 parent 1:30 fq_codelA little explanation for the unfamiliar reader:
Without any additional setup done, now all traffic leaving eth0 is shaped to 95mbit/s and directed through class 1:30. This can be verified by looking at the Sent field of the class statistics printed via tc -s class show dev eth0: Only the root class 1:1 and it's child 1:30 should show any traffic.