LFCE 87 ๐ง QoS โ Priority and Queuing
Quality of Service on Linux is the practice of classifying traffic into different priority levels and queuing each level according to its latency and bandwidth requirements. The previous chapter covered shaping and policing, which enforce a rate on a single stream or interface. This chapter covers the queuing disciplines and classifiers that divide the interface’s bandwidth among multiple traffic classes, giving latency-sensitive trafficโVoIP, SSH, DNSโa higher priority than bulk transfers. The LFCE exam expects the candidate to monitor and tune network performance, and QoS is the toolset that makes priority-based tuning possible .
The priority and queuing mechanisms are the prio qdisc for simple priority ordering and the htb qdisc for hierarchical bandwidth guarantees with priority inheritance. The prio qdisc is a scheduler that never delays packets; it dequeues the higher-priority bands first and only serves the lower bands when the higher ones are empty . The htb qdisc is a shaper that guarantees a minimum rate to each class and allows borrowing up to a ceiling, and its prio parameter determines which class is served first when multiple classes have packets to send . The filtersโu32, flower, and fwโclassify the packets into the classes.
Key point: The prio qdisc has a fixed number of bands created at qdisc creation time, and band 0 is tried first, band 1 second, and so on. It is a work-conserving scheduler, which means it does not shape or delay traffic; it only orders the dequeue. The htb qdisc has a prio parameter on each class, and the classes with the lowest priority number are tried first in the round-robin process. The htb qdisc also has a default class that receives the unclassified packets, and the filters that classify the packets must be attached to the root qdisc, not to the child classes .
Why QoS requires explicit classification and queuing
The unclassified-default problem.
Every packet that enters a classful qdisc must be classified into one of its classes. If no filter matches, the packet goes to the qdisc’s default class. For htb, the default parameter is mandatory in the sense that the unclassified traffic must have a destination, and the default class is usually the bulk class with the lowest guarantee . The filters that classify the traffic are attached to the root qdisc, and a common mistake is to attach them to a child class, where they never match because the packet is enqueued at the root and dequeued from the leaf.
The priority-inheritance problem.
The htb qdisc’s prio parameter is the priority within the parent class. When the parent has bandwidth to distribute, it tries the classes with the lowest priority number first. The prio qdisc, by contrast, has bands that are always dequeued in order, and the band 0 always wins when it has a packet . The htb priority is a preference within the borrowing process, and the prio band is a strict order. The choice depends on whether the goal is to guarantee bandwidth with a preference (htb) or to strictly order the traffic by latency (prio).
The leaf-qdisc problem.
Each htb class has a leaf qdisc attached to it, and the default leaf is pfifo. The leaf qdisc determines how the packets within the class are queued. A class that carries latency-sensitive traffic should have a fq_codel or pfifo leaf, and a class that carries bulk traffic should have a fq_codel leaf for fairness . The leaf qdisc is the last stage before the packet is transmitted, and it determines the queueing behavior within the class.
a. The prio qdisc for strict priority
The prio qdisc is the simplest priority mechanism. It creates a fixed number of bands, and the bands are dequeued in order.
# Create a prio qdisc with three bands
sudo tc qdisc add dev eth0 root handle 1: prio bands 3
# The bands are 1:1, 1:2, and 1:3
# Band 1:1 is the highest priority
The bands 3 parameter creates three classes: 1:1, 1:2, and 1:3. The band 1:1 is tried first, then 1:2, then 1:3 . The priomap parameter maps the packet’s TOS field to a band, and the default priomap sends the normal traffic to band 1 (the middle band) and the minimize-delay traffic to band 2 (the lowest priority, which is counterintuitive). The tc filter command overrides the priomap for the specific traffic.
# Send SSH traffic to band 1:1 (highest priority)
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 \
match ip dport 22 0xffff flowid 1:1
# Send DNS traffic to band 1:1
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 \
match ip dport 53 0xffff flowid 1:1
# Send HTTP traffic to band 1:2 (normal priority)
sudo tc filter add dev eth0 parent 1: protocol ip prio 2 u32 \
match ip dport 80 0xffff flowid 1:2
The flowid 1:1 sends the matching packets to the highest-priority band. The prio 1 on the filter determines the filter evaluation order, not the band priority . The unmatched packets go to the band that the priomap assigns, which is typically band 1 for the normal traffic.
The prio qdisc is a scheduler, not a shaper. It does not limit the rate of any band, and it does not delay the packets unless the lower-priority bands have to wait for the higher-priority bands to empty. It is the correct choice when the goal is to reduce latency for the critical traffic without guaranteeing a specific bandwidth.
b. The htb qdisc for guaranteed bandwidth and priority
The htb qdisc is the hierarchical shaper that guarantees a minimum rate to each class and allows borrowing. The prio parameter on each class determines the order in which the classes are served when the parent has bandwidth to distribute.
# Create the HTB root qdisc with a default class
sudo tc qdisc add dev eth0 root handle 1: htb default 30
# Root class: 100 Mbps total
sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit
# Critical class: 40 Mbps guaranteed, priority 1
sudo tc class add dev eth0 parent 1:1 classid 1:10 htb \
rate 40mbit ceil 100mbit prio 1
# Interactive class: 40 Mbps guaranteed, priority 2
sudo tc class add dev eth0 parent 1:1 classid 1:20 htb \
rate 40mbit ceil 100mbit prio 2
# Bulk class: 20 Mbps guaranteed, priority 3
sudo tc class add dev eth0 parent 1:1 classid 1:30 htb \
rate 20mbit ceil 100mbit prio 3
The rate is the guaranteed minimum, and the ceil is the maximum that the class can send when the parent has spare bandwidth . The prio 1 on the critical class means it is tried first in the round-robin process. The prio 2 on the interactive class means it is tried second, and the prio 3 on the bulk class means it is tried last. The default 30 sends the unclassified packets to the bulk class.
The leaf qdiscs are attached to each class. The fq_codel leaf is the recommended choice for the interactive and bulk classes because it provides fair queueing and low latency within the class.
# Add fq_codel leaf qdiscs to each class
sudo tc qdisc add dev eth0 parent 1:10 handle 10: fq_codel
sudo tc qdisc add dev eth0 parent 1:20 handle 20: fq_codel
sudo tc qdisc add dev eth0 parent 1:30 handle 30: fq_codel
The parent 1:10 attaches the leaf qdisc to the critical class. The handle 10: is the leaf qdisc’s handle, which is used to reference it in the statistics . The leaf qdisc is the last stage before the packet is transmitted, and it determines the queueing behavior within the class.
c. The filters for classification
The filters classify the packets into the classes. The u32 filter is the most common for port-based classification, and the flower filter is the newer alternative with a simpler syntax for the common matches.
# SSH traffic to the critical class
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 u32 \
match ip dport 22 0xffff flowid 1:10
# DNS traffic to the critical class
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 u32 \
match ip dport 53 0xffff flowid 1:10
# HTTP and HTTPS traffic to the interactive class
sudo tc filter add dev eth0 protocol ip parent 1: prio 2 u32 \
match ip dport 80 0xffff flowid 1:20
sudo tc filter add dev eth0 protocol ip parent 1: prio 2 u32 \
match ip dport 443 0xffff flowid 1:20
The parent 1: attaches the filter to the root qdisc. The prio 1 on the filter is the filter’s evaluation order, and the filters with the lower prio number are evaluated first . The flowid 1:10 sends the matching packets to the critical class. The unmatched packets go to the default class, which is the bulk class.
The flower filter is the alternative with a simpler syntax for the common matches.
# SSH traffic to the critical class with flower
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 flower \
ip_proto tcp dst_port 22 classid 1:10
The ip_proto tcp dst_port 22 matches the TCP destination port 22, and the classid 1:10 sends the matching packets to the critical class . The flower filter is less flexible than u32 for the arbitrary packet offsets, but it is simpler for the port and address matches that are the most common in QoS configurations.
The fw filter matches the firewall mark that iptables or nftables has set on the packet. It is the mechanism that connects the firewall’s classification to the traffic control’s scheduling.
# Mark the RTP audio traffic with iptables
sudo iptables -t mangle -A POSTROUTING -o eth0 -p udp --dport 10000:20000 -j MARK --set-mark 10
# Classify the marked traffic with tc
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 handle 10 fw classid 1:10
The --set-mark 10 sets the firewall mark on the matching packets, and the handle 10 fw matches the packets with that mark . The classid 1:10 sends them to the critical class. The fw filter is the correct choice when the classification depends on criteria that the u32 or flower filters cannot express, such as the connection state or the application-layer protocol.
Complete Example Session
# ============================================
# PART 1: CREATE THE HTB ROOT QDISC
# ============================================
sudo tc qdisc add dev eth0 root handle 1: htb default 30
# ============================================
# PART 2: CREATE THE ROOT CLASS
# ============================================
sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit
# ============================================
# PART 3: CREATE THE PRIORITY CLASSES
# ============================================
sudo tc class add dev eth0 parent 1:1 classid 1:10 htb rate 40mbit ceil 100mbit prio 1
sudo tc class add dev eth0 parent 1:1 classid 1:20 htb rate 40mbit ceil 100mbit prio 2
sudo tc class add dev eth0 parent 1:1 classid 1:30 htb rate 20mbit ceil 100mbit prio 3
# ============================================
# PART 4: ADD THE LEAF QDISCS
# ============================================
sudo tc qdisc add dev eth0 parent 1:10 handle 10: fq_codel
sudo tc qdisc add dev eth0 parent 1:20 handle 20: fq_codel
sudo tc qdisc add dev eth0 parent 1:30 handle 30: fq_codel
# ============================================
# PART 5: ADD THE FILTERS
# ============================================
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dport 22 0xffff flowid 1:10
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dport 53 0xffff flowid 1:10
sudo tc filter add dev eth0 protocol ip parent 1: prio 2 u32 match ip dport 80 0xffff flowid 1:20
sudo tc filter add dev eth0 protocol ip parent 1: prio 2 u32 match ip dport 443 0xffff flowid 1:20
# ============================================
# PART 6: VERIFY THE CONFIGURATION
# ============================================
tc qdisc show dev eth0
tc class show dev eth0
tc filter show dev eth0
# ============================================
# PART 7: CHECK THE STATISTICS
# ============================================
tc -s class show dev eth0
# Output shows the packets and bytes per class
The seven parts demonstrate the QoS configuration: the HTB root qdisc, the root class, the priority classes, the leaf qdiscs, the filters, the verification, and the statistics. The critical distinction is between Parts 3 and 5: the classes define the bandwidth guarantees and the priority, and the filters route the traffic into the classes.
Quick Reference
Qdisc Comparison
| Qdisc | Type | Priority Mechanism | Bandwidth Guarantee |
|---|---|---|---|
prio | Classful | Fixed bands, dequeued in order | None |
htb | Classful | prio parameter on each class | rate and ceil |
fq_codel | Classless | Fair queueing | None |
htb Class Parameters
| Parameter | Purpose | Example |
|---|---|---|
rate | Guaranteed minimum | rate 40mbit |
ceil | Maximum with borrowing | ceil 100mbit |
prio | Priority within parent | prio 1 |
burst | Burst at ceil | burst 15k |
Filter Types
| Filter | Use Case | Example |
|---|---|---|
u32 | Arbitrary packet fields | match ip dport 22 0xffff |
flower | Simple port/address matches | ip_proto tcp dst_port 22 |
fw | Firewall mark | handle 10 fw |
Verification Commands
| Command | Purpose |
|---|---|
tc qdisc show dev eth0 | Show the qdisc |
tc class show dev eth0 | Show the classes |
tc filter show dev eth0 | Show the filters |
tc -s class show dev eth0 | Show the class statistics |
Best Practices
โ Do This:
# Use HTB for guaranteed bandwidth and priority
tc qdisc add dev eth0 root handle 1: htb default 30 # โ
# Set the rate and ceil per class
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 40mbit ceil 100mbit prio 1 # โ
# Attach fq_codel to each class
tc qdisc add dev eth0 parent 1:10 handle 10: fq_codel # โ
# Attach filters to the root qdisc
tc filter add dev eth0 parent 1: u32 match ip dport 22 0xffff flowid 1:10 # โ
# Use the fw filter for connection-state-based classification
tc filter add dev eth0 parent 1: handle 10 fw classid 1:10 # โ
# Check the statistics
tc -s class show dev eth0 # โ
โ Don’t Do This:
# Attach filters to a child class
tc filter add dev eth0 parent 1:10 ... # Never matches # โ
# Forget the default class on HTB
tc qdisc add dev eth0 root handle 1: htb # Unclassified traffic # โ
# Use prio for bandwidth guarantees
tc qdisc add dev eth0 root handle 1: prio # No rate control # โ
# Set rate equal to ceil for every class
# The classes cannot borrow; the sum must fit the parent # โ
# Forget the leaf qdiscs
# The default pfifo is used, which may not be optimal # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Class receives no traffic | Filters not attached to the root | Attach to parent 1: |
| Unclassified traffic dropped | No default class | Add default parameter |
| Priority not respected | prio parameter missing or wrong | Set prio 1 on the high-priority class |
| Bandwidth exceeded | Sum of rates exceeds parent | Keep the sum below the parent rate |
| Leaf qdisc not used | No leaf qdisc attached | Attach fq_codel to each class |
Real-World Examples
1. HTB root with default
tc qdisc add dev eth0 root handle 1: htb default 30
2. Root class
tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit
3. High-priority class
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 40mbit ceil 100mbit prio 1
4. Low-priority class
tc class add dev eth0 parent 1:1 classid 1:30 htb rate 20mbit ceil 100mbit prio 3
5. Leaf qdisc
tc qdisc add dev eth0 parent 1:10 handle 10: fq_codel
6. SSH filter
tc filter add dev eth0 parent 1: prio 1 u32 match ip dport 22 0xffff flowid 1:10
7. HTTP filter
tc filter add dev eth0 parent 1: prio 2 u32 match ip dport 80 0xffff flowid 1:20
8. Flower filter
tc filter add dev eth0 parent 1: prio 1 flower ip_proto tcp dst_port 22 classid 1:10
9. Firewall mark filter
tc filter add dev eth0 parent 1: handle 10 fw classid 1:10
10. Class statistics
tc -s class show dev eth0
Visual
Priority Queuing with prio
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PRIO DEQUEUES BAND 0 FIRST, THEN BAND 1, THEN BAND 2 โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Band 1:1 (highest priority) โ โ
โ โ โ dequeued first when it has packets โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Band 1:2 (normal priority) โ โ
โ โ โ dequeued when band 1:1 is empty โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Band 1:3 (lowest priority) โ โ
โ โ โ dequeued when bands 1:1 and 1:2 are empty โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ The prio qdisc is work-conserving; it does not shape. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
HTB Hierarchy with Priority
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HTB GUARANTEES RATE AND HONORS PRIORITY โ
โ โ
โ root 1: (htb, default 30) โ
โ โโโ class 1:1 (rate 100mbit) โ
โ โโโ class 1:10 (rate 40mbit, ceil 100mbit, prio 1) โ
โ โ โโโ leaf fq_codel โ
โ โโโ class 1:20 (rate 40mbit, ceil 100mbit, prio 2) โ
โ โ โโโ leaf fq_codel โ
โ โโโ class 1:30 (rate 20mbit, ceil 100mbit, prio 3) โ
โ โโโ leaf fq_codel โ
โ โ
โ When the parent has bandwidth to distribute, โ
โ the class with the lowest prio is tried first. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Filter Attachment
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FILTERS ATTACH TO THE ROOT QDISC, NOT THE CHILD CLASSES โ
โ โ
โ โ
Correct: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ tc filter add dev eth0 parent 1: u32 ... flowid 1:10 โ โ
โ โ โ the filter is evaluated at the root โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โ Wrong: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ tc filter add dev eth0 parent 1:10 u32 ... flowid 1:10 โ โ
โ โ โ the packet is already enqueued at the root โ โ
โ โ โ the filter is never evaluated โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Strict priority qdisc | prio |
| Bandwidth guarantee qdisc | htb |
| HTB priority parameter | prio on each class |
| HTB rate parameter | rate (guaranteed minimum) |
| HTB ceiling parameter | ceil (maximum with borrowing) |
| HTB default class | default parameter on the root qdisc |
| Leaf qdisc | fq_codel for fairness |
| Filter types | u32, flower, fw |
| Filter attachment | Root qdisc (parent 1:) |
| Verification | tc class show, tc filter show |
Key takeaways:
- The
prioqdisc is a strict priority scheduler. It dequeues band 0 first, band 1 second, and so on. It does not shape or delay traffic; it only orders the dequeue. It is the correct choice when the goal is to reduce latency for the critical traffic without guaranteeing a bandwidth . - The
htbqdisc guarantees bandwidth and honors priority. Therateis the guaranteed minimum, theceilis the maximum with borrowing, and theprioparameter determines which class is served first when the parent has bandwidth to distribute . - The filters must be attached to the root qdisc. A filter attached to a child class is never evaluated, because the packet is enqueued at the root and dequeued from the leaf. The
parent 1:form is the correct attachment point for an HTB hierarchy . - The
defaultclass receives the unclassified traffic. Thedefault 30parameter on the HTB root qdisc sends the packets that match no filter to the bulk class. Without it, the unclassified traffic is enqueued at the root and may be dropped . - The leaf qdisc determines the queueing within the class. The
fq_codelleaf provides fair queueing and low latency, and it is the recommended choice for the interactive and bulk classes . - The
fwfilter connects the firewall mark to the traffic control class. The iptables or nftables rule sets the mark, and thetc filter ... handle <mark> fw classid <class>matches it. This is the mechanism for the classification that depends on criteria the packet filters cannot express .
Remember: QoS is the combination of a qdisc that defines the queuing behavior and filters that classify the traffic into the qdisc’s classes. The prio qdisc provides strict priority ordering, and the htb qdisc provides guaranteed bandwidth with priority. The filters attach to the root qdisc, the default class catches the unclassified traffic, and the leaf qdiscs determine the queueing within each class. The result is a network that serves the latency-sensitive traffic first and gives the bulk traffic the remaining bandwidth.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!