It is possible. I believe tc(8) man is not comprehensive.
Citing from man7.org/tc(8):
tc filters
If tc filters are attached to a class, they are consulted
first for relevant instructions. Filters can match on all
fields of a packet header, as well as on the firewall mark
applied by ipchains or iptables.
Just checked on my Linux 5.0.3-zen1-2-zen laptop and it works.
lang-bash
sudo tc qdisc add dev enp3s0 root handle 1: htb default 10
# inner class
sudo tc class add dev enp3s0 parent 1: classid 1:1 htb rate 100kbps
# leaf classes
sudo tc class add dev enp3s0 parent 1:1 classid 1:10 htb rate 100kbps
sudo tc class add dev enp3s0 parent 1:1 classid 1:20 htb rate 100kbps
sudo tc class add dev enp3s0 parent 1:1 classid 1:30 htb rate 100kbps
sudo tc filter add dev enp3s0 parent 1: protocol ip matchall flowid 1:1
sudo tc filter add dev enp3s0 parent 1:1 u32 match ip dst 192.168.1.6 flowid 1:30
ping -c1 192.168.1.6
ping -c2 192.168.1.1
sudo tc -s -g class show dev enp3s0
sudo tc -s filter show dev enp3s0 parent 1:
sudo tc -s filter show dev enp3s0 parent 1:1
First filter's parent is qdisc and it sets flowid to inner class 1:1
. Second filter's parent is the inner class 1:1
and it does final verdict selecting leaf class 1:30
Output of last 3 commands:
$ sudo tc -s -g class show dev enp3s0
+---(1:1) htb rate 800Kbit ceil 800Kbit burst 1600b cburst 1600b
| Sent 294 bytes 3 pkt (dropped 0, overlimits 0 requeues 0)
| backlog 0b 0p requeues 0
|
+---(1:10) htb prio 0 rate 800Kbit ceil 800Kbit burst 1600b cburst 1600b
| Sent 196 bytes 2 pkt (dropped 0, overlimits 0 requeues 0)
| backlog 0b 0p requeues 0
|
+---(1:20) htb prio 0 rate 800Kbit ceil 800Kbit burst 1600b cburst 1600b
| Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
| backlog 0b 0p requeues 0
|
+---(1:30) htb prio 0 rate 800Kbit ceil 800Kbit burst 1600b cburst 1600b
Sent 98 bytes 1 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
$ sudo tc -s filter show dev enp3s0 parent 1:
filter protocol ip pref 49152 matchall chain 0
filter protocol ip pref 49152 matchall chain 0 handle 0x1 flowid 1:1
not_in_hw
$ sudo tc -s filter show dev enp3s0 parent 1:1
filter protocol all pref 49152 u32 chain 0
filter protocol all pref 49152 u32 chain 0 fh 800: ht divisor 1
filter protocol all pref 49152 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:30 not_in_hw (rule hit 3 success 1)
match c0a80106/ffffffff at 16 (success 1 )
Note: To make filters parented at inner class work, inner class should be referenced (with classid
or flowid
) by filters from upper level inner class or qdisc, i.e. preserve chain qdisc --filter--> inner class --filter--> leaf class
.
Also make sure htb default
is set to leaf class.