I spent months trying to get dynamic HTB traffic shaping.
I want to have priority tiers, classes. I need high priority traffic to choke lower priority traffic to basic rate, but when no high priority traffic taking root bandwidth - low priority should upscale to full available bandwidth.
So here is my lab setup:
Lab-TC-Target 10.128.7.215 - iperf3 server Lab-TC-GW 10.128.7.216, 172.16.2.1/24 - VyOS GW, NAT, shaper Lab-TC-T1 172.16.2.2/24 - Tier 1, high priority Lab-TC-T2 172.16.2.3/24 - Tier 2, middle 1 priority Lab-TC-T3 172.16.2.4/24 - Tier 2, middle 2 priority Lab-TC-T4 172.16.2.5/24 - Tier 3, lowest priority Lab-TC-T5 172.16.2.6/24 - Tier 4, default priority
Config
# Packet marking set policy route tc-mark interface 'eth1' set policy route tc-mark rule 10 description 't1' set policy route tc-mark rule 10 set mark '244010' set policy route tc-mark rule 10 source address '172.16.2.2/32' set policy route tc-mark rule 20 description 't2' set policy route tc-mark rule 20 set mark '244020' set policy route tc-mark rule 20 source address '172.16.2.3/32' set policy route tc-mark rule 30 description 't3' set policy route tc-mark rule 30 set mark '244030' set policy route tc-mark rule 30 source address '172.16.2.4/32' set policy route tc-mark rule 40 description 't4' set policy route tc-mark rule 40 set mark '244040' set policy route tc-mark rule 40 source address '172.16.2.5/32' set qos traffic-match-group t1 match t1 mark '244010' set qos traffic-match-group t1 match t2 mark '244020' set qos traffic-match-group t2 match t3 mark '244030' set qos traffic-match-group t3 match t4 mark '244040' set qos traffic-match-group t4 match t5 mark '244050' # Delete delete qos policy shaper UPLOAD-200 # Shaper set qos policy shaper UPLOAD-200 bandwidth '200mbit' # High priority set qos policy shaper UPLOAD-200 class 10 description 'T1' set qos policy shaper UPLOAD-200 class 10 ceiling '25mbit' set qos policy shaper UPLOAD-200 class 10 bandwidth '25mbit' set qos policy shaper UPLOAD-200 class 10 priority '1' set qos policy shaper UPLOAD-200 class 10 burst '256k' set qos policy shaper UPLOAD-200 class 10 queue-type 'fq-codel' set qos policy shaper UPLOAD-200 class 10 match-group t1 # Default priority set qos policy shaper UPLOAD-200 default ceiling '200mbit' set qos policy shaper UPLOAD-200 default bandwidth '25mbit' set qos policy shaper UPLOAD-200 default burst '128k' set qos policy shaper UPLOAD-200 default priority '7' set qos policy shaper UPLOAD-200 default queue-type 'fq-codel' # Middle 1 priority set qos policy shaper UPLOAD-200 class 20 description 'T2' set qos policy shaper UPLOAD-200 class 20 ceiling '200mbit' set qos policy shaper UPLOAD-200 class 20 bandwidth '10mbit' set qos policy shaper UPLOAD-200 class 20 burst '128k' set qos policy shaper UPLOAD-200 class 20 priority '9' set qos policy shaper UPLOAD-200 class 20 queue-type 'fq-codel' set qos policy shaper UPLOAD-200 class 20 match-group t2 # Middle 2 priority set qos policy shaper UPLOAD-200 class 40 description 'T3' set qos policy shaper UPLOAD-200 class 40 ceiling '200mbit' set qos policy shaper UPLOAD-200 class 40 bandwidth '10mbit' set qos policy shaper UPLOAD-200 class 40 burst '128k' set qos policy shaper UPLOAD-200 class 40 priority '10' set qos policy shaper UPLOAD-200 class 40 queue-type 'fq-codel' set qos policy shaper UPLOAD-200 class 40 match-group t3 # Lowest priority set qos policy shaper UPLOAD-200 class 70 description 'T4' set qos policy shaper UPLOAD-200 class 70 ceiling '200mbit' set qos policy shaper UPLOAD-200 class 70 bandwidth '10mbit' set qos policy shaper UPLOAD-200 class 70 burst '128k' set qos policy shaper UPLOAD-200 class 70 priority '14' set qos policy shaper UPLOAD-200 class 70 queue-type 'fq-codel' set qos policy shaper UPLOAD-200 class 70 match-group t4
And guess what - all leaf qdiscs (classes) was always stuck at configured bandwidth, 10mbit.
Then I digged up tc filters, and output showed this
(Sorry, I only have screenshot at the moment).
What you can see is tc filter rules have action POLICY, with hard coded bandwidth limits according to qos class config.
In case of HTB, why statically limit bandwidth? HTB's task is to upscale and downscale queues dynamically.
Digging source code I've found that filters add policy action,
https://github.com/vyos/vyos-1x/blob/current/python/vyos/qos/base.py#L230
Why?
I see it was fixed in past,
https://github.com/vyos/vyos-1x/blob/08b333eac3c274a9111e68823e5c9de585add5e4/python/vyos/qos/base.py#L243, sever-sever commented action policy in filters, and after that commit it would have been possible to configure HTB with dynamic bandwidth scaling according to priorities, tokens.
Just after the month of commit, new change to fix policer was added
https://github.com/vyos/vyos-1x/commit/7632b7446ec45d2444cb3ccfb075c8e8b2df31b0
If this was done intentionally, then 'action policy' in base.py is also being called in shaper policy, which shouldn't happen.
I was able to achieve HTB shaping, but outside of VyOS using tc directly like this
#!/bin/bash set -e IF="eth0" PARENT_RATE="200mbit" PARENT_CEIL="200mbit" MARK_HIGH=244010 # T1 MARK_T2=244020 # T2 MARK_T3=244030 # T3 MARK_T4=244040 # T4 RATE_HIGH="25mbit" # class 10 RATE_T2="10mbit" # class 20 RATE_T3="10mbit" # class 40 RATE_T4="10mbit" # class 70 RATE_DEFAULT="256kbit" # class 30 (unmatched traffic) # Ceil for all classes. Everyone can borrow up to the parent when idle CEIL_ALL="200mbit" PRIO_HIGH=0 PRIO_T2=1 PRIO_DEFAULT=2 PRIO_T3=3 PRIO_T4=7 QUANTUM=15140 BURST_PARENT="64k" BURST_CHILD="64k" # Clean tc qdisc del dev "$IF" root 2>/dev/null || true tc qdisc del dev "$IF" ingress 2>/dev/null || true # Root HTB qdisc tc qdisc add dev "$IF" root handle 1: htb default 30 r2q 10 # Root class 1:1 tc class add dev "$IF" parent 1: classid 1:1 htb \ rate "$PARENT_RATE" ceil "$PARENT_CEIL" \ burst "$BURST_PARENT" cburst "$BURST_PARENT" # 1:10 = HIGH / T1 # 1:20 = T2 # 1:30 = DEFAULT # 1:40 = T3 # 1:70 = T4 (lowest) # High priority / T1 tc class add dev "$IF" parent 1:1 classid 1:10 htb \ rate "$RATE_HIGH" ceil "$CEIL_ALL" \ burst "$BURST_CHILD" cburst "$BURST_CHILD" \ quantum "$QUANTUM" prio "$PRIO_HIGH" # T2 tc class add dev "$IF" parent 1:1 classid 1:20 htb \ rate "$RATE_T2" ceil "$CEIL_ALL" \ burst "$BURST_CHILD" cburst "$BURST_CHILD" \ quantum "$QUANTUM" prio "$PRIO_T2" # Default tc class add dev "$IF" parent 1:1 classid 1:30 htb \ rate "$RATE_DEFAULT" ceil "$CEIL_ALL" \ burst "$BURST_CHILD" cburst "$BURST_CHILD" \ quantum "$QUANTUM" prio "$PRIO_DEFAULT" # T3 tc class add dev "$IF" parent 1:1 classid 1:40 htb \ rate "$RATE_T3" ceil "$CEIL_ALL" \ burst "$BURST_CHILD" cburst "$BURST_CHILD" \ quantum "$QUANTUM" prio "$PRIO_T3" # T4 (lowest) tc class add dev "$IF" parent 1:1 classid 1:70 htb \ rate "$RATE_T4" ceil "$CEIL_ALL" \ burst "$BURST_CHILD" cburst "$BURST_CHILD" \ quantum "$QUANTUM" prio "$PRIO_T4" tc qdisc add dev "$IF" parent 1:10 handle 110: fq_codel tc qdisc add dev "$IF" parent 1:20 handle 120: fq_codel tc qdisc add dev "$IF" parent 1:30 handle 130: fq_codel tc qdisc add dev "$IF" parent 1:40 handle 140: fq_codel tc qdisc add dev "$IF" parent 1:70 handle 170: fq_codel # High / T1 traffic -> 1:10 tc filter add dev "$IF" parent 1: protocol all prio 10 handle $MARK_HIGH fw classid 1:10 # T2 traffic -> 1:20 tc filter add dev "$IF" parent 1: protocol all prio 20 handle $MARK_T2 fw classid 1:20 # T3 traffic -> 1:40 tc filter add dev "$IF" parent 1: protocol all prio 30 handle $MARK_T3 fw classid 1:40 # T4 (lowest) traffic -> 1:70 tc filter add dev "$IF" parent 1: protocol all prio 40 handle $MARK_T4 fw classid 1:70
So, what needs to be done?
Block that adds 'action policy' should be removed OR additional check if its called for shaper, not limiter (policer)
