Dear reader, I'm not updating these pages anymore. If you have tc or ip related questions, you can post them on the LARTC mailing list.


The traffic control mechanism in the Linux kernel consists of the following components:

Qdiscs are responsible for transmitting the data. Classes are attached to qdiscs and contain traffic. Each class with no child classes attached to it, always has 1 qdisc associated with it to transmit the packets and this qdisc holds all the traffic that flows in that class. Filters are attached to qdiscs and classes and split the traffic into different child-classes.

Policers are used to make sure filters match only a certain rate of packets. Policers can be shared by different filters and on different interfaces. It can be used to bound certain incoming traffic, but it's not as powerful as shaping on the outgoing interface with cbq or htb.

Other info: "Linux Traffic Control - Implementation Overview" (04/02/01). The original paper was found on and is called tc-<<DATE>>.

Root qdisc

Each NIC has one special queueing discipline associated with it: the root-qdisc. Normally this is a very basic one (a FIFO qdisc) that transmits the packets in the same order as they are entered into the queue and as fast as possible, but it's doing this unfairly. There are more complex queueing disciplines with classes which can hold a part of the traffic.

The root qdisc holds all the data that's transmitted. You can replace this root qdisc with another qdisc. So when you want to replace it with a SFQ qdisc, you can use this command:

tc qdisc add dev eth0 root handle 10: SFQ

qdiscs-classes-filters relation

Classes are attached to qdiscs and hold a part of the traffic. Classes and qdiscs are very tied into each other. Normally, classes are not responsible for handling the packets themselves. They have a qdisc associated with them to do this and this is a standard FIFO qdisc. Of course you can change this and attach a new qdisc with classes, etc ... . Each time you can split the traffic to the different classes with filters. So filters are always attached to qdiscs.

The qdiscs CBQ, DS_MARK, CSZ and p-FIFO can contain classes. All the traffic that's been put in a class by a filter, enters also the qdisc that's attached to the class.

The classes attached to a qdisc can have a different priority and/or rate. So when one packet enters class A, 2 packets enter class B. So the rate of class B is twice the rate of class A.

It's possible that a qdisc has a built-in filter (wrr for example uses the mac-address or ip-address to put the traffic in classes so each address has it's own class). But normally you will have to attach a filter to a qdisc to put the traffic in classes.


Each class and qdisc has a unique number: "<major number:minor number>". For a qdisc the minor number is zero, or you give no number. The major number of a class is the same as the major number of the qdisc the class belongs to. So, pairs x:y are class handles and x:0 or x: are qdisc handles.

Each major number can be a number between 1 and 0x7fff. Major number 0x8000 - 0xffff is used for qdiscs with an unspecified major number. Numbers from ffff:fff0 to ffff:ffff are reserved or have a special meaning.

The major numbers are unique for each qdisc. The minor numbers are unique per major number. You can use hexadecimal numbers.

From include/linux/pkt_sched.h:

/* "Handles"
    All the traffic control objects have 32bit identifiers, or "handles".
    They can be considered as opaque numbers from user API viewpoint,
    but actually they always consist of two fields: major and
    minor numbers, which are interpreted by kernel specially,
    that may be used by applications, though not recommended.
    F.e. qdisc handles always have minor number equal to zero,
    classes (or flows) have major equal to parent qdisc major, and
    minor uniquely identifying class inside qdisc.