Hi First of all thanks a lot for your comments. The offset I managed to figure out by looking at the netlink debug printout when adding the corresponding rule (add rule netdev filter in vlan type 0x88ba). nft --debug=netlink add rule netdev filter in vlan type 0x88ba netdev filter in [ meta load iiftype => reg 1 ] [ cmp eq reg 1 0x00000001 ] [ payload load 2b @ link header + 12 => reg 1 ] [ cmp eq reg 1 0x00000081 ] [ payload load 2b @ link header + 16 => reg 1 ] [ cmp eq reg 1 0x0000ba88 ] What I meant by offsetof() is which struct do I need to use to get the correct offset. Similar to like offsetof(struct iphdr, protocol) it would be nice to have e.g offsetof(struct XX, vlan). I added a counter to my rule and it seems to match the packets correctly. table netdev filter { chain in { type filter hook ingress device pru10 priority 0; policy accept; iiftype ether @ll,96,16 33024 @ll,128,16 35002 counter packets 387948 bytes 43450176 <- done via libnftnl vlan type 0x88ba counter packets 62428 bytes 6991936 <- done with #nft add rule netdev filter in iiftype ether @ll,96,16 33024 @ll,128,16 35002 counter } } Any idea why #nft list ruleset shows the two identical rules differently? Thanks a lot. BR Andy On Fri, Jul 03, 2020 at 06:45:45AM +0000, Andreas Hoefler wrote: > Hi > I am trying to use libnftnl to construct this: > > table netdev filter { > chain in { > type filter hook ingress device pru20 priority 0; policy accept; > vlan type 0x88ba > } > } > > I do : > add_meta(r, NFT_META_IIFTYPE, NFT_REG_1); > uint32_t iiftype = 1; > add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &iiftype, sizeof(iiftype)); > > add_payload(r, NFT_PAYLOAD_LL_HEADER, NFT_REG_1, 12, sizeof(uint16_t)); > uint16_t vtype = htons(ETH_P_8021Q); > add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &vtype, sizeof(vtype)); Is your offset (in bytes) correct? > add_payload(r, NFT_PAYLOAD_LL_HEADER, NFT_REG_1, 16, sizeof(uint16_t)); > uint16_t et = htons(0x88ba); > add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &et, sizeof(et)); > > This produces the following rule > table netdev filter { > chain in { > type filter hook ingress device pru20 priority 0; policy drop; > iiftype ether @ll,96,16 33024 @ll,128,16 35002 > } > } > When I manually add the constructed rule: > #nft add rule netdev filter in iiftype ether @ll,96,16 33024 > @ll,128,16 35002 > > then nft list ruleset translates it correctly so I assume that this rule is built right: > > table netdev filter { > chain in { > type filter hook ingress device pru20 priority 0; policy drop; > iiftype ether @ll,96,16 33024 @ll,128,16 35002 <- constructed with code above > vlan type 0x88ba <- manually added, same rule as above but translated ok > } > } > > My questions: > - What are the correct enums to use for e.g iiftype =1;? ARPHRD_ETHER > - Is there something like offsetof(struct ???, vlan) which I could use instead of hardcoded offset? man 3 offsetof > - Why does list ruleset show the coded rule differently from the manually added one? Is your bytecode matching packets? Probably adding a counter would allow you to check for this. > - uint16_t vtype = htons(ETH_P_8021Q); seems weird to use htons here, is there another enum I should use? You can use --debug=netlink to display the bytecode that nft generates: # nft --debug=netlink add rule x y vlan type 0x88ba ip [ meta load iiftype => reg 1 ] [ cmp eq reg 1 0x00000001 ] [ payload load 2b @ link header + 2 => reg 1 ] [ cmp eq reg 1 0x0000ba88 ] Error: Could not process rule: No such file or directory add rule x y vlan type 0x88ba Then, compare it with your manually generated bytecode.