tc u32 --- how to match L2 protocols in recent kernels?

I have a nice shaper, with hashed filtering, built at a linux bridge. In short, br0 connects external and internal physical interfaces, VLAN tagged packets are bridged "transparently" (I mean, no VLAN interfaces are there).

Now, different kernels do it differently. I can be wrong with exact kernel verions ranges, please forgive me. Thanks.

2.6.26

So, in debian, 2.6.26 and up (up to 2.6.32, I believe) --- this works:

tc filter add dev internal protocol 802.1q parent 1:0 prio 100 \
    u32 ht 1:64 match ip dst 192.168.1.100 flowid 1:200

Here, "kernel" matches two bytes in "protocol" field with 0x8100, but counts the beginning of ip packet as a "zero position" (sorry for my English, if I'm a bit unclear).

2.6.32

Again, in debian (I've not built vanilla kernel), 2.6.32-5 --- this works:

tc filter add dev internal protocol 802.1q parent 1:0 prio 100 \
    u32 ht 1:64 match ip dst 192.168.1.100 at 20 flowid 1:200

Here, "kernel" matches the same for protocol, but counts offset from the beginning of this protocol's header --- I have to add 4 bytes to offset (20, not 16 for dst address). It's ok, seems more logical, as for me.

3.2.11, the latest stable now

This works --- as if there is no 802.1q tag at all:

tc filter add dev internal protocol ip parent 1:0 prio 100 \
    u32 ht 1:64 match ip dst 192.168.1.100 flowid 1:200

The problem is that I couldn't find a way to match 802.1q tag so far.

Matching 802.1q tag at past

I could do this before as follows:

tc filter add dev internal protocol 802.1q parent 1:0 prio 100 \
    u32 match u16 0x0ed8 0x0fff at -4 flowid 1:300

Now I'm unable to match 802.1q tag with at 0, at -2, at -4, at -6 or like that. The main issue that I have zero hits count --- this filter is not being checked at all, "wrong protocol", in other words.

Please, anyone, help me :-)

Thanks!


VLAN tag is stripped from skb in recent kernels. Try something like this to do a meta match in skb:

tc filter add dev internal protocol all parent 1:0 prio 100 basic match 'meta(vlan mask 0xfff eq 0x0ed8)' flowid 1:300

I had to do exactly this. I found that the answer suggested by @Thusitha was the correct way to do it for new kernels.

Tested with the Debian wheezy kernel 3.2.0-4 and iproute (from where the tc command comes from) version 20120521-3+b3

Here is the complete script, the tc filter lines being almost exactly as specified by @Thusitha

function qos() {
    if="$1"
    vlan1="$2"
    vlan2="$3"

    # delete previous
    tc qdisc del dev $if root >/dev/null 2>&1
    tc qdisc del dev $if ingress >/dev/null 2>&1

    # Root HTB for $if
    tc qdisc add dev $if root handle 1: htb r2q 1 default 1

    # Root class to borrow from
    tc class add dev $if parent 1: classid 1:1 htb quantum 1000000 rate 500mbit ceil 500mbit burst 64k prio 2
    tc qdisc add dev $if parent 1:1 handle 101 sfq perturb 10

    # class for vlan1
    tc class add dev $if parent 1:1 classid 1:106 htb quantum 1000000 rate 1.00mbit ceil 1.00mbit burst 6k
    tc qdisc add dev $if parent 1:106 handle 107 sfq perturb 10
    tc filter add dev $if protocol all parent 1: prio 100 basic match "meta(vlan mask 0xfff eq ${vlan1})" flowid 1:106

    # class for vlan2
    tc class add dev $if parent 1:1 classid 1:108 htb quantum 1000000 rate 1.00mbit ceil 10.00mbit burst 6k
    tc qdisc add dev $if parent 1:108 handle 108 sfq perturb 10
    tc filter add dev $if protocol all parent 1: prio 100 basic match "meta(vlan mask 0xfff eq ${vlan2})" flowid 1:108

}

qos eth1 1234 1235
qos eth2 2345 2346