HTB ISP Tiered Bandwidth
HTB ISP Tiered Bandwidth
Section titled “HTB ISP Tiered Bandwidth”This guide shows how to build a RouterOS HTB queue tree for an ISP that offers tiered service plans — for example Bronze, Silver, and Gold — on a shared uplink. The design gives each tier a guaranteed aggregate rate, a burst allowance, and a hard ceiling. Within each tier, PCQ distributes bandwidth fairly across all active customers. VoIP traffic receives priority over data in every tier.
Prerequisites: Familiarity with Queue Tree fundamentals (HTB,
limit-at,max-limit, priority) and PCQ classifier behaviour.
Design Overview
Section titled “Design Overview”The queue tree has three levels:
QT-DOWN (root — total downlink, e.g. 1 Gbps)├── DOWN-BRONZE limit-at=100M max-limit=300M priority=6│ ├── DOWN-BRONZE-VOIP priority=1 (per-customer PCQ)│ └── DOWN-BRONZE-DATA priority=6 (per-customer PCQ)├── DOWN-SILVER limit-at=200M max-limit=500M priority=4│ ├── DOWN-SILVER-VOIP priority=1│ └── DOWN-SILVER-DATA priority=4└── DOWN-GOLD limit-at=300M max-limit=800M priority=2 ├── DOWN-GOLD-VOIP priority=1 └── DOWN-GOLD-DATA priority=2
QT-UP (root — total uplink)└── (mirror structure for upload)How the tiers interact:
- When the 1 Gbps uplink is idle, Gold customers can use up to 800 Mbps.
- As contention grows, each tier is guaranteed its
limit-atbefore any borrowing occurs: Bronze 100 Mbps, Silver 200 Mbps, Gold 300 Mbps. - Within each tier, VoIP packets (priority 1) are served before data
(priority 4–6) whenever the tier’s
max-limitis reached. - PCQ at the leaf level divides each class fairly among active customers — no single customer can starve others in the same tier.
Step 1: PCQ Queue Types
Section titled “Step 1: PCQ Queue Types”Create PCQ types for each tier. Set pcq-rate to the per-customer ceiling; set
it to 0 for fully dynamic sharing.
/queue type# Per-customer download caps by tieradd name=pcq-down-bronze kind=pcq pcq-classifier=dst-address pcq-rate=10Madd name=pcq-down-silver kind=pcq pcq-classifier=dst-address pcq-rate=25Madd name=pcq-down-gold kind=pcq pcq-classifier=dst-address pcq-rate=50M
# Per-customer upload caps by tieradd name=pcq-up-bronze kind=pcq pcq-classifier=src-address pcq-rate=2Madd name=pcq-up-silver kind=pcq pcq-classifier=src-address pcq-rate=5Madd name=pcq-up-gold kind=pcq pcq-classifier=src-address pcq-rate=10M
# VoIP uses dynamic sharing (no hard per-customer VoIP cap)add name=pcq-down-voip kind=pcq pcq-classifier=dst-address pcq-rate=0add name=pcq-up-voip kind=pcq pcq-classifier=src-address pcq-rate=0Tip:
pcq-rate=0means all active substreams share the parent’s capacity equally. Use a non-zero value when you want a hard per-customer ceiling regardless of how much spare capacity exists.
Step 2: Address Lists for Customer Tiers
Section titled “Step 2: Address Lists for Customer Tiers”Maintain address lists to classify customers into tiers. These are the only place you need to change when a customer upgrades or downgrades.
/ip firewall address-listadd address=203.0.113.10 list=bronze-customersadd address=203.0.113.11 list=bronze-customersadd address=203.0.113.20 list=silver-customersadd address=203.0.113.30 list=gold-customersStep 3: Packet Marks in Mangle
Section titled “Step 3: Packet Marks in Mangle”Mark packets by tier and by traffic class (VoIP vs data). Order matters: place
VoIP rules above tier rules so VoIP packets receive both marks via
passthrough=yes.
/ip firewall mangle
# --- VoIP identification (DSCP EF = 46, covers SIP and RTP) ---add chain=forward dscp=46 action=mark-packet \ new-packet-mark=pm-voip-down passthrough=yesadd chain=forward dscp=46 action=mark-packet \ new-packet-mark=pm-voip-up passthrough=yes
# --- Download tier marks (traffic arriving at customer addresses) ---add chain=forward dst-address-list=bronze-customers \ action=mark-packet new-packet-mark=pm-bronze-down passthrough=yesadd chain=forward dst-address-list=silver-customers \ action=mark-packet new-packet-mark=pm-silver-down passthrough=yesadd chain=forward dst-address-list=gold-customers \ action=mark-packet new-packet-mark=pm-gold-down passthrough=yes
# --- Upload tier marks (traffic leaving customer addresses) ---add chain=forward src-address-list=bronze-customers \ action=mark-packet new-packet-mark=pm-bronze-up passthrough=yesadd chain=forward src-address-list=silver-customers \ action=mark-packet new-packet-mark=pm-silver-up passthrough=yesadd chain=forward src-address-list=gold-customers \ action=mark-packet new-packet-mark=pm-gold-up passthrough=yesImportant: Disable FastTrack for customer traffic, or FastTracked packets will bypass mangle and enter no queue:
/ip firewall filteradd chain=forward action=accept src-address-list=bronze-customers \comment="allow but do not fasttrack — managed by QoS"Alternatively, exclude customer subnets from the FastTrack connection mark.
Step 4: Queue Tree — Roots
Section titled “Step 4: Queue Tree — Roots”Attach roots to global (postrouting) for upload and global-in (prerouting)
for download. Adjust max-limit to match your actual uplink capacity.
/queue treeadd name=QT-DOWN parent=global-in max-limit=1Gadd name=QT-UP parent=global max-limit=1GStep 5: Tier Queues with Burst
Section titled “Step 5: Tier Queues with Burst”Each tier queue sits under the root and sets the aggregate policy for that plan.
| Tier | Guaranteed (down) | Ceiling (down) | Burst (down) | Priority |
|---|---|---|---|---|
| Bronze | 100 Mbps | 300 Mbps | 350 Mbps | 6 |
| Silver | 200 Mbps | 500 Mbps | 600 Mbps | 4 |
| Gold | 300 Mbps | 800 Mbps | 900 Mbps | 2 |
/queue tree# --- Download tiers ---add name=DOWN-BRONZE parent=QT-DOWN packet-mark=pm-bronze-down \ limit-at=100M max-limit=300M \ burst-limit=350M burst-threshold=250M burst-time=30s \ priority=6
add name=DOWN-SILVER parent=QT-DOWN packet-mark=pm-silver-down \ limit-at=200M max-limit=500M \ burst-limit=600M burst-threshold=420M burst-time=30s \ priority=4
add name=DOWN-GOLD parent=QT-DOWN packet-mark=pm-gold-down \ limit-at=300M max-limit=800M \ burst-limit=900M burst-threshold=700M burst-time=30s \ priority=2
# --- Upload tiers ---add name=UP-BRONZE parent=QT-UP packet-mark=pm-bronze-up \ limit-at=30M max-limit=100M \ burst-limit=120M burst-threshold=80M burst-time=30s \ priority=6
add name=UP-SILVER parent=QT-UP packet-mark=pm-silver-up \ limit-at=80M max-limit=250M \ burst-limit=300M burst-threshold=210M burst-time=30s \ priority=4
add name=UP-GOLD parent=QT-UP packet-mark=pm-gold-up \ limit-at=150M max-limit=400M \ burst-limit=480M burst-threshold=340M burst-time=30s \ priority=2How burst works in this design
Section titled “How burst works in this design”burst-time=30s is the averaging window. If a Bronze tier’s 30-second average
download rate is below burst-threshold=250M, all Bronze customers collectively
can burst to 350 Mbps. Once sustained traffic pushes the average above 250 Mbps,
the tier drops back to its max-limit=300M.
Set burst-threshold roughly at 80–85% of max-limit to allow short spikes
(page loads, connection setup) without letting bulk transfers hold burst for long.
Step 6: Service-Class Leaf Queues (VoIP Priority)
Section titled “Step 6: Service-Class Leaf Queues (VoIP Priority)”Add VoIP and data children under each tier. VoIP uses priority=1 to be served
first when the tier’s max-limit is reached.
/queue tree
# --- Bronze download children ---add name=DOWN-BRONZE-VOIP parent=DOWN-BRONZE packet-mark=pm-voip-down \ queue=pcq-down-voip priority=1 limit-at=20M max-limit=80M
add name=DOWN-BRONZE-DATA parent=DOWN-BRONZE packet-mark=pm-bronze-down \ queue=pcq-down-bronze priority=6 limit-at=80M max-limit=300M
# --- Silver download children ---add name=DOWN-SILVER-VOIP parent=DOWN-SILVER packet-mark=pm-voip-down \ queue=pcq-down-voip priority=1 limit-at=40M max-limit=150M
add name=DOWN-SILVER-DATA parent=DOWN-SILVER packet-mark=pm-silver-down \ queue=pcq-down-silver priority=4 limit-at=160M max-limit=500M
# --- Gold download children ---add name=DOWN-GOLD-VOIP parent=DOWN-GOLD packet-mark=pm-voip-down \ queue=pcq-down-voip priority=1 limit-at=80M max-limit=200M
add name=DOWN-GOLD-DATA parent=DOWN-GOLD packet-mark=pm-gold-down \ queue=pcq-down-silver priority=2 limit-at=220M max-limit=800M
# --- Bronze upload children ---add name=UP-BRONZE-VOIP parent=UP-BRONZE packet-mark=pm-voip-up \ queue=pcq-up-voip priority=1 limit-at=5M max-limit=20M
add name=UP-BRONZE-DATA parent=UP-BRONZE packet-mark=pm-bronze-up \ queue=pcq-up-bronze priority=6 limit-at=25M max-limit=100M
# --- Silver upload children ---add name=UP-SILVER-VOIP parent=UP-SILVER packet-mark=pm-voip-up \ queue=pcq-up-voip priority=1 limit-at=10M max-limit=50M
add name=UP-SILVER-DATA parent=UP-SILVER packet-mark=pm-silver-up \ queue=pcq-up-silver priority=4 limit-at=70M max-limit=250M
# --- Gold upload children ---add name=UP-GOLD-VOIP parent=UP-GOLD packet-mark=pm-voip-up \ queue=pcq-up-voip priority=1 limit-at=20M max-limit=80M
add name=UP-GOLD-DATA parent=UP-GOLD packet-mark=pm-gold-up \ queue=pcq-up-gold priority=2 limit-at=130M max-limit=400MWhy VoIP has its own
limit-at: When the tier is congested, HTB guarantees the VoIP child itslimit-atbefore serving data. Without a non-zerolimit-aton a high-priority queue, HTB may still delay it in some edge cases.
Monitoring
Section titled “Monitoring”Check all queues with live statistics:
/queue tree print statsWatch a specific tier in real time:
/queue tree print stats where name~"BRONZE"Key fields to watch:
| Field | What it tells you |
|---|---|
rate | Current throughput — compare to limit-at and max-limit |
dropped | Packets dropped because the queue is full — indicates sustained overload |
borrows | Packets forwarded above limit-at using spare parent capacity |
lends | Unused guaranteed capacity lent to siblings |
queued-packets | Non-zero means the tier is currently being shaped |
Reset counters to start a fresh measurement window:
/queue tree reset-counters-allTroubleshooting
Section titled “Troubleshooting”Tier queue shows 0 bytes/packets
Section titled “Tier queue shows 0 bytes/packets”The packet mark on the queue entry does not match any marked packets. Verify:
/ip firewall mangle print statsThe mangle rule byte/packet count must be increasing. If not, check:
- Address lists contain the right customer IPs
- The
chain=forwardis correct for the traffic direction - FastTrack is disabled for these customers
Customers exceed their per-customer cap
Section titled “Customers exceed their per-customer cap”PCQ pcq-rate is 0 or too high. Set a non-zero pcq-rate on the queue type
to enforce hard per-customer limits:
/queue type set pcq-down-bronze pcq-rate=10MVoIP is not getting priority
Section titled “VoIP is not getting priority”Priority operates among siblings (VoIP and data both under the same tier
parent). If VoIP and data are under different parents, priority has no effect
between them. Confirm both VoIP and data queues share the same parent= value.
Also check that the VoIP mangle mark (pm-voip-down) is incrementing — if no
packets carry DSCP 46, no traffic enters the VoIP queue.
Rate exceeds max-limit
Section titled “Rate exceeds max-limit”Hardware offloading bypasses software queuing. Disable it on shaped interfaces:
/interface ethernet set ether1 l2mtu=1598Or check for FastTrack connections bypassing the queue:
/ip firewall connection print where fasttrackRelated Resources
Section titled “Related Resources”- Queue Tree — HTB fundamentals,
limit-at,max-limit, priority - PCQ Example — Per-customer fair sharing with PCQ classifiers
- Queue Types — PCQ, SFQ, FIFO, and other qdisc options
- Firewall Mangle — Packet marking for queue tree classification
- Address Lists — Managing customer tier membership