QoS Vocabulary
QoS Vocabulary
Quality of Service is a pile of vendor features if you start from menus. Start from vocabulary and intent: classify → mark → queue → schedule → (maybe) shape/police. This chapter stays conceptual with small Linux tc experiments in Containerlab—no paid QoS licenses (mid-2026).
Learning goals
By the end of this chapter you can:
- Explain classification, marking, queuing, scheduling, policing, shaping
- Map DSCP/PHB ideas without memorizing every codepoint table
- Build a minimal Linux htb/fq_codel lab and measure fairness under load
- Predict what happens when you police vs shape
- Know what not to promise with lab QoS
Concepts — the pipeline
packets in
│
▼
Classify (match: 5-tuple, interface, DSCP, VLAN, app)
│
▼
Mark (set DSCP / internal class / MPLS TC / VLAN PCP)
│
▼
Police (drop/remark excess — hard limit)
│
▼
Queue (buffers per class)
│
▼
Schedule (which queue sends next: PQ, WRR, WFQ-like…)
│
▼
Shape (smooth egress to rate — buffer then send)
│
▼
wire
Not every hop does every stage. Trust boundaries matter: remark at edges, honor marks in the core only if you trust them.
Vocabulary table
| Term | Meaning |
|---|---|
| Classification | Deciding which class a packet belongs to |
| Marking | Writing a field others can see (e.g. DSCP) |
| PHB | Per-hop behavior—what a class should experience |
| Policing | Enforce rate; excess drop or remark (little buffering) |
| Shaping | Enforce rate by delaying (buffering) excess |
| Queue depth | How much can wait; latency vs drop trade-off |
| Congestion avoidance | ECN, RED/WRED-like early drop/mark |
| Scheduling | Priority vs sharing bandwidth among classes |
| LLQ / priority queue | Low-latency class that can starve others if unbounded |
| Fair queueing | Prevent one flow from dominating (fq / fq_codel ideas) |
| Trust boundary | Where you accept or rewrite marks |
DSCP / PHB (operator view)
| Intent class | Typical idea | Notes |
|---|---|---|
| Network control | Highest care for protocols | Protect, don’t just “EF everything” |
| Expedited (EF-like) | Low latency, low loss | Strict admission; small volume |
| Assured / AF-like | Business data tiers | Drop precedence inside class |
| Default / BE | Best effort | Most traffic |
| Scavenger | Below BE | Backups, bulk |
Exact codepoints are standards-listed; do not treat exam bingo as design. Document your map:
EF-like → voice lab class (tiny)
AF31-like → interactive
CS6-like → routing (if marked)
BE → default
Policing vs shaping
| Police | Shape | |
|---|---|---|
| Excess | Drop/remark | Delay in buffer |
| Latency | Can be lower for in-profile | Adds delay when congested |
| Use | Enforce contract, ingress | Match egress to circuit rate |
Predict: shaping a 100 mbit stream to 10 mbit increases latency under load; policing 10 mbit drops packets and breaks TCP more brutally.
Linux lab topology
name: qos-vocab
topology:
nodes:
r1:
kind: linux
image: alpine:3.20
exec:
- apk add --no-cache iproute2 iputils iperf3
h1:
kind: linux
image: alpine:3.20
exec:
- apk add --no-cache iproute2 iputils iperf3
h2:
kind: linux
image: alpine:3.20
exec:
- apk add --no-cache iproute2 iputils iperf3
links:
- endpoints: ["h1:eth1", "r1:eth1"]
- endpoints: ["r1:eth2", "h2:eth1"]Addressing
| Node | IP |
|---|---|
| h1 | 10.80.1.10/24 via 10.80.1.1 |
| r1 eth1 | 10.80.1.1/24 |
| r1 eth2 | 10.80.2.1/24 |
| h2 | 10.80.2.10/24 via 10.80.2.1 |
docker exec clab-qos-vocab-r1 sysctl -w net.ipv4.ip_forward=1
# apply addresses on all nodes...Experiment 1 — bottleneck without QoS
# limit r1 eth2 with a simple tbf shape to create congestion
docker exec clab-qos-vocab-r1 tc qdisc replace dev eth2 root tbf rate 5mbit burst 32kbit latency 50ms
docker exec -d clab-qos-vocab-h2 iperf3 -s
docker exec clab-qos-vocab-h1 iperf3 -c 10.80.2.10 -t 20 -P 4Predict
Aggregate ~5 Mbit; latency rises; flows share roughly (TCP dynamics).
Observe
docker exec clab-qos-vocab-h1 iperf3 -c 10.80.2.10 -t 10 -J | head
docker exec clab-qos-vocab-r1 tc -s qdisc show dev eth2Experiment 2 — two classes with HTB
Goal: bulk class 3 mbit ceiling; interactive class 2 mbit guaranteed-ish; total 5 mbit.
docker exec clab-qos-vocab-r1 sh -c '
tc qdisc del dev eth2 root 2>/dev/null || true
tc qdisc add dev eth2 root handle 1: htb default 20
tc class add dev eth2 parent 1: classid 1:1 htb rate 5mbit ceil 5mbit
tc class add dev eth2 parent 1:1 classid 1:10 htb rate 2mbit ceil 5mbit prio 1
tc class add dev eth2 parent 1:1 classid 1:20 htb rate 3mbit ceil 5mbit prio 2
tc qdisc add dev eth2 parent 1:10 handle 10: fq_codel
tc qdisc add dev eth2 parent 1:20 handle 20: fq_codel
# classify by destination port: "interactive" = 5202, bulk = 5201
tc filter add dev eth2 protocol ip parent 1:0 prio 1 u32 match ip dport 5202 0xffff flowid 1:10
tc filter add dev eth2 protocol ip parent 1:0 prio 2 u32 match ip dport 5201 0xffff flowid 1:20
'Run two iperf servers/ports on h2:
docker exec -d clab-qos-vocab-h2 iperf3 -s -p 5201
docker exec -d clab-qos-vocab-h2 iperf3 -s -p 5202
docker exec -d clab-qos-vocab-h1 iperf3 -c 10.80.2.10 -p 5201 -t 30 -P 2
docker exec clab-qos-vocab-h1 iperf3 -c 10.80.2.10 -p 5202 -t 20Predict
Interactive class gets better latency / share under contention than pure bulk; total still capped ~5 mbit.
Observe
docker exec clab-qos-vocab-r1 tc -s class show dev eth2
docker exec clab-qos-vocab-r1 tc -s qdisc show dev eth2Fix
Wrong filter → all traffic in default class. Check flowid and dport match (u32 host byte care).
Experiment 3 — police vs shape feel
# police ingress-ish on r1 eth1 (example)
docker exec clab-qos-vocab-r1 sh -c '
tc qdisc replace dev eth1 handle ffff: ingress
tc filter add dev eth1 parent ffff: protocol ip u32 match u32 0 0 \
police rate 2mbit burst 20k drop flowid :1
'Compare TCP goodput and retransmissions vs tbf shape on egress.
Predict: police drops → TCP sawtooth pain; shape delays → higher RTT, smoother rate.
Marking sketch (DSCP)
# set DSCP on packets from h1 (example iptables)
docker exec clab-qos-vocab-h1 sh -c '
apk add --no-cache iptables
iptables -t mangle -A OUTPUT -p tcp --dport 5202 -j DSCP --set-dscp 0x2e
'
# on r1 classify on DSCP instead of dport (tc flower/u32 match)Predict
If r1 trusts and classifies EF-like mark into priority class, marked flows win under load.
Observe
docker exec clab-qos-vocab-r1 sh -c 'apk add --no-cache tcpdump; tcpdump -ni eth1 -v -c 5 tcp'What lab QoS cannot prove
- Hardware queue behavior of a specific ASIC
- Carrier hierarchical QoS at scale
- Exact voice MOS scores
- Multi-vendor PHB interoperability without careful design
It can prove: classification logic, bottlenecks, unfairness, and the difference between police/shape.
FRR / NOS note
FRR is not your main QoS engine. Real NOS (including SR Linux community) expose class-maps and queues in their models. Translate:
class VOICE → match DSCP EF → queue LLQ-like → police admit
class BULK → match default → shaped remainder
Same vocabulary.
Design mini-checklist
- Trust boundary defined
- Classes ≤ 4–5 for first design
- Admission for low-latency class
- Capacity math: sum guarantees ≤ link
- Congestion metrics (drop, latency, ECN) collected
- Failure mode: QoS mis-class worse than no QoS?
Predict → observe → fix template
| Step | Example |
|---|---|
| Predict | Bulk should not starve interactive under 5 mbit cap |
| Observe | iperf + tc -s + ping RTT during load |
| Fix | filters, class rates, default class, ECN/fq_codel |
Common mistakes
| Mistake | Symptom |
|---|---|
| Mark without classifying later | Marks are cosmetics |
| Guarantees sum > link | Unexpected drops |
| Priority unbounded | Starvation of BE |
| QoS to “fix” bandwidth shortage | Still need capacity |
| Policing management/routing | Self-outage (see CoPP) |
Summary
- QoS is a pipeline of intents, not a single CLI feature
- Classify → mark → queue → schedule → police/shape
- Linux
tc+ iperf3 in Containerlab is enough to feel congestion policy
- Document trust boundaries and class maps in git
- Keep routing/management classes out of scavenger police
Next: hardening checklists—turn models into repeatable gates for labs and edges.