* [PATCH nf v2 1/2] netfilter: br_netfilter: restore VLAN tag on refragmented IPv6 packets
2026-10-04 21:24 [PATCH nf v2 0/2] netfilter: preserve bridge egress VLAN tags Andrea Parri
@ 2026-10-04 21:24 ` Andrea Parri
2026-10-04 21:24 ` [PATCH nf v2 2/2] netfilter: br_netfilter: clear stale VLAN tag on refragmented packets Andrea Parri
2026-10-04 21:29 ` [PATCH nf v2 0/2] netfilter: preserve bridge egress VLAN tags netdev-bot+sinfo
2 siblings, 0 replies; 5+ messages in thread
From: Andrea Parri @ 2026-10-04 21:24 UTC (permalink / raw)
To: Pablo Neira Ayuso, Florian Westphal, netfilter-devel
Cc: Andrea Parri, Phil Sutter, Nikolay Aleksandrov, Ido Schimmel,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Bernhard Thaler, coreteam, bridge, netdev,
linux-kernel, stable
Bridged IPv6 packets can lose their VLAN tag or acquire an unrelated tag
when conntrack-reassembled packets are refragmented. On a VLAN-aware
bridge, or on any bridge with bridge-nf-filter-vlan-tagged enabled, this
can send fragments with a different VLAN tag from the one selected for
forwarding.
br_nf_push_frag_xmit() restores the VLAN tag from per-CPU storage, but
only the IPv4 branch of br_nf_dev_queue_xmit() saves it. The IPv6 branch
leaves the saved tag from the previous IPv4 refragmentation on that CPU.
Newly allocated fragments do not inherit the tag through
ip6_copy_metadata().
Commit d7b597421519 ("netfilter: bridge: restore vlan tag when
refragmenting") added VLAN tag restoration for IPv4 only, shortly after
IPv6 refragmentation was introduced.
This was reproduced on a VLAN-aware bridge by first refragmenting an IPv4
VLAN 100 flow on the same CPU. All fragments of subsequent IPv6 flows left
with stale VLAN 100 instead of the VLAN selected for their egress ports.
Move saving the L2 header and VLAN tag into br_nf_save_frag_data() and
call it from both branches, so the fragments that ip6_fragment() builds
use the current packet's VLAN information.
Fixes: efb6de9b4ba0 ("netfilter: bridge: forward IPv6 fragmented packets")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
net/bridge/br_netfilter_hooks.c | 45 +++++++++++++++------------------
1 file changed, 21 insertions(+), 24 deletions(-)
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 0a394e5f43916..fe8d2910dae21 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -832,6 +832,25 @@ static unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
return 0;
}
+/* Saved for br_nf_push_frag_xmit() to restore on every fragment. */
+static void br_nf_save_frag_data(const struct sk_buff *skb)
+{
+ struct brnf_frag_data *data = this_cpu_ptr(&brnf_frag_data_storage);
+
+ if (skb_vlan_tag_present(skb)) {
+ data->vlan_tci = skb->vlan_tci;
+ data->vlan_proto = skb->vlan_proto;
+ } else {
+ data->vlan_proto = 0;
+ }
+
+ data->encap_size = nf_bridge_encap_header_len(skb);
+ data->size = ETH_HLEN + data->encap_size;
+
+ skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
+ data->size);
+}
+
static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
@@ -866,28 +885,13 @@ static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff
*/
if (IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) &&
skb->protocol == htons(ETH_P_IP)) {
- struct brnf_frag_data *data;
-
if (br_validate_ipv4(net, skb))
goto drop;
IPCB(skb)->frag_max_size = nf_bridge->frag_max_size;
local_lock_nested_bh(&brnf_frag_data_storage.bh_lock);
- data = this_cpu_ptr(&brnf_frag_data_storage);
-
- if (skb_vlan_tag_present(skb)) {
- data->vlan_tci = skb->vlan_tci;
- data->vlan_proto = skb->vlan_proto;
- } else {
- data->vlan_proto = 0;
- }
-
- data->encap_size = nf_bridge_encap_header_len(skb);
- data->size = ETH_HLEN + data->encap_size;
-
- skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
- data->size);
+ br_nf_save_frag_data(skb);
ret = br_nf_ip_fragment(net, sk, skb, br_nf_push_frag_xmit);
local_unlock_nested_bh(&brnf_frag_data_storage.bh_lock);
@@ -895,20 +899,13 @@ static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff
}
if (IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) &&
skb->protocol == htons(ETH_P_IPV6)) {
- struct brnf_frag_data *data;
-
if (br_validate_ipv6(net, skb))
goto drop;
IP6CB(skb)->frag_max_size = nf_bridge->frag_max_size;
local_lock_nested_bh(&brnf_frag_data_storage.bh_lock);
- data = this_cpu_ptr(&brnf_frag_data_storage);
- data->encap_size = nf_bridge_encap_header_len(skb);
- data->size = ETH_HLEN + data->encap_size;
-
- skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
- data->size);
+ br_nf_save_frag_data(skb);
ret = ip6_fragment(net, sk, skb, br_nf_push_frag_xmit);
local_unlock_nested_bh(&brnf_frag_data_storage.bh_lock);
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH nf v2 2/2] netfilter: br_netfilter: clear stale VLAN tag on refragmented packets
2026-10-04 21:24 [PATCH nf v2 0/2] netfilter: preserve bridge egress VLAN tags Andrea Parri
2026-10-04 21:24 ` [PATCH nf v2 1/2] netfilter: br_netfilter: restore VLAN tag on refragmented IPv6 packets Andrea Parri
@ 2026-10-04 21:24 ` Andrea Parri
2026-10-04 21:29 ` [PATCH nf v2 0/2] netfilter: preserve bridge egress VLAN tags netdev-bot+sinfo
2 siblings, 0 replies; 5+ messages in thread
From: Andrea Parri @ 2026-10-04 21:24 UTC (permalink / raw)
To: Pablo Neira Ayuso, Florian Westphal, netfilter-devel
Cc: Andrea Parri, Phil Sutter, Nikolay Aleksandrov, Ido Schimmel,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Bernhard Thaler, coreteam, bridge, netdev,
linux-kernel, stable
Bridged packets that conntrack reassembles and br_netfilter refragments
can leave a port that is an untagged member of their VLAN with all but
the first fragment still carrying the ingress VLAN tag.
With bridge-nf-filter-vlan-tagged enabled, conntrack defrag keeps the
original fragments on the frag_list of the reassembled skb, and each of
them keeps the tag it had on ingress. br_handle_vlan() only strips the
tag of the head skb. When that skb is not cloned, ip_do_fragment() and
ip6_fragment() send the frag_list skbs as the fragments, and
br_nf_push_frag_xmit() only ever sets a tag.
This was triggered with a reproducer that sends fragmented IPv4 and
IPv6 packets in VLAN 10 to a port that is untagged in VLAN 10, through
a static FDB entry. The first fragment of each packet left untagged and
the others left tagged:
... ethertype 802.1Q (0x8100), length 1514: vlan 10, p 0,
ethertype IPv6 (0x86dd), fd00:10::1 > fd00:10::4:
frag (1448|1448)
IPv6 became affected when commit efb6de9b4ba0 ("netfilter: bridge:
forward IPv6 fragmented packets") added IPv6 refragmentation.
IPv4 has been affected since commit 7885198861fc ("bridge: Implement
vlan ingress/egress policy with PVID.") made the bridge strip the tag on
untagged egress, when IPv4 defragmentation retained fragments on
frag_list. Commit 14fe22e33462 ("Revert "ipv4: use skb coalescing in
defragmentation"") later restored unconditional frag_list reuse.
Clear the tag when no tag was saved, as nf_ct_bridge_frag_restore()
does on the nf_conntrack_bridge path.
Fixes: efb6de9b4ba0 ("netfilter: bridge: forward IPv6 fragmented packets")
Fixes: 7885198861fc ("bridge: Implement vlan ingress/egress policy with PVID.")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
net/bridge/br_netfilter_hooks.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index fe8d2910dae21..2519c3820fb00 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -795,8 +795,12 @@ static int br_nf_push_frag_xmit(struct net *net, struct sock *sk, struct sk_buff
return 0;
}
- if (data->vlan_proto)
+ if (data->vlan_proto) {
__vlan_hwaccel_put_tag(skb, data->vlan_proto, data->vlan_tci);
+ } else if (skb_vlan_tag_present(skb)) {
+ /* Fragments reused from frag_list keep their ingress tag. */
+ __vlan_hwaccel_clear_tag(skb);
+ }
skb_copy_to_linear_data_offset(skb, -data->size, data->mac, data->size);
__skb_push(skb, data->encap_size);
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH nf v2 0/2] netfilter: preserve bridge egress VLAN tags
2026-10-04 21:24 [PATCH nf v2 0/2] netfilter: preserve bridge egress VLAN tags Andrea Parri
2026-10-04 21:24 ` [PATCH nf v2 1/2] netfilter: br_netfilter: restore VLAN tag on refragmented IPv6 packets Andrea Parri
2026-10-04 21:24 ` [PATCH nf v2 2/2] netfilter: br_netfilter: clear stale VLAN tag on refragmented packets Andrea Parri
@ 2026-10-04 21:29 ` netdev-bot+sinfo
2026-10-04 21:42 ` Andrea Parri
2 siblings, 1 reply; 5+ messages in thread
From: netdev-bot+sinfo @ 2026-10-04 21:29 UTC (permalink / raw)
To: Andrea Parri
Cc: Pablo Neira Ayuso, Florian Westphal, netfilter-devel,
Phil Sutter, Nikolay Aleksandrov, Ido Schimmel, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Bernhard Thaler, coreteam, bridge, netdev, linux-kernel
Hi!
This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:
- How the issue was discovered, e.g. hit in production, hit during
development, syzbot report, manual code inspection, LLM or static
analysis tool scan.
Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.
The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH nf v2 0/2] netfilter: preserve bridge egress VLAN tags
2026-10-04 21:29 ` [PATCH nf v2 0/2] netfilter: preserve bridge egress VLAN tags netdev-bot+sinfo
@ 2026-10-04 21:42 ` Andrea Parri
0 siblings, 0 replies; 5+ messages in thread
From: Andrea Parri @ 2026-10-04 21:42 UTC (permalink / raw)
To: netdev-bot+sinfo
Cc: Andrea Parri, Pablo Neira Ayuso, Florian Westphal,
netfilter-devel, Phil Sutter, Nikolay Aleksandrov, Ido Schimmel,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Bernhard Thaler, coreteam, bridge, netdev,
linux-kernel
On Sun, Oct 04, 2026 at 09:29:13PM +0000, netdev-bot+sinfo@kernel.org wrote:
> This is an automated message. This series looks like a fix, but its
> commit messages seem to be missing some information:
>
> - How the issue was discovered, e.g. hit in production, hit during
> development, syzbot report, manual code inspection, LLM or static
> analysis tool scan.
The issue fixed by the first patch was found by an LLM-assisted scan of
br_netfilter. The issue fixed by the second patch was reported by the
Sashiko AI review of v1 [1]. I then confirmed both with a reproducer
under virtme-ng; the reproduced symptoms are described in each commit
message.
[1] https://lore.kernel.org/all/179084999520.434549.4764913674478855382@kernel.org/
Thanks,
Andrea
^ permalink raw reply [flat|nested] 5+ messages in thread