Eric Garver reported a number of issues when matching vlan headers: In: update @macset { ether saddr . vlan id timeout 5s } Out: update @macset { @ll,48,48 . @ll,112,16 & 0xfff timeout 5s } This is because of amnesia in nft during expression decoding: When we encounter 'vlan id', the L2 protocl (ethernet) is replaced by vlan, so we attempt to match @ll,48,48 vs. the vlan header and come up empty. The vlan decode fails because we can't handle '& 0xfff' in this instance, so we can locate the right offset but the payload expression length doesn't match the template length (16 vs 12 bits). The main patch is patch 3, which adds a stack of l2 protocols to track instead of only keeping the cumulative size. The latter is ok for serialization (we have the expression tree, so its enough to add the size of the 'previous' l2 headers to payload expressions that match the new 'top' l2 header. But for deserialization, we need to be able to search all protocols base headers seen. The remaining patches improve handling of 'integer base type' expressions and add test cases.