tc-cbq builds a small Class Based Queueing hierarchy you can inspect, test and tear down cleanly. CBQ is an older classful discipline: it shapes classes and shares spare capacity, but its timing isn't exact at short timescales.
Allow about fifteen minutes, plus time to confirm the interface's real capacity. You'll need a root shell or sudo, the tc command from iproute2 6.1.0, installed here as package version 6.1.0-1ubuntu6.4, and an interface you're allowed to reconfigure. This changes live packet scheduling: don't run the add commands on a production interface without a maintenance window and a recovery path.
Pick an interface and write down the physical or parent link rate before doing anything else:
$ IFACE='eth0'
$ tc qdisc show dev "$IFACE"
qdisc ...
eth0 with ip link or your normal network inventory before you swap in a real interface name.bandwidth describes the underlying interface or parent, not the rate you want a child class to get.Checkpoint: Stop if the interface is a bridge, tunnel, PPP link or anything else whose real bottleneck isn't obvious. CBQ tolerates large bandwidth errors at the cost of coarser shaping, per the manpage, but that's not licence to guess: measure it or document the assumption first.
This needs elevated privileges. The root qdisc controls the whole interface, so adding it can alter scheduling immediately, save the step 1 output first in case you need to restore a non-default qdisc:
$ sudo tc qdisc add dev "$IFACE" root handle 1: cbq bandwidth 100Mbit avpkt 1000
$ sudo tc -s qdisc show dev "$IFACE"
qdisc cbq 1: root ...
bandwidth is mandatory here because CBQ's idle-time calculations need the underlying rate, and avpkt is mandatory too, supplying the average packet size those calculations use. The handle 1: gives the qdisc a major number its child classes can share.
If the add command says a qdisc already exists, don't cycle through variants hoping one sticks. Inspect what's there first, replacing it can disrupt traffic and discard a carefully chosen policy.
Create a child class with a 10 Mbit/s ceiling under the 100 Mbit/s root:
$ sudo tc class add dev "$IFACE" parent 1: classid 1:10 cbq \
allot 1514 avpkt 1000 bandwidth 100Mbit rate 10Mbit prio 5
$ sudo tc -s class show dev "$IFACE"
class cbq 1:10 ... rate 10Mbit ...
rate is the maximum for this class and its children.prio chooses precedence: lower numbers get served first.allot is how much a class can dequeue in one round. The manpage says it's silently raised to at least one and a half times avpkt when needed.Creating a class doesn't prove your traffic will land in it. Classification is a separate part of the CBQ tree, handled by filters or by CBQ's priority and defmap mechanisms described in tc-cbq(8). If nothing sends a packet to a child, CBQ just enqueues it at the node it reached. Check the counters while testing rather than assuming a configured rate is actually being used.
For a first test, leave maxburst and minburst alone unless you have a measured reason to tune them. If you set either, the class also needs a bandwidth value, as above.
maxburst sets how many average packets CBQ can send when it's accumulated idle time. Higher tolerates bigger bursts.minburst changes how CBQ sleeps and releases packets when overlimit. Larger values improve long-term shaping but make millisecond-scale bursts bigger.Tweaking both because a short-term graph looks uneven is a classic distraction, it can make the short-term behaviour worse, not better. CBQ uses an exponentially weighted moving average, so its timing is sensitive to recent traffic, and the manpage warns that ordinary kernel timing can make shaping imprecise over a few seconds even when the long-term rate is close. Treat the result as a measured policy, not a hard real-time guarantee.
Check the structure and stats while the relevant traffic is actually running:
$ sudo tc -s qdisc show dev "$IFACE"
$ sudo tc -s class show dev "$IFACE"
class cbq 1:10 ... Sent ... bytes ...
Counters and detail vary by kernel and traffic. What you're checking for: the CBQ qdisc and class exist, and the class counters move when traffic has actually been classified there. If they sit at zero, chase the classification path before you touch rates.
Warning: Deleting the root qdisc is a live network change. It removes the CBQ hierarchy, the child class with it, and returns the interface to the kernel's default queueing. Only do this when you're ready for the transition:
$ sudo tc qdisc del dev "$IFACE" root
$ tc qdisc show dev "$IFACE"
qdisc noqueue 0: root ...
Recovery: That last line is only an example of what a simple idle interface might show, check your own output. If the interface had a previous non-default qdisc, restore that policy from your change record rather than guessing. tc doesn't provide an automatic undo for an earlier configuration.
tc -s qdisc show displays the expected CBQ root and handle.