tc – show / manipulate traffic control settings




This script will shape traffic, based on IP, and have it QoS to a specific rate. The type of QoS I use is HTB.

In this example I use eth0 as it is the interface connecting to the client portion of the network, and eth1 being the external interface of my gateway.

Think of the active flow of traffic shaping in this order.

Packet -> Filter -> Class -> Qdisc -> Freedom?

Filter captures the IP address of the packet and passes it to the corresponding class.

Class sets the actual speed of the packet and puts it in the qdisc for delivery.

Qdisc is the “bucket” in HTB that will deliver the packet.

The actual creation and of the QoS is done in top down order.

1. Create your qdisc
2. Create your class
3. Create your filter

1. Creating qdisc

Before you create your qdisc, by default you will have a root qdisc of PFIFO_FAST that you must delete.

You can check what qdisc’s are setup using:

tc qdisc show dev eth0
qdisc pfifo_fast 0: root bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1

To delete this PFIFO_FAST qdisc use:

tc qdisc del dev eth0 root

Now to add the new root HTB qdisc:

tc qdisc add dev eth0 root handle 1: htb

Viewing the new qdisc should look something like:

tc qdisc show dev eth0
qdisc htb 1: r2q 10 default 0 direct_packets_stat 28228

2. Class creation.
Now that we have our qdisc ready to go we can create a class.

The command to do this is pretty straight forward.

tc class add dev eth0 parent 1: classid 1:[Flow ID] htb [rate]

Replacing the flow id and rate to what you need. The flow id is just an identifier for your filter later. The rate is something like 128kbit or 1.5mbit .. etc.

To view the new class:

tc class show dev eth0
class htb 1:252 root prio 0 rate 5000Kbit ceil 5000Kbit burst 4Kb cburst 4Kb

You can see we created a parent class with a flow id of 252 and a rate of 5mbit (5000Kbit)

3. Filters
Now lets create a filter to direct traffic to that new class using the flow id we used of 252.

tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dst [IP Address[[/[Netmask] flowid 1:[Flow ID]

The key for the filter above is the u32 match which allows you to match on specific information about the packet, in this case the DST IP Address. Replace the IP Address / Netmask and Flow ID.

Viewing the new filter:

tc filter show dev eth0
filter parent 1: protocol ip pref 1 u32 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:252 match 0a010503/ffffffff at 16

The output is a little cryptic but its not to bad. You can see the flow id we used of 252 and the u32 match for the ip address / netmask in hex.

If we ping 0xa010503 the IP address becomes easier to read:

ping 0x0a010503
PING 0x0a010503 ( 56(84) bytes of data.

You can delete a filter by referencing the 800::801 part.
For example to delete the one we just created:

tc filter del dev eth0 parent 1: proto ip prio 1 handle 800::801 u32

A Packet from gets intercepted by our filter based on the u32 match.
The filter sends the packet to the class matching the same flow id of 252
The class assigns a speed of 5mbit which in turn passes it to the qdisc.
The qdisc does its thing and the packet is delivered.

Tc will only support a Flow ID up to 9999
This will only rate limit download speeds, which are packets to the user. I have not found a way to rate limit upload speeds, packets from the user.

You can use this information to match on entire subnets.
You can create multiple filters to point to the same class / flow id
In my server I track the Flow ID / IP Address in a database and I can then manage them easier via a webpage.

This is just a simple yet very powerful example of how to use HTB QoS.
There are lots of options you can set on each command. This is just a quick guide to get you up and running. I would highly recomend reading up more on

Let me know what you think or if you have any suggestions / corrections.