QoS Vocabulary

Updated

September 4, 2026

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 4

Predict

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 eth2

Experiment 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 20

Predict

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 eth2

Fix

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.