{"id":2214547,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2214547/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20260322225147.469027-1-pablo@netfilter.org/","project":{"id":26,"url":"http://patchwork.ozlabs.org/api/1.2/projects/26/?format=json","name":"Netfilter Development","link_name":"netfilter-devel","list_id":"netfilter-devel.vger.kernel.org","list_email":"netfilter-devel@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20260322225147.469027-1-pablo@netfilter.org>","list_archive_url":null,"date":"2026-03-22T22:51:47","name":"[nf-next,v2] netfilter: nft_meta: add double-tagged vlan and pppoe support","commit_ref":null,"pull_url":null,"state":"accepted","archived":true,"hash":"bd2e1aad482449fcfe3c7bdcad4afc16a4662524","submitter":{"id":1315,"url":"http://patchwork.ozlabs.org/api/1.2/people/1315/?format=json","name":"Pablo Neira Ayuso","email":"pablo@netfilter.org"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20260322225147.469027-1-pablo@netfilter.org/mbox/","series":[{"id":497022,"url":"http://patchwork.ozlabs.org/api/1.2/series/497022/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/list/?series=497022","date":"2026-03-22T22:51:47","name":"[nf-next,v2] netfilter: nft_meta: add double-tagged vlan and pppoe support","version":2,"mbox":"http://patchwork.ozlabs.org/series/497022/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2214547/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2214547/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <netfilter-devel+bounces-11368-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","netfilter-devel@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=netfilter.org header.i=@netfilter.org\n header.a=rsa-sha256 header.s=2025 header.b=BGp1xKM8;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c04:e001:36c::12fc:5321; helo=tor.lore.kernel.org;\n envelope-from=netfilter-devel+bounces-11368-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=netfilter.org header.i=@netfilter.org\n header.b=\"BGp1xKM8\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=217.70.190.124","smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=netfilter.org","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=netfilter.org"],"Received":["from tor.lore.kernel.org (tor.lore.kernel.org\n [IPv6:2600:3c04:e001:36c::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4ffBPs74Z2z1xy3\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 23 Mar 2026 09:52:01 +1100 (AEDT)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 23049300888C\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 22 Mar 2026 22:51:59 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id A2F3B37F00D;\n\tSun, 22 Mar 2026 22:51:55 +0000 (UTC)","from mail.netfilter.org (mail.netfilter.org [217.70.190.124])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id A226531D381\n\tfor <netfilter-devel@vger.kernel.org>; Sun, 22 Mar 2026 22:51:53 +0000 (UTC)","from localhost.localdomain (mail-agni [217.70.190.124])\n\tby mail.netfilter.org (Postfix) with ESMTPSA id D696F6017D;\n\tSun, 22 Mar 2026 23:51:50 +0100 (CET)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1774219915; cv=none;\n b=dR2ZwIBAcrr7GtPjkUAZQQUd3DOEjgJj8luSKyyG44H1//zgR5P1xM13vq+6m6vlMvQrNAbv7VL2JYZcaUHd8ctlVLt7cYsiTvUQJNwmvlTfOluGrlIrFQNuObXJNV8vwjeIrfhx/crXOH+bMR49243UoB+019BZTftYe4ClFpQ=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1774219915; c=relaxed/simple;\n\tbh=MHmj++b5OyzR3sAqoM5R4Z+kQkUOqRc6XZ5y1alEEwk=;\n\th=From:To:Cc:Subject:Date:Message-ID:MIME-Version;\n b=ZVdD/NTFAuPzJJ7M2PZEsEBZP3Z5Gg0+xc5mK/DnOesDBZVRJ207BsDlm2jy25pb0b3PVXv1mDhhzOUnGGpBwKrp1ifHQ/RdWB8YHy6Y59LjzYGJQ3Vbztto4sqA0EB3xqf9GUwUygdQZjBgsOedsOhPaRQdm0xT21UCiQUotQs=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=netfilter.org;\n spf=pass smtp.mailfrom=netfilter.org;\n dkim=pass (2048-bit key) header.d=netfilter.org header.i=@netfilter.org\n header.b=BGp1xKM8; arc=none smtp.client-ip=217.70.190.124","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=netfilter.org;\n\ts=2025; t=1774219911;\n\tbh=yd2wbi7IfabOjlLCPuZ/IutC6OJ8VmyEUR3dWgL8QN8=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=BGp1xKM8aJe03JbVeWJNmgzcLQ6cYrmkOIaile72VncXX/mNBUmFpl3oyNx/3TbPl\n\t cGYRk+1WnB3EARdUUUzQQhdU4xiG6GUmWszTw97j+JCC3GqO31hoNTDcrDe5fvwOcf\n\t cR7oJGgsjaPVzsLA0N3h3QNBV/HMsO8ygBS2dvRyoj5cAB1Kx0szVFGqMO+tYvBIdw\n\t jmUlHVlch6uLALl1DjNJq0UXTR34yJ3lpbQzhlPFW1YRlEFPAXcko1NEFkYF0QUVwV\n\t 3tUKLAgM0DLWxHmWZ3WfTbYEWk7K/A9DMKk5AN9D7BT3xmTtERfivRwtr9T4YKWcBI\n\t FGbFLg2ZAnsPA==","From":"Pablo Neira Ayuso <pablo@netfilter.org>","To":"netfilter-devel@vger.kernel.org","Cc":"fw@strlen.de","Subject":"[PATCH nf-next,v2] netfilter: nft_meta: add double-tagged vlan and\n pppoe support","Date":"Sun, 22 Mar 2026 23:51:47 +0100","Message-ID":"<20260322225147.469027-1-pablo@netfilter.org>","X-Mailer":"git-send-email 2.47.3","Precedence":"bulk","X-Mailing-List":"netfilter-devel@vger.kernel.org","List-Id":"<netfilter-devel.vger.kernel.org>","List-Subscribe":"<mailto:netfilter-devel+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:netfilter-devel+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit"},"content":"Currently:\n\n  add rule netdev x y ip saddr 1.1.1.1\n\ndoes not work with neither double-tagged vlan nor pppoe packets. This is\nbecause the network and transport header offset are not pointing to the\nIP and transport protocol headers in the stack.\n\nThis patch expands NFT_META_PROTOCOL and NFT_META_L4PROTO to parse\ndouble-tagged vlan and pppoe packets so matching network and transport\nheader fields becomes possible with the existing userspace generated\nbytecode. Note that this parser only supports double-tagged vlan which\nis composed of vlan offload + vlan header in the skb payload area for\nsimplicity.\n\nNFT_META_PROTOCOL is used by bridge and netdev family as an implicit\ndependency in the bytecode to match on network header fields.\nSimilarly, there is also NFT_META_L4PROTO, which is also used as an\nimplicit dependency when matching on the transport protocol header\nfields.\n\nSigned-off-by: Pablo Neira Ayuso <pablo@netfilter.org>\n---\nv2: - Fix typo comment \"packer\" -> \"packet\".\n    - Add comment to clarify this support double-tagged vlan with\n      vlan offload + vlan header in skb data area.\n    - remove nft_meta_protocol_store() helper function.\n    Note: IPv6 still untested here.\n\n include/net/netfilter/nf_tables.h      |  4 ++\n include/net/netfilter/nf_tables_ipv4.h | 17 +++++---\n include/net/netfilter/nf_tables_ipv6.h | 16 +++++---\n net/netfilter/nf_tables_core.c         |  2 +-\n net/netfilter/nft_meta.c               | 54 +++++++++++++++++++++++++-\n net/netfilter/nft_payload.c            |  2 +-\n 6 files changed, 82 insertions(+), 13 deletions(-)","diff":"diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h\nindex acfa2ceef9c0..38847a91c4d0 100644\n--- a/include/net/netfilter/nf_tables.h\n+++ b/include/net/netfilter/nf_tables.h\n@@ -31,7 +31,9 @@ struct nft_pktinfo {\n \tconst struct nf_hook_state\t*state;\n \tu8\t\t\t\tflags;\n \tu8\t\t\t\ttprot;\n+\t__be16\t\t\t\tethertype;\n \tu16\t\t\t\tfragoff;\n+\tu16\t\t\t\tnhoff;\n \tu16\t\t\t\tthoff;\n \tu16\t\t\t\tinneroff;\n };\n@@ -83,6 +85,8 @@ static inline void nft_set_pktinfo_unspec(struct nft_pktinfo *pkt)\n {\n \tpkt->flags = 0;\n \tpkt->tprot = 0;\n+\tpkt->ethertype = pkt->skb->protocol;\n+\tpkt->nhoff = 0;\n \tpkt->thoff = 0;\n \tpkt->fragoff = 0;\n }\ndiff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h\nindex fcf967286e37..e715405a73cb 100644\n--- a/include/net/netfilter/nf_tables_ipv4.h\n+++ b/include/net/netfilter/nf_tables_ipv4.h\n@@ -12,16 +12,19 @@ static inline void nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt)\n \tip = ip_hdr(pkt->skb);\n \tpkt->flags = NFT_PKTINFO_L4PROTO;\n \tpkt->tprot = ip->protocol;\n+\tpkt->ethertype = pkt->skb->protocol;\n+\tpkt->nhoff = 0;\n \tpkt->thoff = ip_hdrlen(pkt->skb);\n \tpkt->fragoff = ntohs(ip->frag_off) & IP_OFFSET;\n }\n \n-static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)\n+static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt,\n+\t\t\t\t\t\t  int nhoff)\n {\n \tstruct iphdr *iph, _iph;\n \tu32 len, thoff, skb_len;\n \n-\tiph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb),\n+\tiph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb) + nhoff,\n \t\t\t\t sizeof(*iph), &_iph);\n \tif (!iph)\n \t\treturn -1;\n@@ -31,7 +34,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)\n \n \tlen = iph_totlen(pkt->skb, iph);\n \tthoff = iph->ihl * 4;\n-\tskb_len = pkt->skb->len - skb_network_offset(pkt->skb);\n+\tskb_len = pkt->skb->len - skb_network_offset(pkt->skb) - nhoff;\n \n \tif (skb_len < len)\n \t\treturn -1;\n@@ -42,7 +45,9 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)\n \n \tpkt->flags = NFT_PKTINFO_L4PROTO;\n \tpkt->tprot = iph->protocol;\n-\tpkt->thoff = skb_network_offset(pkt->skb) + thoff;\n+\tpkt->ethertype = pkt->skb->protocol;\n+\tpkt->nhoff = nhoff;\n+\tpkt->thoff = skb_network_offset(pkt->skb) + nhoff + thoff;\n \tpkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;\n \n \treturn 0;\n@@ -50,7 +55,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)\n \n static inline void nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)\n {\n-\tif (__nft_set_pktinfo_ipv4_validate(pkt) < 0)\n+\tif (__nft_set_pktinfo_ipv4_validate(pkt, 0) < 0)\n \t\tnft_set_pktinfo_unspec(pkt);\n }\n \n@@ -78,6 +83,8 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt)\n \t}\n \n \tpkt->flags = NFT_PKTINFO_L4PROTO;\n+\tpkt->ethertype = pkt->skb->protocol;\n+\tpkt->nhoff = 0;\n \tpkt->tprot = iph->protocol;\n \tpkt->thoff = thoff;\n \tpkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;\ndiff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h\nindex c53ac00bb974..d7b8c559b795 100644\n--- a/include/net/netfilter/nf_tables_ipv6.h\n+++ b/include/net/netfilter/nf_tables_ipv6.h\n@@ -20,21 +20,23 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt)\n \n \tpkt->flags = NFT_PKTINFO_L4PROTO;\n \tpkt->tprot = protohdr;\n+\tpkt->ethertype = pkt->skb->protocol;\n+\tpkt->nhoff = 0;\n \tpkt->thoff = thoff;\n \tpkt->fragoff = frag_off;\n }\n \n-static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)\n+static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt, int nhoff)\n {\n #if IS_ENABLED(CONFIG_IPV6)\n \tunsigned int flags = IP6_FH_F_AUTH;\n \tstruct ipv6hdr *ip6h, _ip6h;\n-\tunsigned int thoff = 0;\n+\tunsigned int thoff = nhoff;\n \tunsigned short frag_off;\n \tu32 pkt_len, skb_len;\n \tint protohdr;\n \n-\tip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb),\n+\tip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb) + nhoff,\n \t\t\t\t  sizeof(*ip6h), &_ip6h);\n \tif (!ip6h)\n \t\treturn -1;\n@@ -43,7 +45,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)\n \t\treturn -1;\n \n \tpkt_len = ipv6_payload_len(pkt->skb, ip6h);\n-\tskb_len = pkt->skb->len - skb_network_offset(pkt->skb);\n+\tskb_len = pkt->skb->len - skb_network_offset(pkt->skb) - nhoff;\n \tif (pkt_len + sizeof(*ip6h) > skb_len)\n \t\treturn -1;\n \n@@ -53,6 +55,8 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)\n \n \tpkt->flags = NFT_PKTINFO_L4PROTO;\n \tpkt->tprot = protohdr;\n+\tpkt->ethertype = pkt->skb->protocol;\n+\tpkt->nhoff = nhoff;\n \tpkt->thoff = thoff;\n \tpkt->fragoff = frag_off;\n \n@@ -64,7 +68,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)\n \n static inline void nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)\n {\n-\tif (__nft_set_pktinfo_ipv6_validate(pkt) < 0)\n+\tif (__nft_set_pktinfo_ipv6_validate(pkt, 0) < 0)\n \t\tnft_set_pktinfo_unspec(pkt);\n }\n \n@@ -99,6 +103,8 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt)\n \n \tpkt->flags = NFT_PKTINFO_L4PROTO;\n \tpkt->tprot = protohdr;\n+\tpkt->ethertype = pkt->skb->protocol;\n+\tpkt->nhoff = 0;\n \tpkt->thoff = thoff;\n \tpkt->fragoff = frag_off;\n \ndiff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c\nindex 6557a4018c09..5ddd5b6e135f 100644\n--- a/net/netfilter/nf_tables_core.c\n+++ b/net/netfilter/nf_tables_core.c\n@@ -151,7 +151,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr,\n \tunsigned char *ptr;\n \n \tif (priv->base == NFT_PAYLOAD_NETWORK_HEADER)\n-\t\tptr = skb_network_header(skb);\n+\t\tptr = skb_network_header(skb) + pkt->nhoff;\n \telse {\n \t\tif (!(pkt->flags & NFT_PKTINFO_L4PROTO))\n \t\t\treturn false;\ndiff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c\nindex d0df6cf374d1..ecce151c7ad6 100644\n--- a/net/netfilter/nft_meta.c\n+++ b/net/netfilter/nft_meta.c\n@@ -23,6 +23,8 @@\n #include <net/tcp_states.h> /* for TCP_TIME_WAIT */\n #include <net/netfilter/nf_tables.h>\n #include <net/netfilter/nf_tables_core.h>\n+#include <net/netfilter/nf_tables_ipv4.h>\n+#include <net/netfilter/nf_tables_ipv6.h>\n #include <net/netfilter/nft_meta.h>\n #include <net/netfilter/nf_tables_offload.h>\n \n@@ -309,6 +311,54 @@ nft_meta_get_eval_sdifname(u32 *dest, const struct nft_pktinfo *pkt)\n \tnft_meta_store_ifname(dest, dev);\n }\n \n+static void nft_meta_pktinfo_may_update(struct nft_pktinfo *pkt)\n+{\n+\tstruct sk_buff *skb = pkt->skb;\n+\tstruct vlan_ethhdr *veth;\n+\t__be16 ethertype;\n+\tint nhoff;\n+\n+\t/* Is this an IP packet? Then, skip. */\n+\tif (pkt->flags)\n+\t\treturn;\n+\n+\t/* ... else maybe an IP packet over PPPoE or Q-in-Q? */\n+\tswitch (skb->protocol) {\n+\tcase htons(ETH_P_8021Q):\n+\t\tif (!pskb_may_pull(skb, skb_mac_offset(skb) + sizeof(*veth)))\n+\t\t\treturn;\n+\n+\t\tveth = (struct vlan_ethhdr *)skb_mac_header(skb);\n+\t\tnhoff = VLAN_HLEN;\n+\t\tethertype = veth->h_vlan_encapsulated_proto;\n+\t\tbreak;\n+\tcase htons(ETH_P_PPP_SES):\n+\t\tif (!nf_flow_pppoe_proto(skb, &ethertype))\n+\t\t\treturn;\n+\n+\t\tnhoff = PPPOE_SES_HLEN;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn;\n+\t}\n+\n+\tnhoff += skb_network_offset(skb);\n+\tswitch (ethertype) {\n+\tcase htons(ETH_P_IP):\n+\t\tif (__nft_set_pktinfo_ipv4_validate(pkt, nhoff))\n+\t\t\tnft_set_pktinfo_unspec(pkt);\n+\t\tbreak;\n+\tcase htons(ETH_P_IPV6):\n+\t\tif (__nft_set_pktinfo_ipv6_validate(pkt, nhoff))\n+\t\t\tnft_set_pktinfo_unspec(pkt);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\tpkt->ethertype = ethertype;\n+}\n+\n void nft_meta_get_eval(const struct nft_expr *expr,\n \t\t       struct nft_regs *regs,\n \t\t       const struct nft_pktinfo *pkt)\n@@ -322,12 +372,14 @@ void nft_meta_get_eval(const struct nft_expr *expr,\n \t\t*dest = skb->len;\n \t\tbreak;\n \tcase NFT_META_PROTOCOL:\n-\t\tnft_reg_store16(dest, (__force u16)skb->protocol);\n+\t\tnft_meta_pktinfo_may_update((struct nft_pktinfo *)pkt);\n+\t\tnft_reg_store16(dest, (__force u16)pkt->ethertype);\n \t\tbreak;\n \tcase NFT_META_NFPROTO:\n \t\tnft_reg_store8(dest, nft_pf(pkt));\n \t\tbreak;\n \tcase NFT_META_L4PROTO:\n+\t\tnft_meta_pktinfo_may_update((struct nft_pktinfo *)pkt);\n \t\tif (!(pkt->flags & NFT_PKTINFO_L4PROTO))\n \t\t\tgoto err;\n \t\tnft_reg_store8(dest, pkt->tprot);\ndiff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c\nindex 973d56af03ff..75500eb328bc 100644\n--- a/net/netfilter/nft_payload.c\n+++ b/net/netfilter/nft_payload.c\n@@ -183,7 +183,7 @@ void nft_payload_eval(const struct nft_expr *expr,\n \t\toffset = skb_mac_header(skb) - skb->data;\n \t\tbreak;\n \tcase NFT_PAYLOAD_NETWORK_HEADER:\n-\t\toffset = skb_network_offset(skb);\n+\t\toffset = skb_network_offset(skb) + pkt->nhoff;\n \t\tbreak;\n \tcase NFT_PAYLOAD_TRANSPORT_HEADER:\n \t\tif (!(pkt->flags & NFT_PKTINFO_L4PROTO) || pkt->fragoff)\n","prefixes":["nf-next","v2"]}