Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next 00/15] Netfilter updates for net-next
@ 2026-11-13  3:12 Pablo Neira Ayuso
  2026-11-13  3:12 ` [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state Pablo Neira Ayuso
                   ` (14 more replies)
  0 siblings, 15 replies; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

Hi,

The following patchset contains Netfilter updates for net-next, mostly
including flowtable fixes and improvements:

1) Do not offload flows to hardware from the packet path if they are
   already being torn down.

2) Fix PMTU discovery in nf_flow_tunnel_ip6ip6_push(): update the PMTU
   before skb_scrub_packet() drops the dst, otherwise the update is
   silently lost. From Lorenzo Bianconi.

3) Fetch hardware stats when a flow is deleted, so the last stats sync
   with the conntrack entry is no longer missed. Update the mtk_ppe and
   airoha drivers to use this. From Lorenzo Bianconi and me.

4) Fix xt_time monthday computation for dates from 2040 on by using
   time64_to_tm() instead of the days_since_epoch[] table. This also
   shrinks the xt_time object by about 23%. From Fernando F. Mancera.

5) Do not offload multicast flows from nft_flow_offload, from Koral Ilgun.

6) Make the nf_hooks_lwtunnel sysctl read-only in non-init netns so
   unprivileged users cannot toggle the global static branch.
   From Pablo Neira Ayuso.

7) Purge the remaining flowtable entries directly when the flowtable
   is destroyed instead of going through the hardware offload
   workqueue, which may fail to allocate work.

8) Remove a no-op loop initializer in the nf_queue selftest to silence
   a clang -Wunused-value warning. From Sasha Levin.

9) Do not scrub the skb as crossing netns in the IPIP flowtable tx
   path, so the skb mark is preserved.

10) Do not let the DEV_PATH_TUN entry set h_source from the tunnel
    device's dev_addr. Before this patch, fast-path frames went out with
    part of the tunnel's local IP address as the Ethernet source MAC.
    This broke offloaded flows on DS-Lite over PPPoE. From Marek
    Majkowski.

11) Clear IPS_OFFLOAD_BIT only once, as part of the one-time teardown
    transition. Before this patch, a repeated teardown of an expired
    hardware flow could clear the bit owned by a replacement flow for
    the same conntrack entry. From João Silva.

12) Call skb_ensure_writable() in nfnetlink_queue before writing
    NFQA_L2HDR over the mac header. Before this patch, the write could
    corrupt cloned frames that bridge flooding sends to other ports.
    From Tamaki Yanagawa.

13) Add a UDP flowtable offload selftest for IPv4 and IPv6, with and
    without masquerade. From Jiri Peska.

Please, pull these changes from:

  git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next.git nf-next-26-11-13

Thanks.

----------------------------------------------------------------

The following changes since commit 573af801552999954caa8347e4490b04cf4763f0:

  Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net (2026-11-12 14:54:47 -0800)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next.git nf-next-26-11-13

for you to fetch changes up to 4810f60b9f0e9111cd0dcbfd2ca7a7c591a57464:

  selftests: netfilter: add UDP flowtable offload test (2026-11-13 04:06:51 +0100)

----------------------------------------------------------------
netfilter pull request 26-11-13

----------------------------------------------------------------
Fernando Fernandez Mancera (1):
      netfilter: xt_time: fix monthday computation for dates past 2039

Jiri Peska (1):
      selftests: netfilter: add UDP flowtable offload test

João Silva (1):
      netfilter: flowtable: do not clear the offload bit twice

Koral Ilgun (1):
      netfilter: nft_flow_offload: do not offload multicast

Lorenzo Bianconi (2):
      netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push()
      net: airoha: update stats on flow deletion

Marek Majkowski (1):
      netfilter: flowtable: tunnel device poisons h_source of fast-path frames

Pablo Neira Ayuso (6):
      netfilter: flowtable: do not hw offload flows in teardown state
      netfilter: flowtable: retrieve stats on flow deletion
      net: ethernet: mtk_ppe: update stats on flow deletion
      netfilter: lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non init-netns
      netfilter: flowtable: purge entries immediately when destroying the flowtable
      netfilter: flowtable: do not scrub as crossing netns

Sasha Levin (1):
      selftests: netfilter: nf_queue: drop the no-op loop initializer

Tamaki Yanagawa (1):
      netfilter: nfnetlink_queue: make skb writable before writing NFQA_L2HDR

 drivers/net/ethernet/airoha/airoha_ppe.c           |  31 +--
 drivers/net/ethernet/mediatek/mtk_ppe_offload.c    |  32 ++-
 include/net/netfilter/nf_flow_table.h              |   3 +-
 net/netfilter/nf_flow_table_core.c                 |  35 ++-
 net/netfilter/nf_flow_table_ip.c                   |   4 +-
 net/netfilter/nf_flow_table_offload.c              |  83 ++++---
 net/netfilter/nf_flow_table_path.c                 |   7 +-
 net/netfilter/nf_hooks_lwtunnel.c                  |  12 +-
 net/netfilter/nfnetlink_queue.c                    |   7 +-
 net/netfilter/nft_flow_offload.c                   |   5 +
 net/netfilter/xt_time.c                            |  82 +------
 tools/testing/selftests/net/netfilter/Makefile     |   1 +
 tools/testing/selftests/net/netfilter/nf_queue.c   |   2 +-
 .../selftests/net/netfilter/nft_flowtable_udp.sh   | 238 +++++++++++++++++++++
 14 files changed, 398 insertions(+), 144 deletions(-)
 create mode 100755 tools/testing/selftests/net/netfilter/nft_flowtable_udp.sh

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-13  3:17   ` netdev-bot+sinfo
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 02/15] netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push() Pablo Neira Ayuso
                   ` (13 subsequent siblings)
  14 siblings, 2 replies; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

Sashiko reports that refreshing hw offload from packet path is racy
since the flow might be already going away.

Since commit 7c549fb7eecd ("netfilter: flowtable: generalize pending
status bit"), GC skips flow entries that are still pending to be
offloaded. Let's just check for the teardown status bit after the
pending bit is set on to address this race.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_flow_table_offload.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index 4365859220e6..3a18fa48cadb 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -1120,6 +1120,12 @@ void nf_flow_offload_refresh(struct nf_flowtable *flowtable,
 	if (!offload)
 		return;
 
+	if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
+		clear_bit(NF_FLOW_PENDING, &flow->flags);
+		kfree(offload);
+		return;
+	}
+
 	flow_offload_queue_work(offload);
 }
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 02/15] netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push()
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
  2026-11-13  3:12 ` [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 03/15] netfilter: flowtable: retrieve stats on flow deletion Pablo Neira Ayuso
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>

nf_flow_tunnel_ip6ip6_push() invokes skb_scrub_packet() before updating
the PMTU. skb_scrub_packet() drops the skb dst, so
skb_dst_update_pmtu_no_confirm() finds no dst and the PMTU update is
silently lost.

Move the PMTU update before skb_scrub_packet() so the inner route is
updated with the MTU advertised by the tunnel dst, as done in
ip6_tnl_xmit().

Fixes: 93cf357fa797 ("netfilter: flowtable: Add IP6IP6 tx sw acceleration")
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_flow_table_ip.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index 19cafae748ab..644e70fd6773 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -699,10 +699,10 @@ static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,
 	if (err)
 		return err;
 
-	skb_scrub_packet(skb, true);
 	mtu = dst_mtu(dst) - sizeof(*ip6h);
 	mtu = max(mtu, IPV6_MIN_MTU);
 	skb_dst_update_pmtu_no_confirm(skb, mtu);
+	skb_scrub_packet(skb, true);
 
 	skb_push(skb, sizeof(*ip6h));
 	skb_reset_network_header(skb);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 03/15] netfilter: flowtable: retrieve stats on flow deletion
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
  2026-11-13  3:12 ` [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state Pablo Neira Ayuso
  2026-11-13  3:12 ` [PATCH net-next 02/15] netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push() Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 04/15] net: ethernet: mtk_ppe: update " Pablo Neira Ayuso
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

Retrieve stats too when deleting a flow from hardware. Apparently, the
existing approach is missing last stats sync with the conntrack entry.
Pass the stats object to the driver so it has a chance to populate it
at deletion time. Drivers need to be updated to use this new approach.

Reported-by: Valentin Berezhnoy <dreyksmail@gmail.com>
Closes: https://patch.msgid.link/netfilter-devel/20261021175030.4800-1-dreyksmail@gmail.com/
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_flow_table_offload.c | 54 ++++++++++++++++-----------
 1 file changed, 33 insertions(+), 21 deletions(-)

diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index 3a18fa48cadb..c302051489f2 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -928,12 +928,33 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable,
 	}
 	up_read(&flowtable->flow_block_lock);
 
-	if (cmd == FLOW_CLS_STATS)
+	if (stats)
 		memcpy(stats, &cls_flow.stats, sizeof(*stats));
 
 	return i;
 }
 
+static void flow_offload_update_stats(struct flow_offload_work *offload,
+				      struct flow_stats *stats)
+{
+	u64 lastused;
+
+	lastused = max_t(u64, stats[0].lastused, stats[1].lastused);
+	offload->flow->timeout = max_t(u64, offload->flow->timeout,
+				       lastused + flow_offload_get_timeout(offload->flow));
+
+	if (offload->flowtable->flags & NF_FLOWTABLE_COUNTER) {
+		if (stats[0].pkts)
+			nf_ct_acct_add(offload->flow->ct,
+				       FLOW_OFFLOAD_DIR_ORIGINAL,
+				       stats[0].pkts, stats[0].bytes);
+		if (stats[1].pkts)
+			nf_ct_acct_add(offload->flow->ct,
+				       FLOW_OFFLOAD_DIR_REPLY,
+				       stats[1].pkts, stats[1].bytes);
+	}
+}
+
 static int flow_offload_tuple_add(struct flow_offload_work *offload,
 				  struct nf_flow_rule *flow_rule,
 				  enum flow_offload_tuple_dir dir)
@@ -946,11 +967,12 @@ static int flow_offload_tuple_add(struct flow_offload_work *offload,
 }
 
 static void flow_offload_tuple_del(struct flow_offload_work *offload,
-				   enum flow_offload_tuple_dir dir)
+				   enum flow_offload_tuple_dir dir,
+				   struct flow_stats *stats)
 {
 	nf_flow_offload_tuple(offload->flowtable, offload->flow, NULL, dir,
 			      offload->flowtable->priority,
-			      FLOW_CLS_DESTROY, NULL,
+			      FLOW_CLS_DESTROY, stats,
 			      &offload->flowtable->flow_block.cb_list);
 }
 
@@ -991,10 +1013,15 @@ static void flow_offload_work_add(struct flow_offload_work *offload)
 
 static void flow_offload_work_del(struct flow_offload_work *offload)
 {
+	struct flow_stats stats[FLOW_OFFLOAD_DIR_MAX] = {};
+
 	clear_bit(IPS_HW_OFFLOAD_BIT, &offload->flow->ct->status);
-	flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL);
+	flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL, &stats[0]);
 	if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
-		flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY);
+		flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY, &stats[1]);
+
+	flow_offload_update_stats(offload, stats);
+
 	set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
 }
 
@@ -1011,27 +1038,12 @@ static void flow_offload_tuple_stats(struct flow_offload_work *offload,
 static void flow_offload_work_stats(struct flow_offload_work *offload)
 {
 	struct flow_stats stats[FLOW_OFFLOAD_DIR_MAX] = {};
-	u64 lastused;
 
 	flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_ORIGINAL, &stats[0]);
 	if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
 		flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_REPLY,
 					 &stats[1]);
-
-	lastused = max_t(u64, stats[0].lastused, stats[1].lastused);
-	offload->flow->timeout = max_t(u64, offload->flow->timeout,
-				       lastused + flow_offload_get_timeout(offload->flow));
-
-	if (offload->flowtable->flags & NF_FLOWTABLE_COUNTER) {
-		if (stats[0].pkts)
-			nf_ct_acct_add(offload->flow->ct,
-				       FLOW_OFFLOAD_DIR_ORIGINAL,
-				       stats[0].pkts, stats[0].bytes);
-		if (stats[1].pkts)
-			nf_ct_acct_add(offload->flow->ct,
-				       FLOW_OFFLOAD_DIR_REPLY,
-				       stats[1].pkts, stats[1].bytes);
-	}
+	flow_offload_update_stats(offload, stats);
 }
 
 static void flow_offload_work_handler(struct work_struct *work)
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 04/15] net: ethernet: mtk_ppe: update stats on flow deletion
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (2 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 03/15] netfilter: flowtable: retrieve stats on flow deletion Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 05/15] net: airoha: " Pablo Neira Ayuso
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

Update this driver to use the new approach to fetch stats on flow
deletion, introduced by ("netfilter: flowtable: retrieve stats on flow
deletion").

Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 .../net/ethernet/mediatek/mtk_ppe_offload.c   | 32 ++++++++++++-------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
index 99b28aaa7cc4..8ca694bb5e1b 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -526,6 +526,24 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
 	return err;
 }
 
+static void
+mtk_flow_offload_update_stats(struct mtk_eth *eth, struct flow_cls_offload *f,
+			      struct mtk_flow_entry *entry)
+{
+	struct mtk_foe_accounting diff;
+	u32 idle;
+
+	idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
+	f->stats.lastused = jiffies - idle * HZ;
+
+	if (entry->hash != 0xFFFF &&
+	    mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
+				  &diff)) {
+		f->stats.pkts += diff.packets;
+		f->stats.bytes += diff.bytes;
+	}
+}
+
 static int
 mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
 {
@@ -536,6 +554,8 @@ mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
 	if (!entry)
 		return -ENOENT;
 
+	mtk_flow_offload_update_stats(eth, f, entry);
+
 	mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry);
 	rhashtable_remove_fast(&eth->flow_table, &entry->node,
 			       mtk_flow_ht_params);
@@ -550,23 +570,13 @@ static int
 mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
 {
 	struct mtk_flow_entry *entry;
-	struct mtk_foe_accounting diff;
-	u32 idle;
 
 	entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
 				  mtk_flow_ht_params);
 	if (!entry)
 		return -ENOENT;
 
-	idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
-	f->stats.lastused = jiffies - idle * HZ;
-
-	if (entry->hash != 0xFFFF &&
-	    mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
-				  &diff)) {
-		f->stats.pkts += diff.packets;
-		f->stats.bytes += diff.bytes;
-	}
+	mtk_flow_offload_update_stats(eth, f, entry);
 
 	return 0;
 }
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 05/15] net: airoha: update stats on flow deletion
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (3 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 04/15] net: ethernet: mtk_ppe: update " Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 06/15] netfilter: xt_time: fix monthday computation for dates past 2039 Pablo Neira Ayuso
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>

Update this driver to use the new approach to fetch stats on flow
deletion, introduced by ("netfilter: flowtable: retrieve stats on flow
deletion").

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/ethernet/airoha/airoha_ppe.c | 31 +++++++++++++++---------
 1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index f5369c45766d..af8c7fe9b7f0 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1285,6 +1285,23 @@ static int airoha_ppe_flow_offload_replace(struct airoha_eth *eth,
 	return err;
 }
 
+static void airoha_ppe_flow_update_stats(struct airoha_eth *eth,
+					 struct flow_cls_offload *f,
+					 struct airoha_flow_table_entry *e)
+{
+	u32 idle = airoha_ppe_entry_idle_time(eth->ppe, e);
+
+	f->stats.lastused = jiffies - idle * HZ;
+	if (e->hash != 0xffff) {
+		struct airoha_foe_stats64 stats = {};
+
+		airoha_ppe_foe_entry_get_stats(eth->ppe, e->hash, &stats);
+		f->stats.pkts += (stats.packets - e->stats.packets);
+		f->stats.bytes += (stats.bytes - e->stats.bytes);
+		e->stats = stats;
+	}
+}
+
 static int airoha_ppe_flow_offload_destroy(struct airoha_eth *eth,
 					   struct flow_cls_offload *f)
 {
@@ -1295,6 +1312,7 @@ static int airoha_ppe_flow_offload_destroy(struct airoha_eth *eth,
 	if (!e)
 		return -ENOENT;
 
+	airoha_ppe_flow_update_stats(eth, f, e);
 	airoha_ppe_foe_flow_remove_entry(eth->ppe, e);
 	rhashtable_remove_fast(&eth->flow_table, &e->node,
 			       airoha_flow_table_params);
@@ -1342,24 +1360,13 @@ static int airoha_ppe_flow_offload_stats(struct airoha_eth *eth,
 					 struct flow_cls_offload *f)
 {
 	struct airoha_flow_table_entry *e;
-	u32 idle;
 
 	e = rhashtable_lookup(&eth->flow_table, &f->cookie,
 			      airoha_flow_table_params);
 	if (!e)
 		return -ENOENT;
 
-	idle = airoha_ppe_entry_idle_time(eth->ppe, e);
-	f->stats.lastused = jiffies - idle * HZ;
-
-	if (e->hash != 0xffff) {
-		struct airoha_foe_stats64 stats = {};
-
-		airoha_ppe_foe_entry_get_stats(eth->ppe, e->hash, &stats);
-		f->stats.pkts += (stats.packets - e->stats.packets);
-		f->stats.bytes += (stats.bytes - e->stats.bytes);
-		e->stats = stats;
-	}
+	airoha_ppe_flow_update_stats(eth, f, e);
 
 	return 0;
 }
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 06/15] netfilter: xt_time: fix monthday computation for dates past 2039
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (4 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 05/15] net: airoha: " Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 07/15] netfilter: nft_flow_offload: do not offload multicast Pablo Neira Ayuso
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Fernando Fernandez Mancera <fmancera@suse.de>

iocaltime_3() derives the year from days_since_epoch[], which only has
entries up to 2039. For any date from 2040-01-01 on, the lookup stays on
the 2039 entry, so the day-of-year offset is 365 or more. That value is
used to compute r->monthday, which ends up larger than 31 (32 on
2040-01-01, and it wraps in the u8 later in the year).

Use time64_to_tm() instead of the table walk. It is always built in,
handles any time64_t, and always returns tm_mday in 1-31, so the table
and its 2039 limit go away. The result for 1970-2039 is unchanged.

Only the real-time clock reaches localtime_3(), so this is not
triggerable before the clock passes 2040-01-01 UTC.

This significantly reduces the size of xt_time object.

add/remove: 7/10 grow/shrink: 0/1 up/down: 143/-490 (-347)
Total: Before=1493, After=1146, chg -23.24%

Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/xt_time.c | 82 +++--------------------------------------
 1 file changed, 6 insertions(+), 76 deletions(-)

diff --git a/net/netfilter/xt_time.c b/net/netfilter/xt_time.c
index 2065fce8ef81..f0c4c1d1bc4a 100644
--- a/net/netfilter/xt_time.c
+++ b/net/netfilter/xt_time.c
@@ -13,8 +13,8 @@
 
 #include <linux/ktime.h>
 #include <linux/module.h>
-#include <linux/rtc.h>
 #include <linux/skbuff.h>
+#include <linux/time.h>
 #include <linux/types.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_time.h>
@@ -31,38 +31,7 @@ struct xtm {
 
 extern struct timezone sys_tz; /* ouch */
 
-static const u_int16_t days_since_year[] = {
-	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
-};
-
-static const u_int16_t days_since_leapyear[] = {
-	0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
-};
-
-/*
- * Since time progresses forward, it is best to organize this array in reverse,
- * to minimize lookup time.
- */
-enum {
-	DSE_FIRST = 2039,
-	SECONDS_PER_DAY = 86400,
-};
-static const u_int16_t days_since_epoch[] = {
-	/* 2039 - 2030 */
-	25202, 24837, 24472, 24106, 23741, 23376, 23011, 22645, 22280, 21915,
-	/* 2029 - 2020 */
-	21550, 21184, 20819, 20454, 20089, 19723, 19358, 18993, 18628, 18262,
-	/* 2019 - 2010 */
-	17897, 17532, 17167, 16801, 16436, 16071, 15706, 15340, 14975, 14610,
-	/* 2009 - 2000 */
-	14245, 13879, 13514, 13149, 12784, 12418, 12053, 11688, 11323, 10957,
-	/* 1999 - 1990 */
-	10592, 10227, 9862, 9496, 9131, 8766, 8401, 8035, 7670, 7305,
-	/* 1989 - 1980 */
-	6940, 6574, 6209, 5844, 5479, 5113, 4748, 4383, 4018, 3652,
-	/* 1979 - 1970 */
-	3287, 2922, 2557, 2191, 1826, 1461, 1096, 730, 365, 0,
-};
+#define SECONDS_PER_DAY 86400
 
 /*
  * Each network packet has a (nano)seconds-since-the-epoch (SSTE) timestamp.
@@ -102,51 +71,12 @@ static inline void localtime_2(struct xtm *r, time64_t time)
 
 static void localtime_3(struct xtm *r, time64_t time)
 {
-	unsigned int year, i, w = r->dse;
-
-	/*
-	 * In each year, a certain number of days-since-the-epoch have passed.
-	 * Find the year that is closest to said days.
-	 *
-	 * Consider, for example, w=21612 (2029-03-04). Loop will abort on
-	 * dse[i] <= w, which happens when dse[i] == 21550. This implies
-	 * year == 2009. w will then be 62.
-	 */
-	for (i = 0, year = DSE_FIRST; days_since_epoch[i] > w;
-	    ++i, --year)
-		/* just loop */;
-
-	w -= days_since_epoch[i];
+	struct tm tm;
 
-	/*
-	 * By now we have the current year, and the day of the year.
-	 * r->yearday = w;
-	 *
-	 * On to finding the month (like above). In each month, a certain
-	 * number of days-since-New Year have passed, and find the closest
-	 * one.
-	 *
-	 * Consider w=62 (in a non-leap year). Loop will abort on
-	 * dsy[i] < w, which happens when dsy[i] == 31+28 (i == 2).
-	 * Concludes i == 2, i.e. 3rd month => March.
-	 *
-	 * (A different approach to use would be to subtract a monthlength
-	 * from w repeatedly while counting.)
-	 */
-	if (is_leap_year(year)) {
-		/* use days_since_leapyear[] in a leap year */
-		for (i = ARRAY_SIZE(days_since_leapyear) - 1;
-		    i > 0 && days_since_leapyear[i] > w; --i)
-			/* just loop */;
-		r->monthday = w - days_since_leapyear[i] + 1;
-	} else {
-		for (i = ARRAY_SIZE(days_since_year) - 1;
-		    i > 0 && days_since_year[i] > w; --i)
-			/* just loop */;
-		r->monthday = w - days_since_year[i] + 1;
-	}
+	time64_to_tm(time, 0, &tm);
 
-	r->month    = i + 1;
+	r->monthday = tm.tm_mday;	/* 1-31 */
+	r->month    = tm.tm_mon + 1;	/* 1-12 */
 }
 
 static bool
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 07/15] netfilter: nft_flow_offload: do not offload multicast
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (5 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 06/15] netfilter: xt_time: fix monthday computation for dates past 2039 Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 08/15] netfilter: lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non init-netns Pablo Neira Ayuso
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Koral Ilgun <koral.ilgun@adtran.com>

A flow holds one output per direction, which it resolves when it is
added. The outputs of a multicast route change when listeners join
or leave. So a multicast flow in the flowtable continues to send to
the output that it resolved, also after the last listener left, and
the counters of the multicast routing stop.

A ruleset can keep multicast out with a match before the flow
offload statement. Without one, IPv4 multicast stays out of the
flowtable only because the reverse route lookup in nft_flow_route()
refuses a multicast source address. The IPv6 route lookup has no such
check, so IPv6 multicast that ip6mr forwards goes into the flowtable.

Skip a multicast destination for both families in the flow offload
expression itself, so that the result no longer depends on the
ruleset or on the route lookup.

Signed-off-by: Koral Ilgun <koral.ilgun@adtran.com>
Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nft_flow_offload.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index d3c5651dd699..530e20c7fd45 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -10,6 +10,7 @@
 #include <linux/netfilter/nf_conntrack_common.h>
 #include <linux/netfilter/nf_tables.h>
 #include <net/ip.h>
+#include <net/ipv6.h>
 #include <net/flow.h>
 #include <net/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables_core.h>
@@ -33,6 +34,10 @@ static bool nft_flow_offload_skip(struct sk_buff *skb, int family)
 
 		if (unlikely(opt->optlen))
 			return true;
+
+		return ipv4_is_multicast(ip_hdr(skb)->daddr);
+	} else if (family == NFPROTO_IPV6) {
+		return ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr);
 	}
 
 	return false;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 08/15] netfilter: lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non init-netns
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (6 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 07/15] netfilter: nft_flow_offload: do not offload multicast Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 09/15] netfilter: flowtable: purge entries immediately when destroying the flowtable Pablo Neira Ayuso
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

Expose nf_hooks_lwtunnel sysctl with 0444 mode from non init-netns
instead of 0644, sashiko reports this is exposing a global static branch
to modification by unprivileged users.

Fixes: 7a3f5b0de364 ("netfilter: add netfilter hooks to SRv6 data plane")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_hooks_lwtunnel.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/net/netfilter/nf_hooks_lwtunnel.c b/net/netfilter/nf_hooks_lwtunnel.c
index 40c6fce14a63..7e2ee8677f1f 100644
--- a/net/netfilter/nf_hooks_lwtunnel.c
+++ b/net/netfilter/nf_hooks_lwtunnel.c
@@ -68,14 +68,18 @@ static int __net_init nf_lwtunnel_net_init(struct net *net)
 {
 	const struct ctl_table *table;
 	struct ctl_table_header *hdr;
+	struct ctl_table *dup;
 
 	table = nf_lwtunnel_sysctl_table;
 	if (!net_eq(net, &init_net)) {
-		table = kmemdup(nf_lwtunnel_sysctl_table,
-				sizeof(nf_lwtunnel_sysctl_table),
-				GFP_KERNEL_ACCOUNT);
-		if (!table)
+		dup = kmemdup(nf_lwtunnel_sysctl_table,
+			      sizeof(nf_lwtunnel_sysctl_table),
+			      GFP_KERNEL_ACCOUNT);
+		if (!dup)
 			goto err_alloc;
+
+		dup->mode = 0444;
+		table = dup;
 	}
 
 	hdr = register_net_sysctl_sz(net, "net/netfilter", table,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 09/15] netfilter: flowtable: purge entries immediately when destroying the flowtable
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (7 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 08/15] netfilter: lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non init-netns Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 10/15] selftests: netfilter: nf_queue: drop the no-op loop initializer Pablo Neira Ayuso
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

Do not use hardware offload workqueue to purge the remaining flow
entries when the flowtable is being destroyed. Instead, iterate over the
flowtable to purge the remaining flow offload entries. Work memory
allocations can fail, leaving us with either waiting indefinitely until
work can be allocated or leaving entries in the hardware.

Given it is possible to encounter either the original or reply direction
when walking down the rhashtable in the destroy path, use the
NF_FLOW_PENDING bit to indicate that the first tuplehash has been
already visited, so this flow can be safely released.

Fixes: c921ffe85333 ("netfilter: flowtable: Fix flushing of offloaded flows on free")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/net/netfilter/nf_flow_table.h |  3 ++-
 net/netfilter/nf_flow_table_core.c    | 29 +++++++++++++++++++++++----
 net/netfilter/nf_flow_table_offload.c | 23 +++++++++++++--------
 3 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index 5b22d31cb140..9bb46457c192 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -375,7 +375,8 @@ void nf_flow_offload_stats(struct nf_flowtable *flowtable,
 			   struct flow_offload *flow);
 
 void nf_flow_table_offload_flush(struct nf_flowtable *flowtable);
-void nf_flow_table_offload_flush_cleanup(struct nf_flowtable *flowtable);
+void nf_flow_table_offload_purge(struct nf_flowtable *flowtable,
+				 struct flow_offload *flow);
 
 int nf_flow_table_offload_setup(struct nf_flowtable *flowtable,
 				struct net_device *dev,
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 2aa2647ed9df..733107180d56 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -770,6 +770,29 @@ void nf_flow_table_cleanup(struct net_device *dev)
 }
 EXPORT_SYMBOL_GPL(nf_flow_table_cleanup);
 
+static void nf_flow_table_do_purge(void *ptr, void *arg)
+{
+	struct flow_offload_tuple_rhash *tuplehash = ptr;
+	struct nf_flowtable *flow_table = arg;
+	struct flow_offload *flow;
+	int dir;
+
+	dir = tuplehash->tuple.dir;
+	flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+
+	/* Let the second tuplehash that is seen release the flow. */
+	if (!test_and_set_bit(NF_FLOW_PENDING, &flow->flags))
+		return;
+
+	flow_offload_teardown(flow);
+
+	if (nf_flowtable_hw_offload(flow_table) &&
+	    !test_bit(NF_FLOW_HW_DEAD, &flow->flags))
+		nf_flow_table_offload_purge(flow_table, flow);
+
+	flow_offload_free(flow);
+}
+
 void nf_flow_table_free(struct nf_flowtable *flow_table)
 {
 	mutex_lock(&flowtable_lock);
@@ -779,10 +802,8 @@ void nf_flow_table_free(struct nf_flowtable *flow_table)
 	cancel_delayed_work_sync(&flow_table->gc_work);
 	nf_flow_table_offload_flush(flow_table);
 	/* ... no more pending work after this stage ... */
-	nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
-	nf_flow_table_gc_run(flow_table);
-	nf_flow_table_offload_flush_cleanup(flow_table);
-	rhashtable_destroy(&flow_table->rhashtable);
+	rhashtable_free_and_destroy(&flow_table->rhashtable,
+				    nf_flow_table_do_purge, flow_table);
 }
 EXPORT_SYMBOL_GPL(nf_flow_table_free);
 
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index c302051489f2..11434cfb105b 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -1181,14 +1181,6 @@ void nf_flow_offload_stats(struct nf_flowtable *flowtable,
 	flow_offload_queue_work(offload);
 }
 
-void nf_flow_table_offload_flush_cleanup(struct nf_flowtable *flowtable)
-{
-	if (nf_flowtable_hw_offload(flowtable)) {
-		flush_workqueue(nf_flow_offload_del_wq);
-		nf_flow_table_gc_run(flowtable);
-	}
-}
-
 void nf_flow_table_offload_flush(struct nf_flowtable *flowtable)
 {
 	if (nf_flowtable_hw_offload(flowtable)) {
@@ -1198,6 +1190,21 @@ void nf_flow_table_offload_flush(struct nf_flowtable *flowtable)
 	}
 }
 
+void nf_flow_table_offload_purge(struct nf_flowtable *flowtable,
+				 struct flow_offload *flow)
+{
+	struct flow_offload_work offload = {
+		.cmd		= FLOW_CLS_DESTROY,
+		.flowtable	= flowtable,
+		.flow		= flow,
+	};
+
+	if (nf_flow_offload_unsupported(flow))
+		return;
+
+	flow_offload_work_del(&offload);
+}
+
 static int nf_flow_table_block_setup(struct nf_flowtable *flowtable,
 				     struct flow_block_offload *bo,
 				     enum flow_block_command cmd)
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 10/15] selftests: netfilter: nf_queue: drop the no-op loop initializer
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (8 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 09/15] netfilter: flowtable: purge entries immediately when destroying the flowtable Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-13  3:12 ` [PATCH net-next 11/15] netfilter: flowtable: do not scrub as crossing netns Pablo Neira Ayuso
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Sasha Levin <sashal@kernel.org>

The out-of-order verdict loop starts with "for (ooo_count; ...", and
clang warns about the unused expression:

  nf_queue.c:354:10: warning: expression result unused [-Wunused-value]

Leave the initializer empty.

Signed-off-by: Sasha Levin <sashal@kernel.org>
Reviewed-by: Hangbin Liu <liuhangbin@kylinos.cn>
Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 tools/testing/selftests/net/netfilter/nf_queue.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/net/netfilter/nf_queue.c b/tools/testing/selftests/net/netfilter/nf_queue.c
index cca6c3cfa9e4..bc0b2f601a0a 100644
--- a/tools/testing/selftests/net/netfilter/nf_queue.c
+++ b/tools/testing/selftests/net/netfilter/nf_queue.c
@@ -351,7 +351,7 @@ static int mainloop(void)
 		if (opts.out_of_order) {
 			ooo_ids[ooo_count] = id;
 			if (ooo_count >= 15) {
-				for (ooo_count; ooo_count >= 0; ooo_count--) {
+				for (; ooo_count >= 0; ooo_count--) {
 					nlh = nfq_build_verdict(buf, ooo_ids[ooo_count],
 								opts.queue_num, opts.verdict);
 					if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 11/15] netfilter: flowtable: do not scrub as crossing netns
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (9 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 10/15] selftests: netfilter: nf_queue: drop the no-op loop initializer Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 12/15] netfilter: flowtable: tunnel device poisons h_source of fast-path frames Pablo Neira Ayuso
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

IPIP tunneling support does not result in crossing a netns, therefore,
adopt the more conservative variant of skb_scrub_packet() by specifying
!xnet, keep the existing skb mark in place.

Fixes: d30301ba4b07 ("netfilter: flowtable: Add IPIP tx sw acceleration")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_flow_table_ip.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index 644e70fd6773..001abfae5a1f 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -630,7 +630,7 @@ static int nf_flow_tunnel_ip_push(struct net *net, struct sk_buff *skb,
 	if (err)
 		return err;
 
-	skb_scrub_packet(skb, true);
+	skb_scrub_packet(skb, false);
 	skb_clear_hash_if_not_l4(skb);
 
 	/* Push down and install the IP header. */
@@ -702,7 +702,7 @@ static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,
 	mtu = dst_mtu(dst) - sizeof(*ip6h);
 	mtu = max(mtu, IPV6_MIN_MTU);
 	skb_dst_update_pmtu_no_confirm(skb, mtu);
-	skb_scrub_packet(skb, true);
+	skb_scrub_packet(skb, false);
 
 	skb_push(skb, sizeof(*ip6h));
 	skb_reset_network_header(skb);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 12/15] netfilter: flowtable: tunnel device poisons h_source of fast-path frames
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (10 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 11/15] netfilter: flowtable: do not scrub as crossing netns Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  2026-11-13  3:12 ` [PATCH net-next 13/15] netfilter: flowtable: do not clear the offload bit twice Pablo Neira Ayuso
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Marek Majkowski <marek@lysk.ai>

When a flow enters an IP tunnel, the forward path discovered by
dev_fill_forward_path() starts with a DEV_PATH_TUN entry for the tunnel
device, followed by whatever carries the outer packet.
nft_dev_path_info() handles DEV_PATH_TUN in the same switch case as the
Ethernet-like path types and copies path->dev->dev_addr into
info->h_source if it is still unset. For a tunnel device dev_addr is not
an Ethernet address: ip6_tunnel stores the 16-byte local IPv6 there and
ip_tunnel the 4-byte local IPv4. The tunnel entry is the first one on
the path, so its "address" wins and poisons h_source: every fast-path
frame is transmitted with the first bytes of the tunnel local IP address
as the Ethernet source MAC.

Seen on a DS-Lite setup, LAN -> ip6tnl (IPv4 inside IPv6) -> PPPoE ->
802.1Q VLAN -> Ethernet, with conntrack flows offloaded through an
nftables flowtable ('flow add @ft'). The first packets of a flow, still
on the slow path, and the fast-path packets of the same flow differ only
in the Ethernet source address:

  slow path:  d8:58:d7:00:2e:bd > 80:ac:ac:39:ef:c2, 802.1Q vlan 35,
              PPPoE [ses 0x454] IP6 2a01:110f:4328:c400::1 >
              2a01:1000:0:1::2216 next-header IPIP, 192.0.0.2.40010 >
              1.1.1.1.53
  fast path:  2a:01:11:0f:43:28 > 80:ac:ac:39:ef:c2, same frame otherwise

As you can see 2a:01:11:0f:43:28 is the start of 2a01:110f:4328:c400::1,
the tunnel local address. The PPPoE endpoint binds the session to the
subscriber MAC and silently drops the frames, the effect is that every
TCP/UDP flow dies the moment it is offloaded, while ICMP, which is never
offloaded, keeps working. Received traffic is unaffected.

Do not let DEV_PATH_TUN poison h_source: skip the assignment for it. The
real link-layer device appears later on the same path and provides the
correct address; for a path with no Ethernet device the address stays
zero as before.

Fixes: ab427db17885 ("netfilter: flowtable: Add IPIP rx sw acceleration")
Signed-off-by: Marek Majkowski <marek@lysk.ai>
Reviewed-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_flow_table_path.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index 841f099434d9..4ca6e09c7f8e 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -121,7 +121,12 @@ static int nft_dev_path_info(struct net_device_path_stack *stack,
 		case DEV_PATH_PPPOE:
 		case DEV_PATH_TUN:
 			info->dev = path->dev;
-			if (is_zero_ether_addr(info->h_source))
+			/* A tunnel device has no Ethernet address: its dev_addr
+			 * is the tunnel local IP address. Take h_source from the
+			 * real link-layer device further down the path.
+			 */
+			if (path->type != DEV_PATH_TUN &&
+			    is_zero_ether_addr(info->h_source))
 				memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
 
 			if (path->type == DEV_PATH_ETHERNET ||
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 13/15] netfilter: flowtable: do not clear the offload bit twice
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (11 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 12/15] netfilter: flowtable: tunnel device poisons h_source of fast-path frames Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-13  3:12 ` [PATCH net-next 14/15] netfilter: nfnetlink_queue: make skb writable before writing NFQA_L2HDR Pablo Neira Ayuso
  2026-11-13  3:12 ` [PATCH net-next 15/15] selftests: netfilter: add UDP flowtable offload test Pablo Neira Ayuso
  14 siblings, 0 replies; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: João Silva <jgc3silva@gmail.com>

An expired hardware flow remains in the flowtable until its asynchronous
hardware deletion completes. Teardown clears IPS_OFFLOAD_BIT, so a packet
can create a replacement flow referencing the same conntrack entry while
the old flow is still waiting for deletion.

The next GC pass calls flow_offload_teardown() on the expired old flow
again, clearing the replacement's offload bit. More packets can then
create additional flows for the same conntrack entry. A pending hardware
work item can also delay deletion and expose the same repeated teardown.

Commit d2d31ea8cd80 ("netfilter: conntrack: fix erronous removal of
offload bit") closed this window for software flows by unlinking them in
the same GC pass and made the conntrack state fixup one-shot, but
hardware flows stay linked until NF_FLOW_HW_DEAD is set.

Make clearing IPS_OFFLOAD_BIT part of the existing one-time teardown
transition, alongside the conntrack state fixup. Subsequent teardown calls
must not alter the offload state now owned by a replacement flow.

Fixes: e5eaac2beb54 ("netfilter: flowtable: fix TCP flow teardown")
Signed-off-by: João Silva <jgc3silva@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_flow_table_core.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 733107180d56..d5652e45ee04 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -398,9 +398,11 @@ static void flow_offload_del(struct nf_flowtable *flow_table,
 
 void flow_offload_teardown(struct flow_offload *flow)
 {
+	if (test_and_set_bit(NF_FLOW_TEARDOWN, &flow->flags))
+		return;
+
 	clear_bit(IPS_OFFLOAD_BIT, &flow->ct->status);
-	if (!test_and_set_bit(NF_FLOW_TEARDOWN, &flow->flags))
-		flow_offload_fixup_ct(flow);
+	flow_offload_fixup_ct(flow);
 }
 EXPORT_SYMBOL_GPL(flow_offload_teardown);
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 14/15] netfilter: nfnetlink_queue: make skb writable before writing NFQA_L2HDR
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (12 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 13/15] netfilter: flowtable: do not clear the offload bit twice Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-13  3:12 ` [PATCH net-next 15/15] selftests: netfilter: add UDP flowtable offload test Pablo Neira Ayuso
  14 siblings, 0 replies; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Tamaki Yanagawa <ty@000ty.net>

nfqa_parse_bridge() copies the NFQA_L2HDR attribute over the mac header
of the queued skb in place. Bridge flooding queues clones that share
their data with the copies sent to the other ports, so the rewritten
header also shows up on frames that never went through the queue, and
is changed while those may already be in transmission. Call
skb_ensure_writable() before the copy. At the bridge hooks skb->data
points to the network header, so the mac header sits in the headroom,
which skb_clone_writable() already takes into account: a write length
of zero is enough, and a clone that owns its header area (nohdr) is
left alone instead of being copied.

Fixes: 8d45ff22f1b4 ("netfilter: bridge: nf queue verdict to use NFQA_VLAN and NFQA_L2HDR")
Signed-off-by: Tamaki Yanagawa <ty@000ty.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nfnetlink_queue.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index a8aa7ad832c3..862d2a762106 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -1830,10 +1830,15 @@ static int nfqa_parse_bridge(struct nf_queue_entry *entry,
 
 		if (mac_header_len != nla_len(nfqa[NFQA_L2HDR]))
 			return -EINVAL;
-		else if (mac_header_len > 0)
+
+		if (mac_header_len > 0) {
+			if (skb_ensure_writable(entry->skb, 0))
+				return -ENOMEM;
+
 			memcpy(skb_mac_header(entry->skb),
 			       nla_data(nfqa[NFQA_L2HDR]),
 			       mac_header_len);
+		}
 	}
 
 	return 0;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH net-next 15/15] selftests: netfilter: add UDP flowtable offload test
  2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
                   ` (13 preceding siblings ...)
  2026-11-13  3:12 ` [PATCH net-next 14/15] netfilter: nfnetlink_queue: make skb writable before writing NFQA_L2HDR Pablo Neira Ayuso
@ 2026-11-13  3:12 ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  14 siblings, 1 reply; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:12 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Jiri Peska <jpeska@redhat.com>

nft_flowtable.sh offloads only TCP, so the UDP flowtable paths never
run: the UDP tuple lookup, the UDP checksum update, IPv6 source NAT,
and nf_ct_acct_update behind the flowtable counter flag.

Add an echo test for IPv4 and IPv6, with and without masquerade. The
payload must come back intact, the forward chain must see only the
handshake, and conntrack must mark the flow OFFLOAD and account every
datagram.

Signed-off-by: Jiri Peska <jpeska@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 .../testing/selftests/net/netfilter/Makefile  |   1 +
 .../net/netfilter/nft_flowtable_udp.sh        | 238 ++++++++++++++++++
 2 files changed, 239 insertions(+)
 create mode 100755 tools/testing/selftests/net/netfilter/nft_flowtable_udp.sh

diff --git a/tools/testing/selftests/net/netfilter/Makefile b/tools/testing/selftests/net/netfilter/Makefile
index df3c20c90f5d..1bc7f7a75f1e 100644
--- a/tools/testing/selftests/net/netfilter/Makefile
+++ b/tools/testing/selftests/net/netfilter/Makefile
@@ -30,6 +30,7 @@ TEST_PROGS := \
 	nft_fib.sh \
 	nft_fib_nexthop.sh \
 	nft_flowtable.sh \
+	nft_flowtable_udp.sh \
 	nft_interface_stress.sh \
 	nft_meta.sh \
 	nft_nat.sh \
diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable_udp.sh b/tools/testing/selftests/net/netfilter/nft_flowtable_udp.sh
new file mode 100755
index 000000000000..e0f05c76bd8e
--- /dev/null
+++ b/tools/testing/selftests/net/netfilter/nft_flowtable_udp.sh
@@ -0,0 +1,238 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# nft_flowtable.sh offloads TCP only. These UDP paths therefore stay at
+# zero coverage in that run:
+#
+#   nf_flow_tuple_ip / nf_flow_tuple_ipv6          IPPROTO_UDP
+#   nf_flow_nat_ip_udp / nf_flow_nat_port_udp      IPv4 NAT checksum
+#   nf_flow_snat_ipv6 / nf_flow_dnat_ipv6          IPv6 NAT, including UDP
+#   flow_offload_get_timeout / flow_offload_fixup_ct for UDP
+#   nf_ct_acct_update when the flowtable counter flag is set
+#
+# Forward a burst of UDP echoes, with and without masquerade. The forward
+# hook must see only the handshake: the rest is carried by the ingress
+# flowtable, and conntrack accounting must include those packets.
+
+source lib.sh
+
+ret=0
+port=12345
+count=32
+
+checktool "nft --version" "run test without nft"
+checktool "socat -h" "run test without socat"
+
+setup_ns ns1 nsr ns2
+
+cleanup() {
+	cleanup_all_ns
+}
+trap cleanup EXIT
+
+# Disable DAD before the devices exist. A link-local still running DAD
+# delays the first IPv6 packet by about two seconds.
+for ns in "$ns1" "$ns2" "$nsr"; do
+	ip netns exec "$ns" sysctl -wq net.ipv6.conf.default.accept_dad=0
+	ip netns exec "$ns" sysctl -wq net.ipv6.conf.default.dad_transmits=0
+	ip netns exec "$ns" sysctl -wq net.ipv6.conf.all.accept_dad=0
+done
+
+ip link add veth0 netns "$nsr" type veth peer name eth0 netns "$ns1"
+ip link add veth1 netns "$nsr" type veth peer name eth0 netns "$ns2"
+
+ip -net "$nsr" link set veth0 up
+ip -net "$nsr" link set veth1 up
+ip -net "$ns1" link set eth0 up
+ip -net "$ns2" link set eth0 up
+
+ip netns exec "$nsr" sysctl -wq net.ipv4.ip_forward=1
+ip netns exec "$nsr" sysctl -wq net.ipv6.conf.all.forwarding=1
+
+ip -net "$nsr" addr add 10.0.1.1/24 dev veth0
+ip -net "$nsr" addr add 10.0.2.1/24 dev veth1
+ip -net "$ns1" addr add 10.0.1.99/24 dev eth0
+ip -net "$ns2" addr add 10.0.2.99/24 dev eth0
+
+ip -net "$nsr" addr add dead:1::1/64 dev veth0 nodad
+ip -net "$nsr" addr add dead:2::1/64 dev veth1 nodad
+ip -net "$ns1" addr add dead:1::99/64 dev eth0 nodad
+ip -net "$ns2" addr add dead:2::99/64 dev eth0 nodad
+
+ip -net "$ns1" route add default via 10.0.1.1
+ip -net "$ns2" route add default via 10.0.2.1
+ip -6 -net "$ns1" route add default via dead:1::1
+ip -6 -net "$ns2" route add default via dead:2::1
+
+if ! ip netns exec "$nsr" nft -f - <<EOF
+table inet filter {
+	flowtable f1 {
+		hook ingress priority 0
+		devices = { veth0, veth1 }
+		counter
+	}
+
+	counter slow {}
+
+	chain forward {
+		type filter hook forward priority 0; policy accept;
+
+		# Installing the flow does not stop this packet. Later
+		# packets of the same flow are taken at ingress and never
+		# reach this chain, so "slow" counts only the handshake.
+		meta l4proto udp ct state established,related flow add @f1
+		meta l4proto udp counter name slow
+	}
+}
+
+table inet nat {
+	chain postrouting {
+		type nat hook postrouting priority 100; policy accept;
+	}
+}
+EOF
+then
+	echo "SKIP: Could not load nft ruleset"
+	exit "$ksft_skip"
+fi
+
+ip netns exec "$nsr" sysctl -wq net.netfilter.nf_conntrack_acct=1
+
+if ! ip netns exec "$ns1" ping -c 1 -W 2 -q 10.0.2.99 >/dev/null; then
+	echo "FAIL: IPv4 connectivity" >&2
+	exit 1
+fi
+if ! ip netns exec "$ns1" ping -6 -c 1 -W 2 -q dead:2::99 >/dev/null; then
+	echo "FAIL: IPv6 connectivity" >&2
+	exit 1
+fi
+
+listener_ready() {
+	ss -N "$ns2" -uln -H "sport = :$port" | grep -q "$port"
+}
+
+ip netns exec "$ns2" socat -T 60 \
+	UDP4-LISTEN:"$port",reuseaddr,fork EXEC:cat >/dev/null 2>&1 &
+ip netns exec "$ns2" socat -T 60 \
+	UDP6-LISTEN:"$port",reuseaddr,ipv6only=1,fork EXEC:cat >/dev/null 2>&1 &
+
+if ! busywait 3000 listener_ready; then
+	echo "FAIL: UDP listener did not start" >&2
+	exit 1
+fi
+
+udp_exchange() {
+	local fam=$1
+	local dst=$2
+	local sport=$3
+	local i msg got target
+
+	if [ "$fam" = 6 ]; then
+		target="UDP6:[${dst}]:${port},bind=[dead:1::99]:${sport}"
+	else
+		target="UDP4:${dst}:${port},bind=10.0.1.99:${sport}"
+	fi
+
+	# One socat for the whole burst. Closing it after every datagram
+	# makes socat emit an extra empty packet.
+	coproc SO { ip netns exec "$ns1" socat -T 5 - \
+		"$target,reuseaddr,shut-none" 2>/dev/null; }
+	i=0
+	while [ "$i" -lt "$count" ]; do
+		msg=$(printf '%04d' "$i")
+		printf '%s' "$msg" >&"${SO[1]}"
+		if ! IFS= read -r -n 4 -t 2 got <&"${SO[0]}"; then
+			echo "FAIL: packet $i timeout" >&2
+			kill "$SO_PID" 2>/dev/null || true
+			wait "$SO_PID" 2>/dev/null || true
+			return 1
+		fi
+		if [ "$got" != "$msg" ]; then
+			echo "FAIL: packet $i mismatch" >&2
+			kill "$SO_PID" 2>/dev/null || true
+			wait "$SO_PID" 2>/dev/null || true
+			return 1
+		fi
+		i=$((i + 1))
+	done
+	kill "$SO_PID" 2>/dev/null || true
+	wait "$SO_PID" 2>/dev/null || true
+}
+
+ct_line() {
+	local sport=$1
+
+	ip netns exec "$nsr" cat /proc/net/nf_conntrack \
+		| grep -F "sport=${sport} dport=${port}" | head -n 1
+}
+
+slow_packets() {
+	ip netns exec "$nsr" nft list counter inet filter slow \
+		| sed -n 's/.*packets \([0-9][0-9]*\).*/\1/p' | head -n 1
+}
+
+run_case() {
+	local what=$1
+	local fam=$2
+	local dst=$3
+	local sport=$4
+	local line orig repl slow
+
+	ip netns exec "$nsr" nft reset counter inet filter slow >/dev/null
+
+	if ! udp_exchange "$fam" "$dst" "$sport"; then
+		echo "FAIL: $what: payload was not forwarded" >&2
+		ret=1
+		return
+	fi
+
+	slow=$(slow_packets)
+	line=$(ct_line "$sport")
+
+	if ! printf '%s\n' "$line" | grep -Fq '[OFFLOAD]'; then
+		echo "FAIL: $what: conntrack entry is not offloaded" >&2
+		printf '%s\n' "$line" >&2
+		echo "forward counter packets=${slow:-none}" >&2
+		ret=1
+		return
+	fi
+
+	# Forward hook sees the handshake only. A full burst means the
+	# ingress flowtable did not take over.
+	if [ "${slow:-999}" -gt 8 ]; then
+		echo "FAIL: $what: forward hook saw ${slow} UDP packets" >&2
+		printf '%s\n' "$line" >&2
+		ret=1
+		return
+	fi
+
+	orig=$(printf '%s\n' "$line" | grep -o 'packets=[0-9]*' | head -n 1)
+	repl=$(printf '%s\n' "$line" | grep -o 'packets=[0-9]*' | sed -n '2p')
+	orig=${orig#packets=}
+	repl=${repl#packets=}
+
+	# nf_ct_acct_update is what the flowtable counter flag does. Without
+	# it, only the handshake is accounted.
+	if [ "${orig:-0}" -lt "$count" ] || [ "${repl:-0}" -lt "$count" ]; then
+		echo "FAIL: $what: got ${orig:-0}/${repl:-0}, need $count" >&2
+		printf '%s\n' "$line" >&2
+		ret=1
+		return
+	fi
+
+	echo "PASS: $what (forward=${slow}, orig=${orig}, reply=${repl})"
+}
+
+run_case "IPv4 UDP" 4 10.0.2.99 5401
+run_case "IPv6 UDP" 6 dead:2::99 5402
+
+if ! ip netns exec "$nsr" nft add rule inet nat postrouting \
+		oifname "veth1" masquerade; then
+	echo "FAIL: could not add masquerade rule" >&2
+	exit 1
+fi
+
+run_case "IPv4 UDP masquerade" 4 10.0.2.99 5403
+run_case "IPv6 UDP masquerade" 6 dead:2::99 5404
+
+exit $ret
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state
  2026-11-13  3:12 ` [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state Pablo Neira Ayuso
@ 2026-11-13  3:17   ` netdev-bot+sinfo
  2026-11-13  3:24     ` Pablo Neira Ayuso
  2026-11-14  3:23   ` netdev-bot+sashiko
  1 sibling, 1 reply; 30+ messages in thread
From: netdev-bot+sinfo @ 2026-11-13  3:17 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

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.

 - Whether the issue was actually triggered, or is only theoretical
   (e.g. found by LLM and not proven at runtime). If it was triggered
   please include the symptoms, like the stack trace or kernel messages.

 - What hardware the change was tested on. For driver fixes please
   mention the device (and if relevant firmware version) used for
   testing, or say that the change was not tested on real hardware.

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] 30+ messages in thread

* Re: [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state
  2026-11-13  3:17   ` netdev-bot+sinfo
@ 2026-11-13  3:24     ` Pablo Neira Ayuso
  0 siblings, 0 replies; 30+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13  3:24 UTC (permalink / raw)
  To: netdev-bot+sinfo
  Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Hi,

On Fri, Nov 13, 2026 at 03:17:05AM +0000, netdev-bot+sinfo@kernel.org wrote:
> 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.
> 
>  - Whether the issue was actually triggered, or is only theoretical
>    (e.g. found by LLM and not proven at runtime). If it was triggered
>    please include the symptoms, like the stack trace or kernel messages.
> 
>  - What hardware the change was tested on. For driver fixes please
>    mention the device (and if relevant firmware version) used for
>    testing, or say that the change was not tested on real hardware.
> 
> 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.

This is a pull request for nf-next. It mixes fixes and new work, so
here is the information per patch:

- "flowtable: do not hw offload flows in teardown state" and
  "lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non
  init-netns": reported by Sashiko (LLM review, theoretical).

- "flowtable: retrieve stats on flow deletion" plus the mtk_ppe and
  airoha updates: reported by Valentin Berezhnoy on netfilter-devel
  (link in the Closes: tag). The last stats sync with conntrack is
  missed on hardware flow deletion.

- "flowtable: tunnel device poisons h_source of fast-path frames":
  hit in production on a DS-Lite setup (ip6tnl over PPPoE over 802.1Q
  VLAN). Every offloaded TCP/UDP flow dies because the PPPoE endpoint
  drops frames with the wrong source MAC.

- "xt_time: fix monthday computation for dates past 2039":
  theoretical. LLM keeps banging on this as an issue, so let's
  address it.

- "nft_flow_offload: do not offload multicast": this was most likely
  found in production.

- "flowtable: do not clear the offload bit twice": Found while
   developing a new driver supporting the flowtable.

- "nfnetlink_queue: make skb writable before writing NFQA_L2HDR":
   LLM finding.

- "flowtable: purge entries immediately when destroying the
  flowtable", "flowtable: do not scrub as crossing netns" and
  "flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push()":
  LLM findings too.

- The two selftest patches add a test and fix a clang warning. They
  are not fixes.

Thanks.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state
  2026-11-13  3:12 ` [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state Pablo Neira Ayuso
  2026-11-13  3:17   ` netdev-bot+sinfo
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  1 sibling, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 1 · Medium: 0 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] The patch only partly closes the packet-path refresh race.
- [Low] The commit fixes a race found by an automated reporter ('Sashiko
  reports...') but carries no Fixes:, Reported-by: or Closes: tag.

--

netfilter: flowtable: do not hw offload flows in teardown state

This commit makes nf_flow_offload_refresh() check NF_FLOW_TEARDOWN again
after NF_FLOW_PENDING has been acquired. The goal is to stop the packet
path from queueing a hardware refresh for a flow that is going away. It
relies on GC skipping flows that have NF_FLOW_PENDING set, which came in
with commit 7c549fb7eecd.

> Sashiko reports that refreshing hw offload from packet path is racy
> since the flow might be already going away.
>
> Since commit 7c549fb7eecd ("netfilter: flowtable: generalize pending
> status bit"), GC skips flow entries that are still pending to be
> offloaded. Let's just check for the teardown status bit after the
> pending bit is set on to address this race.

[Severity: Low]
This isn't a bug, but should this have Fixes:, Reported-by: and Closes:
tags for the report?

The fix only works on trees where nf_flow_offload_gc_step() already skips
flows with NF_FLOW_PENDING set:

	if (test_bit(NF_FLOW_PENDING, &flow->flags))
		return;

That check came from commit 7c549fb7eecd. A stable backport to a tree
without that commit would leave the race open.

Could the message record this dependency? Could it also state the
consequence, which is offload work queued on a flow that GC frees, i.e. a
possible use-after-free?

> diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
> index 4365859220e6a..3a18fa48cadb0 100644
> --- a/net/netfilter/nf_flow_table_offload.c
> +++ b/net/netfilter/nf_flow_table_offload.c
> @@ -1120,6 +1120,12 @@ void nf_flow_offload_refresh(struct nf_flowtable *flowtable,
>  	if (!offload)
>  		return;
>  
> +	if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
> +		clear_bit(NF_FLOW_PENDING, &flow->flags);
> +		kfree(offload);
> +		return;
> +	}
> +
>  	flow_offload_queue_work(offload);
>  }

[Severity: High]
Does this close the race for flows that are only in the NF_FLOW_CLOSING
state?

GC also sends a hardware DESTROY for a CLOSING flow while TEARDOWN is
still clear. This happens in nf_flow_offload_gc_step():

	} else if (test_bit(NF_FLOW_CLOSING, &flow->flags) &&
		   test_bit(NF_FLOW_HW, &flow->flags) &&
		   !test_bit(NF_FLOW_HW_DYING, &flow->flags)) {
		nf_flow_offload_del(flow_table, flow);

On the packet path, flow_offload_refresh() is the only place that tests
CLOSING, and it does so before PENDING is taken:

	if (likely(!nf_flowtable_hw_offload(flow_table)) ||
	    test_bit(NF_FLOW_CLOSING, &flow->flags))
		return;

	if (test_bit(NF_FLOW_HW, &flow->flags))
		nf_flow_offload_refresh(flow_table, flow);

Could this sequence happen?

CPU B (packet path)
flow_offload_refresh()
  sees CLOSING == 0 and NF_FLOW_HW set
  <stalls before test_and_set_bit(NF_FLOW_PENDING)>

CPU A
nf_flow_state_check() on FIN/RST
  sets NF_FLOW_CLOSING

GC
nf_flow_offload_gc_step()
  nf_flow_offload_del()
    takes PENDING, sets NF_FLOW_HW_DYING, queues DESTROY
flow_offload_work_del()
  removes the rule, sets NF_FLOW_HW_DEAD
  PENDING is cleared

CPU B resumes
nf_flow_offload_refresh()
  nf_flow_offload_work_alloc() wins PENDING
  test_bit(NF_FLOW_TEARDOWN) == 0
  flow_offload_queue_work() with FLOW_CLS_REPLACE

flow_offload_work_add() does not check NF_FLOW_HW_DYING or
NF_FLOW_HW_DEAD. It installs the hardware rule again and sets
IPS_HW_OFFLOAD_BIT.

Nothing clears HW_DYING or HW_DEAD after that. When the flow later
expires, the teardown branch in nf_flow_offload_gc_step() goes straight
to flow_offload_del() and sends no new DESTROY:

		if (test_bit(NF_FLOW_HW, &flow->flags)) {
			if (!test_bit(NF_FLOW_HW_DYING, &flow->flags))
				nf_flow_offload_del(flow_table, flow);
			else if (test_bit(NF_FLOW_HW_DEAD, &flow->flags))
				flow_offload_del(flow_table, flow);

Would the re-installed rule then stay in the driver and hardware after
the flow and its conntrack entry are freed? Its cookie is the address of
the freed tuple, so a new flow allocated at the same address could
collide with it.

There seems to be a worse interleaving as well:

1. GC reads PENDING == 0 at the top of nf_flow_offload_gc_step().
2. CPU B wins PENDING and reads TEARDOWN == 0.
3. GC calls flow_offload_teardown(). HW_DYING and HW_DEAD are already set
   from the CLOSING DESTROY, so GC frees the flow through
   flow_offload_del() without taking PENDING.

Can the queued REPLACE work then run on a flow that was freed via
call_rcu()?

The window is narrow. It still looks reachable on PREEMPT_RT, where
softirq is preemptible, or under vCPU preemption. The FIN/RST that starts
it comes from the network.

NF_FLOW_HW_DYING is set while PENDING is held, and it is published before
PENDING is cleared. Would re-checking NF_FLOW_HW_DYING (and/or
NF_FLOW_CLOSING) here, after PENDING is acquired, cover every state in
which GC has already issued the DESTROY?

Later patches in the series don't appear to change
nf_flow_offload_refresh() or flow_offload_refresh().

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 02/15] netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push()
  2026-11-13  3:12 ` [PATCH net-next 02/15] netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push() Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 1 · Medium: 0 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] The fix does nothing.
- [Low] The commit subject and body name nf_flow_tunnel_ip6ip6_push(),
  which does not exist in the tree being patched.

--

netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push()

This commit moves skb_dst_update_pmtu_no_confirm() ahead of
skb_scrub_packet() in nf_flow_tunnel_ip6_push(). The aim is to run the
PMTU update before skb_scrub_packet() drops the skb dst. The inner route
would then get the tunnel MTU, as it does in ip6_tnl_xmit().

> netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push()
>
> nf_flow_tunnel_ip6ip6_push() invokes skb_scrub_packet() before updating
> the PMTU.

[Severity: Low]
This isn't a bug, but there is no nf_flow_tunnel_ip6ip6_push() in this
tree. Commit 93cf357fa797 added it under that name, and it was later
renamed to nf_flow_tunnel_ip6_push(). It now also takes a dst argument and
handles an IPPROTO_IPIP inner protocol.

Could the subject and body use the current name? It could also say that
the change touches 4in6 encapsulation (e.g. DS-Lite) as well as IP6IP6.

> diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
> index 19cafae748abe..644e70fd6773b 100644
> --- a/net/netfilter/nf_flow_table_ip.c
> +++ b/net/netfilter/nf_flow_table_ip.c
> @@ -699,10 +699,10 @@ static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,
>  	if (err)
>  		return err;
>  
> -	skb_scrub_packet(skb, true);
>  	mtu = dst_mtu(dst) - sizeof(*ip6h);
>  	mtu = max(mtu, IPV6_MIN_MTU);
>  	skb_dst_update_pmtu_no_confirm(skb, mtu);
> +	skb_scrub_packet(skb, true);

[Severity: High]
On the flowtable fast path, is there a dst on the skb at this point?

Flowtable hooks can only be registered at netdev ingress:

net/netfilter/nf_tables_api.c:nft_flowtable_parse_hook() {
    ...
	if (hooknum != NF_NETDEV_INGRESS)
		return -EOPNOTSUPP;
    ...
}

This means nf_flow_offload_ipv6_hook() runs before ip6_route_input(), so
no routing dst is attached yet. The outer tunnel dst only comes in as the
dst argument:

nf_flow_offload_ipv6_hook()
  nf_flow_queue_xmit6()
    nf_flow_tunnel_push(state->net, skb, other_tuple,
                        tuplehash->tuple.dst_cache, &ip_addr)
      nf_flow_tunnel_ip6_push()

The skb only gets that dst after the push returns, and only for
FLOW_OFFLOAD_XMIT_NEIGH:

net/netfilter/nf_flow_table_ip.c:nf_flow_queue_xmit6() {
    ...
		xmit.dest = neigh->ha;
		skb_dst_drop(skb);
		skb_dst_set_noref(skb, &rt->dst);
		break;
	case FLOW_OFFLOAD_XMIT_DIRECT:
    ...
}

skb_dst_update_pmtu_no_confirm() does nothing when skb_dst(skb) is NULL:

include/net/dst.h:skb_dst_update_pmtu_no_confirm() {
	struct dst_entry *dst = skb_dst(skb);

	if (dst && dst->ops->update_pmtu)
		dst->ops->update_pmtu(dst, NULL, skb, mtu, false);
}

So the mtu computed from dst_mtu(dst) looks like it is thrown away both
before and after this change.

Ingress sometimes attaches a metadata dst (collect_md or tunnel_key).
That dst uses dst_blackhole_ops, and dst_blackhole_update_pmtu() is
empty. It is also not the flow's inner route.

There also doesn't seem to be an inner route left to update, because
nft_dev_forward_path() replaces it with the tunnel dst:

net/netfilter/nf_flow_table_path.c:nft_dev_forward_path() {
    ...
	dst_release(route->tuple[dir].dst);
	route->tuple[dir].dst = info.tun_dst;
    ...
}

The ip6_tnl_xmit() comparison depends on ip6_forward() having already put
the inner route on the skb. The flowtable fast path skips that step.

If that reading is right, the patch reorders a call that never has any
effect. The PMTU update described as "silently lost" would still be lost
on every fast-path packet.

Would one of these work better: dropping the call, updating an
explicitly stored inner dst, or relying on (and documenting) the
nf_flow_exceeds_mtu() check in nf_flow_offload_ipv6_forward()? That check
already sends oversized packets to the slow path, and ip6_tnl_xmit()
handles PMTU there.

At the end of the series the code still does the skb_dst(skb) based
PMTU update, followed by skb_scrub_packet(skb, false), so later patches
don't seem to fix this either.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 03/15] netfilter: flowtable: retrieve stats on flow deletion
  2026-11-13  3:12 ` [PATCH net-next 03/15] netfilter: flowtable: retrieve stats on flow deletion Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 2 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] The core change only does anything when a driver writes
  f->stats in its FLOW_CLS_DESTROY handler.
- [Medium] The final hardware stats are added to the conntrack
  asynchronously in flow_offload_work_del() ->…

--

netfilter: flowtable: retrieve stats on flow deletion

This commit passes a flow_stats object to the driver on FLOW_CLS_DESTROY,
so the final hardware counters can be synced into the conntrack entry
when a flow is removed from hardware. The stats update logic moves out of
flow_offload_work_stats() into a new flow_offload_update_stats() helper,
which both the stats path and the delete path now use.

Closes: https://patch.msgid.link/netfilter-devel/20261021175030.4800-1-dreyksmail@gmail.com/

> diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
> index 3a18fa48cadb0..c302051489f23 100644
> --- a/net/netfilter/nf_flow_table_offload.c
> +++ b/net/netfilter/nf_flow_table_offload.c
> @@ -928,12 +928,33 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable,

[ ... ]

> +static void flow_offload_update_stats(struct flow_offload_work *offload,
> +				      struct flow_stats *stats)
> +{
> +	u64 lastused;
> +

[ ... ]

> +	if (offload->flowtable->flags & NF_FLOWTABLE_COUNTER) {
> +		if (stats[0].pkts)
> +			nf_ct_acct_add(offload->flow->ct,
> +				       FLOW_OFFLOAD_DIR_ORIGINAL,
> +				       stats[0].pkts, stats[0].bytes);

[Severity: Medium]
When we get here from flow_offload_work_del(), could the conntrack entry
already have been deleted and reported?

One of the triggers that queues the DESTROY work is a dying ct in
nf_flow_offload_gc_step():

net/netfilter/nf_flow_table_core.c:nf_flow_offload_gc_step() {
    ...
	    nf_ct_is_dying(flow->ct) ||
    ...
}

By that point nf_ct_delete() has already set IPS_DYING, unlinked the
entry and sent IPCT_DESTROY. That event carries the ct accounting
counters to ctnetlink/ulogd:

net/netfilter/nf_conntrack_core.c:nf_ct_delete() {
    ...
	if (test_and_set_bit(IPS_DYING_BIT, &ct->status))
		return false;
    ...
	if (nf_conntrack_event_report(IPCT_DESTROY, ct,
				    portid, report) < 0) {
    ...
}

So when an offloaded ct is removed by conntrack -D or -F, by
nf_ct_iterate_cleanup() on device or NAT events, or by netns teardown,
the sequence looks like this:

nf_ct_delete()
  IPCT_DESTROY event sent with the current counters
nf_flow_offload_gc_step()
  nf_ct_is_dying() -> flow_offload_teardown() -> nf_flow_offload_del()
flow_offload_work_handler()
  flow_offload_work_del()
    flow_offload_update_stats()
      nf_ct_acct_add()

The counters added here go into a ct that nothing reports any more. On
this path, the hardware traffic since the last FLOW_CLS_STATS poll is
still lost.

The common case, a flow expiring while its ct is still alive, looks
fine. flow_offload_teardown() leaves the ct alive, and this update runs
before the ct's own destroy event.

Is the deleted-ct case meant to be covered by the "missing last stats
sync" that the commit message describes?

> +		if (stats[1].pkts)
> +			nf_ct_acct_add(offload->flow->ct,
> +				       FLOW_OFFLOAD_DIR_REPLY,
> +				       stats[1].pkts, stats[1].bytes);
> +	}
> +}

[ ... ]

> @@ -991,10 +1013,15 @@ static void flow_offload_work_add(struct flow_offload_work *offload)
>  
>  static void flow_offload_work_del(struct flow_offload_work *offload)
>  {
> +	struct flow_stats stats[FLOW_OFFLOAD_DIR_MAX] = {};
> +
>  	clear_bit(IPS_HW_OFFLOAD_BIT, &offload->flow->ct->status);
> -	flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL);
> +	flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL, &stats[0]);
>  	if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
> -		flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY);
> +		flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY, &stats[1]);
> +
> +	flow_offload_update_stats(offload, stats);

[Severity: Medium]
Won't stats[] still be all zero here for mlx5 and nfp?

cls_flow is zero-initialised in nf_flow_offload_tuple(). If a driver's
FLOW_CLS_DESTROY handler never writes f->stats, the memcpy() copies back
zeroes, and flow_offload_update_stats() skips nf_ct_acct_add().

Later patches in this series convert mtk_ppe and airoha ("net: ethernet:
mtk_ppe: update stats on flow deletion" and "net: airoha: update stats on
flow deletion"). Two other in-tree consumers still report nothing on
DESTROY at the end of the series.

In mlx5, mlx5_tc_ct_block_flow_offload_del() drops the counter through
mlx5_tc_ct_entry_put(). It never calls mlx5_fc_query_cached() or
flow_stats_update().

In nfp, nfp_fl_ct_handle_nft_flow() does:

	case FLOW_CLS_DESTROY:
		ct_map_ent = rhashtable_lookup_fast(...);
		return nfp_fl_ct_del_flow(ct_map_ent);

Both drivers report packet and byte deltas only from their
FLOW_CLS_STATS handlers, mlx5_tc_ct_block_flow_offload_stats() and
nfp_fl_ct_stats().

Both register through act_ct, which turns on counter accounting:

net/sched/act_ct.c:tcf_ct_flow_table_get() {
    ...
	ct_ft->nf_ft.flags |= NF_FLOWTABLE_HW_OFFLOAD |
			      NF_FLOWTABLE_COUNTER;
    ...
}

So with tc act_ct offload on mlx5 or nfp, traffic forwarded between the
last FLOW_CLS_STATS poll and the hardware delete is still missing from
the conntrack counters.

Given the Reported-by and Closes tags, should mlx5 and nfp be converted
in this series as well? sfc doesn't seem to be affected, because
efx_tc_ct_stats() only reports lastused.

> +
>  	set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
>  }

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 04/15] net: ethernet: mtk_ppe: update stats on flow deletion
  2026-11-13  3:12 ` [PATCH net-next 04/15] net: ethernet: mtk_ppe: update " Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 2 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] Bridge (L2) flows on mtk_ppe: the new destroy-time call to
  mtk_flow_offload_update_stats() read-clears the hardware MIB counter…
- [Medium] mtk_flow_offload_destroy() takes the final MIB snapshot before
  it invalidates the hardware FOE entry, so the 'last stats sync' on…

--

net: ethernet: mtk_ppe: update stats on flow deletion

This moves the stats collection out of mtk_flow_offload_stats() into a new
helper, mtk_flow_offload_update_stats(). The helper is also called from
mtk_flow_offload_destroy(), so the final hardware counters are reported
when a flow is deleted. This follows "netfilter: flowtable: retrieve stats
on flow deletion".

> diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
> index 99b28aaa7cc47..8ca694bb5e1b7 100644
> --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
> +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
> @@ -526,6 +526,24 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
>  	return err;
>  }
>  
> +static void
> +mtk_flow_offload_update_stats(struct mtk_eth *eth, struct flow_cls_offload *f,
> +			      struct mtk_flow_entry *entry)
> +{
> +	struct mtk_foe_accounting diff;
> +	u32 idle;
> +
> +	idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
> +	f->stats.lastused = jiffies - idle * HZ;
> +
> +	if (entry->hash != 0xFFFF &&
> +	    mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
> +				  &diff)) {

[Severity: Medium]
When a bridge (L2) flow is destroyed, can this read-clear the hardware
counter of an unrelated flow?

For a flower rule with only ETH_ADDRS, mtk_flow_offload_replace()
allocates the entry with kzalloc_obj(), so entry->hash starts at 0.
mtk_foe_entry_commit() then takes the L2 path:

drivers/net/ethernet/mediatek/mtk_ppe.c:mtk_foe_entry_commit() {
	...
	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
		return mtk_foe_entry_commit_l2(ppe, entry);

	hash = mtk_ppe_hash_entry(ppe->eth, &entry->data);
	entry->hash = 0xffff;
	...
}

mtk_foe_entry_commit_l2() never sets entry->hash = 0xffff. The base L2
entry is not on the foe_flow lists, so __mtk_ppe_check_skb() never updates
its hash. mtk_flow_entry_update_l2() only touches the subflow hashes. As a
result, the base entry keeps hash == 0 for its whole lifetime.

That means the entry->hash != 0xFFFF check passes and
mtk_foe_entry_get_mib(ppe, 0, &diff) runs. mtk_ppe_start() enables
MTK_PPE_MIB_CFG_RD_CLR, so this read zeroes the MIB counter of whatever
flow is bound at FOE index 0, for example an nf_flowtable L3 flow. Those
packets and bytes get charged to the L2 rule and never reach their real
owner.

The bridge flow's own counters live in the subflow slots on
entry->l2_flows, and this function never reads them. So the destroy-time
stats for L2 flows are wrong as well.

The missing 0xffff on L2 base entries is not new. Before this patch,
though, mtk_flow_offload_destroy() never touched the MIB. Now every
FLOW_CLS_DESTROY of a bridge flow does this read-clear, even when nobody
requested stats for that rule.

Should L2 base entries be skipped here? Alternatively, should
mtk_foe_entry_commit_l2() set entry->hash to 0xffff?

> +		f->stats.pkts += diff.packets;
> +		f->stats.bytes += diff.bytes;
> +	}
> +}
> +
>  static int
>  mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
>  {
> @@ -536,6 +554,8 @@ mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
>  	if (!entry)
>  		return -ENOENT;
>  
> +	mtk_flow_offload_update_stats(eth, f, entry);
> +
>  	mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry);

[Severity: Medium]
Is this snapshot really the final one for the flow?

mtk_flow_offload_update_stats() read-clears MIB[entry->hash] while the
hardware FOE entry is still in BIND state. ppe_lock is dropped between
the two steps. The entry is only invalidated later, under ppe_lock, in
__mtk_foe_entry_clear():

drivers/net/ethernet/mediatek/mtk_ppe.c:__mtk_foe_entry_clear() {
	...
		hwe->ib1 &= ~MTK_FOE_IB1_STATE;
		hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
		dma_wmb();
		mtk_ppe_cache_clear(ppe);

		if (ppe->accounting) {
			...
			acct->packets = 0;
			acct->bytes = 0;
		}
	}
	entry->hash = 0xffff;
	...
}

Between the MIB read and the invalidation, the PPE keeps forwarding and
counting packets for this flow. That traffic never reaches f->stats. It
stays in the hardware MIB counter, because only the software acct_table
is zeroed here.

__mtk_foe_entry_commit() doesn't clear the MIB either. So the next
read-clear of that index charges those packets to whichever flow binds
the slot next.

This happens whenever an active flow is torn down, for example on a
flowtable flush, a conntrack flush or a ruleset deletion.

Would it work to invalidate the entry first and then drain
MIB[saved hash]?

>  	rhashtable_remove_fast(&eth->flow_table, &entry->node,
>  			       mtk_flow_ht_params);

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 05/15] net: airoha: update stats on flow deletion
  2026-11-13  3:12 ` [PATCH net-next 05/15] net: airoha: " Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] airoha_ppe_flow_update_stats()
  (drivers/net/ethernet/airoha/airoha_ppe.c, ~1288-1303) computes…
- [Low] In airoha_ppe_flow_offload_destroy() the final counters are
  sampled by airoha_ppe_flow_update_stats() (line 1315) before the…

--

net: airoha: update stats on flow deletion

This commit moves the stats collection code out of
airoha_ppe_flow_offload_stats() into a new helper,
airoha_ppe_flow_update_stats(). The helper is also called from
airoha_ppe_flow_offload_destroy(), so the final counters are reported on
flow deletion, as introduced by "netfilter: flowtable: retrieve stats on
flow deletion".

> diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
> index f5369c45766d2..af8c7fe9b7f09 100644
> --- a/drivers/net/ethernet/airoha/airoha_ppe.c
> +++ b/drivers/net/ethernet/airoha/airoha_ppe.c
> @@ -1285,6 +1285,23 @@ static int airoha_ppe_flow_offload_replace(struct airoha_eth *eth,
>  	return err;
>  }
>  
> +static void airoha_ppe_flow_update_stats(struct airoha_eth *eth,
> +					 struct flow_cls_offload *f,
> +					 struct airoha_flow_table_entry *e)
> +{
> +	u32 idle = airoha_ppe_entry_idle_time(eth->ppe, e);
> +
> +	f->stats.lastused = jiffies - idle * HZ;
> +	if (e->hash != 0xffff) {
> +		struct airoha_foe_stats64 stats = {};
> +
> +		airoha_ppe_foe_entry_get_stats(eth->ppe, e->hash, &stats);
> +		f->stats.pkts += (stats.packets - e->stats.packets);
> +		f->stats.bytes += (stats.bytes - e->stats.bytes);
> +		e->stats = stats;

[Severity: Medium]
Can these subtractions wrap if the entry was re-bound after the last stats
read?

Every time the driver commits a binding, it resets the hardware/NPU counter
slot to zero:

airoha_ppe_foe_insert_entry()
  airoha_ppe_foe_commit_entry(ppe, &e->data, hash, rx_wlan)
    airoha_ppe_foe_flow_stats_update()
      airoha_ppe_foe_flow_stat_entry_reset()
        memset_io(&npu->stats[index], 0, sizeof(*npu->stats));
        memset(&ppe->foe_stats[index], 0, sizeof(*ppe->foe_stats));

e->stats is not cleared on that path. Its only writer is the assignment
above.

Suppose the FOE entry goes UNBIND, either through hardware aging or
because the slot was overwritten. airoha_ppe_foe_insert_entry() then
re-binds it and sets e->hash = hash. The next call here computes
small - old_total in u64, which gives a value near 2^64.

On the DESTROY path, flow_offload_update_stats() passes that value to
nf_ct_acct_add(), which takes unsigned int packets/bytes. The conntrack
counters would then jump by roughly 2^32 - old_total.

The stale baseline already affected the periodic FLOW_CLS_STATS path. But
nf_flow_offload_gc_step() never sends FLOW_CLS_STATS for a flow that has
been torn down. A re-bind between teardown and the DESTROY work used to
go unreported. With this patch it becomes the flow's final accounting
value.

Should e->stats be reset wherever e is recommitted and the hardware counter
is zeroed?

> +	}
> +}
> +
>  static int airoha_ppe_flow_offload_destroy(struct airoha_eth *eth,
>  					   struct flow_cls_offload *f)
>  {
> @@ -1295,6 +1312,7 @@ static int airoha_ppe_flow_offload_destroy(struct airoha_eth *eth,
>  	if (!e)
>  		return -ENOENT;
>  
> +	airoha_ppe_flow_update_stats(eth, f, e);
>  	airoha_ppe_foe_flow_remove_entry(eth->ppe, e);

[Severity: Low]
Could the final stats miss packets that the hardware forwards between
these two calls?

The counters are read without ppe_lock while the FOE entry is still BIND,
so the PPE keeps forwarding and counting. After that,
airoha_ppe_foe_flow_remove_entry() takes ppe_lock and does:

airoha_ppe_foe_remove_flow()
  e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, AIROHA_FOE_STATE_INVALID);
  airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash, false)
    airoha_ppe_foe_flow_stats_update()
      airoha_ppe_foe_flow_stat_entry_reset()
    airoha_ppe_foe_commit_sram_entry()

Packets counted after the snapshot and before the reset are lost. So are
packets counted after the reset and before the SRAM write that
invalidates the entry.

The window is small. Since the goal here is accurate final counters,
would it be better to invalidate the hardware entry first, then read and
reset the counter, all under ppe_lock?

>  	rhashtable_remove_fast(&eth->flow_table, &e->node,
>  			       airoha_flow_table_params);

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 06/15] netfilter: xt_time: fix monthday computation for dates past 2039
  2026-11-13  3:12 ` [PATCH net-next 06/15] netfilter: xt_time: fix monthday computation for dates past 2039 Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] The commit "netfilter: xt_time: fix monthday computation for
  dates past 2039" fixes a long-standing bug but has no `Fixes:` tag and…
- [Low] Two comments in net/netfilter/xt_time.c no longer match the code
  after this patch.

--

netfilter: xt_time: fix monthday computation for dates past 2039

This replaces the days_since_epoch[] table walk in localtime_3() with
time64_to_tm(). With the change, r->monthday stays in the 1-31 range for
dates from 2040-01-01 on. Removing the lookup tables also shrinks the
xt_time object.

> netfilter: xt_time: fix monthday computation for dates past 2039
>
> iocaltime_3() derives the year from days_since_epoch[], which only has
> entries up to 2039. For any date from 2040-01-01 on, the lookup stays on
> the 2039 entry, so the day-of-year offset is 365 or more. That value is
> used to compute r->monthday, which ends up larger than 31 (32 on
> 2040-01-01, and it wraps in the u8 later in the year).

[ ... ]

> Only the real-time clock reaches localtime_3(), so this is not
> triggerable before the clock passes 2040-01-01 UTC.

[Severity: Low]
This isn't a bug, but there's a typo in the first paragraph:
iocaltime_3() should be localtime_3().

Should the first paragraph also say what the out-of-range value leads to?
time_mt() uses it as a shift count:

    if (!(info->monthdays_match & (1U << current_time.monthday)))
        return false;

On 2040-01-01 monthday is 32. Later in 2040 it wraps in the u8 (for
example, 397 becomes 141). A shift of 32 or more is undefined behaviour,
so UBSAN would report shift-out-of-bounds. Monthday rules would then
match or miss arbitrary days.

Is the "not triggerable before ... 2040-01-01 UTC" statement accurate?
A CAP_SYS_TIME holder can set CLOCK_REALTIME past 2040 today with
settimeofday() or clock_settime(). A bad RTC or NTP source can do the
same.

There is also the XT_TIME_LOCAL_TZ adjustment in time_mt():

    if (info->flags & XT_TIME_LOCAL_TZ)
        /* Adjust for local timezone */
        stamp -= 60 * sys_tz.tz_minuteswest;

kernel/time/time.c limits tz_minuteswest to +/-15*60. East of UTC, the
local stamp can therefore reach 2040 up to 15 hours before UTC does.

The patch also has no Fixes: tag. days_since_epoch[], the
DSE_FIRST = 2039 year walk and the monthday computation all come from
ee4411a1b1e0. Should this carry:

Fixes: ee4411a1b1e0 ("[NETFILTER]: x_tables: add xt_time match")
Cc: stable@vger.kernel.org

That would let stable and long-term kernels that will still run in 2040,
such as CIP SLTS, pick up the fix. Other fixes in the same netfilter
batch carry Fixes: tags, for example "netfilter: flowtable: do not clear
the offload bit twice" and "netfilter: nfnetlink_queue: make skb
writable before writing NFQA_L2HDR".

> diff --git a/net/netfilter/xt_time.c b/net/netfilter/xt_time.c
> index 2065fce8ef81b..f0c4c1d1bc4a0 100644
> --- a/net/netfilter/xt_time.c
> +++ b/net/netfilter/xt_time.c

[ ... ]

> @@ -102,51 +71,12 @@ static inline void localtime_2(struct xtm *r, time64_t time)
>  
>  static void localtime_3(struct xtm *r, time64_t time)
>  {
> -	unsigned int year, i, w = r->dse;
> -
> -	/*
> -	 * In each year, a certain number of days-since-the-epoch have passed.
> -	 * Find the year that is closest to said days.

[ ... ]

> +	time64_to_tm(time, 0, &tm);
>  
> -	r->month    = i + 1;
> +	r->monthday = tm.tm_mday;	/* 1-31 */
> +	r->month    = tm.tm_mon + 1;	/* 1-12 */
>  }

[Severity: Low]
The dietlibc-derived tables and table walk are gone now. Does the file
header comment still match the code?

 *	It is using some modified code from dietlibc (localtime() function)
 *	that you can find at https://www.fefe.de/dietlibc/

Date conversion now comes from time64_to_tm() in kernel/time/timeconv.c.

The comment in localtime_2() still says it handles monthday:

	/*
	 * Here comes the rest (weekday, monthday). First, divide the SSTE
	 * by seconds-per-day to get the number of _days_ since the epoch.
	 */

But localtime_2() only sets r->dse and r->weekday. That comment was
already inaccurate before this patch. Now localtime_3() doesn't read
r->dse at all, so the mismatch is larger.

Could both comments be updated to match the new code?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 07/15] netfilter: nft_flow_offload: do not offload multicast
  2026-11-13  3:12 ` [PATCH net-next 07/15] netfilter: nft_flow_offload: do not offload multicast Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] The commit message of "netfilter: nft_flow_offload: do not
  offload multicast" describes a bug that users can see, but the patch…

--

netfilter: nft_flow_offload: do not offload multicast

This patch makes nft_flow_offload_skip() skip packets with a multicast
destination for both IPv4 and IPv6. The flowtable then no longer depends
on the ruleset or on the reverse route lookup to keep multicast flows
out.

> diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
> index d3c5651dd6997..530e20c7fd457 100644
> --- a/net/netfilter/nft_flow_offload.c
> +++ b/net/netfilter/nft_flow_offload.c

[ ... ]

> @@ -33,6 +34,10 @@ static bool nft_flow_offload_skip(struct sk_buff *skb, int family)
>  
>  		if (unlikely(opt->optlen))
>  			return true;
> +
> +		return ipv4_is_multicast(ip_hdr(skb)->daddr);
> +	} else if (family == NFPROTO_IPV6) {
> +		return ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr);

[Severity: Low]
The commit message describes a bug that users can see. IPv6 multicast
forwarded by ip6mr is added to the flowtable, and the flow keeps sending
to the output it resolved even after the last listener leaves. Should
this have a Fixes: tag, and maybe Cc: stable as well?

The path to the bug looks like this:

ip6mr_forward2()
  NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, ...)
    nft_flow_offload_eval()
      nft_flow_route()
      flow_offload_add()

In nft_flow_route() in net/netfilter/nf_flow_table_path.c, the reverse
lookup does:

    fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.src.u3.in6;

For a multicast flow in the original direction, that address is the
multicast group. On IPv4, ip_route_output_key_hash_rcu() returns -EINVAL
for a multicast saddr. The IPv6 output lookup has no such check, so
other_dst resolves and the flow is added.

NF_INET_FORWARD is the only hook nft_flow_offload_validate() accepts. A
ruleset with a plain 'flow add @ft' in a forward chain reaches this path
on current kernels.

Without a Fixes: tag, stable and distro backporters have nothing that
points them at this fix. Older kernels would likely keep sending IPv6
multicast to stale outputs.

>  	}
>  
>  	return false;

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 08/15] netfilter: lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non init-netns
  2026-11-13  3:12 ` [PATCH net-next 08/15] netfilter: lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non init-netns Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] The patch sets the per-netns copy of
  net.netfilter.nf_hooks_lwtunnel to mode 0444 in nf_lwtunnel_net_init()…
- [Low] The nf_hooks_lwtunnel entry in
  Documentation/networking/nf_conntrack-sysctl.rst (lines 212-217)…

--

netfilter: lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non init-netns

The commit registers the per-netns copy of the nf_hooks_lwtunnel sysctl
with mode 0444 instead of 0644 in every netns except init_net. This stops
unprivileged users from flipping the global nf_hooks_lwtunnel_enabled
static key from inside a user namespace.

> diff --git a/net/netfilter/nf_hooks_lwtunnel.c b/net/netfilter/nf_hooks_lwtunnel.c
> index 40c6fce14a63f..7e2ee8677f1fc 100644
> --- a/net/netfilter/nf_hooks_lwtunnel.c
> +++ b/net/netfilter/nf_hooks_lwtunnel.c
> @@ -68,14 +68,18 @@ static int __net_init nf_lwtunnel_net_init(struct net *net)

[ ... ]

>  	if (!net_eq(net, &init_net)) {
> -		table = kmemdup(nf_lwtunnel_sysctl_table,
> -				sizeof(nf_lwtunnel_sysctl_table),
> -				GFP_KERNEL_ACCOUNT);
> -		if (!table)
> +		dup = kmemdup(nf_lwtunnel_sysctl_table,
> +			      sizeof(nf_lwtunnel_sysctl_table),
> +			      GFP_KERNEL_ACCOUNT);
> +		if (!dup)
>  			goto err_alloc;
> +
> +		dup->mode = 0444;
> +		table = dup;

[Severity: Medium]
Should the SRv6 netfilter selftests be updated along with this change?

With 0444, nobody can write the knob from a child netns, including global
root with CAP_NET_ADMIN. net_ctl_permissions() copies only the owner bits:

net/sysctl_net.c:net_ctl_permissions() {
    ...
	if (ns_capable_noaudit(net->user_ns, CAP_NET_ADMIN)) {
		int mode = (table->mode >> 6) & 7;
		return (mode << 6) | (mode << 3) | mode;
	}
    ...
}

test_perm() in fs/proc/proc_sysctl.c does not let root override this, so
the write returns -EACCES.

Both tools/testing/selftests/net/srv6_end_dx4_netfilter_test.sh and
srv6_end_dx6_netfilter_test.sh write the knob from the rt-N netns in
setup_rt_netfilter():

	ip netns exec ${nsname} sysctl -wq net.netfilter.nf_hooks_lwtunnel=1

The scripts don't check the exit status, so the failure goes unnoticed.
nf_hooks_lwtunnel_enabled then stays off, and decapsulated SRv6 packets
never reach the NF_HOOK(NF_INET_PRE_ROUTING) calls in seg6_local.c.

router_netfilter_tests() only checks ping connectivity:

	setup_rt_netfilter 1
	setup_rt_netfilter 2

	check_and_log_hs_connectivity 1 2 100

Plain SRv6 forwarding works without the hooks, so the "netfilter enabled
in routers" tests would still pass without running the netfilter path at
all.

Userspace sees the same change: a privileged container manager that used
to enable this knob as real root from inside a pod netns will now get
EACCES and has to write it in init_net instead.

The restriction itself matches other global netfilter sysctls, such as
those in nf_conntrack_standalone.c and ipvs. The concern is only that the
dependent selftests stay as they are. As of 3e59905baa0f, both scripts
still write the sysctl from rt-N.

[Severity: Low]
This isn't a bug, but should the nf_hooks_lwtunnel entry in
Documentation/networking/nf_conntrack-sysctl.rst mention the new
restriction?

The entry currently reads:

	nf_hooks_lwtunnel - BOOLEAN
		- 0 - disabled (default)
		- not 0 - enabled

		If this option is enabled, the lightweight tunnel netfilter hooks are
		enabled. This option cannot be disabled once it is enabled.

The same document already has this note for nf_conntrack_buckets:

	This sysctl is only writeable in the initial net namespace.

A similar note here would explain the EACCES that users now get when they
write the knob from a child netns. As of 3e59905baa0f, the entry is
unchanged.

>  	}
>  
>  	hdr = register_net_sysctl_sz(net, "net/netfilter", table,

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 09/15] netfilter: flowtable: purge entries immediately when destroying the flowtable
  2026-11-13  3:12 ` [PATCH net-next 09/15] netfilter: flowtable: purge entries immediately when destroying the flowtable Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] The commit message understates the impact of the bug it fixes.

--

netfilter: flowtable: purge entries immediately when destroying the flowtable

When a flowtable is destroyed, this patch stops relying on the hardware
offload workqueue to purge the remaining flow entries. It now walks the
rhashtable through rhashtable_free_and_destroy() with
nf_flow_table_do_purge(). That callback tears down each flow, removes its
hardware state synchronously and frees it. NF_FLOW_PENDING marks the first
tuplehash visited, so the flow is released only on the second visit.

> Do not use hardware offload workqueue to purge the remaining flow
> entries when the flowtable is being destroyed. Instead, iterate over the
> flowtable to purge the remaining flow offload entries. Work memory
> allocations can fail, leaving us with either waiting indefinitely until
> work can be allocated or leaving entries in the hardware.

[Severity: Low]
The code change looks correct. Does this paragraph understate what the old
nf_flow_table_free() sequence could do when the work allocation failed?

The old sequence was:

    nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
    nf_flow_table_gc_run(flow_table);
    nf_flow_table_offload_flush_cleanup(flow_table);
    rhashtable_destroy(&flow_table->rhashtable);

On that path, nf_flow_offload_gc_step() calls nf_flow_offload_del(). If the
GFP_ATOMIC allocation in nf_flow_offload_work_alloc() fails, it returns
before setting NF_FLOW_HW_DYING:

    offload = nf_flow_offload_work_alloc(flowtable, flow, FLOW_CLS_DESTROY);
    if (!offload)
        return;

    set_bit(NF_FLOW_HW_DYING, &flow->flags);

As a result, flow_offload_del() is never called for that flow, and it stays
in the rhashtable. rhashtable_destroy() frees only the bucket tables. That
leaks the struct flow_offload along with its nf_conn reference and its
route dst references, which goes beyond leaving entries in the hardware.

The allocation could also fail in the first nf_flow_table_gc_run() and then
succeed in the second one, inside nf_flow_table_offload_flush_cleanup():

    flush_workqueue(nf_flow_offload_del_wq);
    nf_flow_table_gc_run(flowtable);

In that case, the del work is queued after the last flush and never flushed
again. nf_tables_flowtable_destroy() calls kfree(flowtable) right after
nf_flow_table_free() returns, and tcf_ct_flow_table_cleanup_work() frees
ct_ft. So when the work runs later, flow_offload_work_handler() would read
offload->flowtable->net, and flow_offload_tuple_del() would walk
offload->flowtable->flow_block.cb_list, both in freed memory.

Could the commit message mention the flow and conntrack reference leak, and
the possible use-after-free of the flowtable from late-queued del work?
That would help stable and security triage assess this fix.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 11/15] netfilter: flowtable: do not scrub as crossing netns
  2026-11-13  3:12 ` [PATCH net-next 11/15] netfilter: flowtable: do not scrub as crossing netns Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] The Fixes: tag and the commit message cover only one of the two
  changed call sites.

Pre-existing issues:
- [Medium] This problem wasn't introduced by this patch.

--

netfilter: flowtable: do not scrub as crossing netns

This commit changes the flowtable tunnel push helpers from
skb_scrub_packet(skb, true) to skb_scrub_packet(skb, false). The skb mark
is now kept on encapsulated packets. The commit message says the IPIP
tunneling fast path does not cross a netns.

> diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
> index 644e70fd6773b..001abfae5a1ff 100644
> --- a/net/netfilter/nf_flow_table_ip.c
> +++ b/net/netfilter/nf_flow_table_ip.c
> @@ -630,7 +630,7 @@ static int nf_flow_tunnel_ip_push(struct net *net, struct sk_buff *skb,
>  	if (err)
>  		return err;
>  
> -	skb_scrub_packet(skb, true);
> +	skb_scrub_packet(skb, false);

[Severity: Medium]
This is a pre-existing issue and was not introduced by this patch. Is
the hard-coded xnet = false in nf_flow_tunnel_ip_push() and
nf_flow_tunnel_ip6_push() correct only because the fast path routes in
dev_net(dev) and not in the tunnel's link netns?

The slow path routes in tunnel->net or t->net, and it decides xnet per
device:

net/ipv4/ip_tunnel.c:ip_tunnel_xmit() {
    ...
	iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, proto, tos, ttl,
		      df, !net_eq(tunnel->net, dev_net(dev)), 0);
    ...
}

net/ipv6/ip6_tunnel.c:ip6_tnl_xmit() {
    ...
	skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev)));
    ...
}

The forward path helpers that admit the flow look up the outer route in
the device netns instead:

net/ipv4/ipip.c:ipip_fill_forward_path() {
    ...
	rt = ip_route_output(dev_net(ctx->dev), tiph->daddr, tiph->saddr,
			     inet_dsfield_to_dscp(tiph->tos),
			     tunnel->parms.link, RT_SCOPE_UNIVERSE);
    ...
}

net/ipv6/ip6_tunnel.c:ip6_tnl_fill_forward_path() {
    ...
	dst = ip6_route_output(dev_net(ctx->dev), NULL, &fl6);
    ...
}

Neither helper rejects a tunnel whose link netns differs from
dev_net(dev). nft_dev_path_info() does not reject it either, because it
only checks nft_flowtable_find_dev(info->dev, ft).

Take an ipip or ip6tnl device whose link netns is A but which lives in
netns B. Could the offloaded packets of a flow be routed with B's routing
table, while the slow-path packets of the same flow leave through A's
underlay?

With this patch the two paths would also handle skb->mark differently.
The slow path clears it and the fast path keeps it.

Could ipip_fill_forward_path() and ip6_tnl_fill_forward_path() return
-EOPNOTSUPP when the tunnel's link netns differs from dev_net(dev)? That
would also keep xnet = false correct. Another option is to route in
tunnel->net and carry the real xnet value through flow_offload_tunnel.

> @@ -702,7 +702,7 @@ static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,
>  	mtu = dst_mtu(dst) - sizeof(*ip6h);
>  	mtu = max(mtu, IPV6_MIN_MTU);
>  	skb_dst_update_pmtu_no_confirm(skb, mtu);
> -	skb_scrub_packet(skb, true);
> +	skb_scrub_packet(skb, false);

[Severity: Low]
This isn't a bug, but the skb_scrub_packet() call in
nf_flow_tunnel_ip6_push() came from 93cf357fa797 ("netfilter: flowtable:
Add IP6IP6 tx sw acceleration"). It did not come from the commit named
in the tag:

  Fixes: d30301ba4b07 ("netfilter: flowtable: Add IPIP tx sw acceleration")

The commit message also only talks about "IPIP tunneling support".
However, this hunk changes the IPv6-outer encapsulation path too, so
skb->mark is now kept on those packets as well.

Earlier in the same series, "netfilter: flowtable: fix pmtu discovery in
nf_flow_tunnel_ip6ip6_push()" uses Fixes: 93cf357fa797 for this same
function.

Should a Fixes: tag for 93cf357fa797 be added, and should the commit
message mention the IPv6-outer path?

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 12/15] netfilter: flowtable: tunnel device poisons h_source of fast-path frames
  2026-11-13  3:12 ` [PATCH net-next 12/15] netfilter: flowtable: tunnel device poisons h_source of fast-path frames Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] The commit message's only reproduction cannot happen on the tree
  this patch targets.

--

netfilter: flowtable: tunnel device poisons h_source of fast-path frames

This commit stops nft_dev_path_info() from copying the dev_addr of a
DEV_PATH_TUN device into info->h_source. On a tunnel device, dev_addr is
the local tunnel IP address and not an Ethernet address. Before this
change, those bytes became the source MAC of fast-path frames.

> Seen on a DS-Lite setup, LAN -> ip6tnl (IPv4 inside IPv6) -> PPPoE ->
> 802.1Q VLAN -> Ethernet, with conntrack flows offloaded through an
> nftables flowtable ('flow add @ft'). The first packets of a flow, still
> on the slow path, and the fast-path packets of the same flow differ only
> in the Ethernet source address:
>
>   slow path:  d8:58:d7:00:2e:bd > 80:ac:ac:39:ef:c2, 802.1Q vlan 35,
>               PPPoE [ses 0x454] IP6 2a01:110f:4328:c400::1 >
>               2a01:1000:0:1::2216 next-header IPIP, 192.0.0.2.40010 >
>               1.1.1.1.53
>   fast path:  2a:01:11:0f:43:28 > 80:ac:ac:39:ef:c2, same frame otherwise

[Severity: Low]
Can this DS-Lite setup reach the flowtable fast path on this tree?

ip6_tnl_fill_forward_path() starts by rejecting any inner flow that is not
IPv6:

net/ipv6/ip6_tunnel.c:ip6_tnl_fill_forward_path() {
    ...
	if (ctx->ether_type != cpu_to_be16(ETH_P_IPV6))
		return -EOPNOTSUPP;
    ...
	path->tun.inner_proto = IPPROTO_IPV6;
    ...
}

For a forwarded IPv4 flow, ctx->ether_type comes from pkt->ethertype.
nft_set_pktinfo() sets that to skb->protocol, which is ETH_P_IP here:

include/net/netfilter/nf_tables.h:nft_set_pktinfo() {
    ...
	pkt->ethertype = pkt->skb->protocol;
    ...
}

So dev_fill_forward_path() fails and nft_flow_route() returns -ENOENT. No
flow entry is created, and nft_dev_path_info() never sees a DEV_PATH_TUN
entry for this topology.

inner_proto is also hard-coded to IPPROTO_IPV6. Given that, could the
offload path on this tree produce the "next-header IPIP" fast-path frame
quoted above?

The check came from commit 5592223f3d4e45 ("net: netfilter: add
ether_type to net_device_path_ctx and use it"). That commit says IPv4
over IPv6 is "currently not support[ed]". Was the capture taken on an
older kernel, or on one with local changes?

> The PPPoE endpoint binds the session to the
> subscriber MAC and silently drops the frames, the effect is that every
> TCP/UDP flow dies the moment it is offloaded, while ICMP, which is never
> offloaded, keeps working.

The code change itself looks correct. On this tree, the h_source
poisoning still seems reachable in two ways:

  - ipip_fill_forward_path(), which accepts ETH_P_IP inner flows and
    emits DEV_PATH_TUN with dev_addr set to the local IPv4 address

  - ip6tnl carrying an IPv6 inner flow

Either one needs a PPPoE entry later on the path, or a hw-offload
flowtable on an Ethernet device. Then xmit_type is
FLOW_OFFLOAD_XMIT_DIRECT, and info.h_source is copied into
route->tuple[dir].out.h_source.

Could the commit message describe one of those reproducible cases
instead? Alternatively, it could note that the DS-Lite observation came
from a kernel without commit 5592223f3d4e45.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH net-next 15/15] selftests: netfilter: add UDP flowtable offload test
  2026-11-13  3:12 ` [PATCH net-next 15/15] selftests: netfilter: add UDP flowtable offload test Pablo Neira Ayuso
@ 2026-11-14  3:23   ` netdev-bot+sashiko
  0 siblings, 0 replies; 30+ messages in thread
From: netdev-bot+sashiko @ 2026-11-14  3:23 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
	ja

Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 3

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] The test runs the UDP flowtable checksum code but cannot
  detect a bug in it.
- [Low] The header comment in nft_flowtable_udp.sh claims coverage the
  test does not provide.
- [Low] The "IPv4 UDP masquerade" and "IPv6 UDP masquerade" cases (lines
  235-236) never check that NAT actually happened.
- [Low] listener_ready() is supposed to wait for both UDP listeners but
  passes as soon as either one is bound.

--

selftests: netfilter: add UDP flowtable offload test

This adds nft_flowtable_udp.sh, which sends a burst of UDP echoes through
an ingress flowtable for IPv4 and IPv6, with and without masquerade. It
checks that the payload comes back intact, that the forward chain sees
only the handshake, and that conntrack marks the flow OFFLOAD and counts
every datagram.

> diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable_udp.sh b/tools/testing/selftests/net/netfilter/nft_flowtable_udp.sh
> new file mode 100755
> index 0000000000000..e0f05c76bd8ec
> --- /dev/null
> +++ b/tools/testing/selftests/net/netfilter/nft_flowtable_udp.sh
> @@ -0,0 +1,238 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# nft_flowtable.sh offloads TCP only. These UDP paths therefore stay at
> +# zero coverage in that run:
> +#
> +#   nf_flow_tuple_ip / nf_flow_tuple_ipv6          IPPROTO_UDP
> +#   nf_flow_nat_ip_udp / nf_flow_nat_port_udp      IPv4 NAT checksum
> +#   nf_flow_snat_ipv6 / nf_flow_dnat_ipv6          IPv6 NAT, including UDP
> +#   flow_offload_get_timeout / flow_offload_fixup_ct for UDP

[Severity: Low]
Is this list accurate for nf_flow_dnat_ipv6 and flow_offload_fixup_ct?

The only NAT rule the test adds is a postrouting masquerade, which is
source NAT only. flow_offload_alloc() sets NF_FLOW_DNAT only when the
conntrack has IPS_DST_NAT:

net/netfilter/nf_flow_table_core.c:flow_offload_alloc() {
    ...
	if (ct->status & IPS_DST_NAT)
		__set_bit(NF_FLOW_DNAT, &flow->flags);
    ...
}

nf_flow_nat_ipv6() calls the DNAT helpers only when that bit is set:

	if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
		nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir);
		nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir);

So nf_flow_dnat_ipv6() never seems to run in this test.

The only caller of flow_offload_fixup_ct() is flow_offload_teardown().
The UDP offload timeout is 30 seconds, the test finishes in a few seconds,
and no route or device changes. Teardown only happens when cleanup_all_ns
runs from the EXIT trap, after PASS/FAIL has been printed. Nothing checks
the conntrack state or timeout after that point.

A minor point: tools/testing/selftests/bpf/prog_tests/xdp_flowtable.c
already adds UDP flows to a flowtable. The comment does limit its "zero
coverage" claim to the nft_flowtable.sh run, though.

Could the comment be corrected, or could a DNAT case and a teardown/fixup
check be added?

[ ... ]

> +listener_ready() {
> +	ss -N "$ns2" -uln -H "sport = :$port" | grep -q "$port"
> +}
> +
> +ip netns exec "$ns2" socat -T 60 \
> +	UDP4-LISTEN:"$port",reuseaddr,fork EXEC:cat >/dev/null 2>&1 &
> +ip netns exec "$ns2" socat -T 60 \
> +	UDP6-LISTEN:"$port",reuseaddr,ipv6only=1,fork EXEC:cat >/dev/null 2>&1 &
> +
> +if ! busywait 3000 listener_ready; then

[Severity: Low]
Can listener_ready() succeed when only one of the two listeners is bound?

The two socat processes start in parallel, and grep -q succeeds on the
first matching socket line. So busywait can return once 0.0.0.0:12345 is
bound, before the UDP6 socat has bound [::]:12345.

On a heavily loaded machine, the "IPv6 UDP" case could then send its
first datagram before the IPv6 listener exists. It would get ICMPv6 port
unreachable and fail with "FAIL: packet 0 timeout", which has nothing to
do with the flowtable. The 32 IPv4 round trips that run first make this
unlikely in practice.

Would counting the matching sockets (at least two), or checking ss -4 and
ss -6 separately, close this gap?

> +
> +udp_exchange() {

[ ... ]

> +	i=0
> +	while [ "$i" -lt "$count" ]; do
> +		msg=$(printf '%04d' "$i")
> +		printf '%s' "$msg" >&"${SO[1]}"
> +		if ! IFS= read -r -n 4 -t 2 got <&"${SO[0]}"; then
> +			echo "FAIL: packet $i timeout" >&2
> +			kill "$SO_PID" 2>/dev/null || true
> +			wait "$SO_PID" 2>/dev/null || true
> +			return 1
> +		fi
> +		if [ "$got" != "$msg" ]; then

[Severity: Medium]
Can this payload comparison catch a broken UDP checksum update in the
flowtable NAT code?

The commit message lists "the UDP checksum update" as newly covered. The
header comment labels nf_flow_nat_ip_udp / nf_flow_nat_port_udp as "IPv4
NAT checksum".

All links are veth pairs with default features, and VETH_FEATURES
includes NETIF_F_HW_CSUM. So the datagrams socat sends stay
CHECKSUM_PARTIAL from end to end.

On the router, nf_flow_nat_ip_udp() takes this branch, which only adjusts
the pseudo-header seed:

net/netfilter/nf_flow_table_ip.c:nf_flow_nat_ip_udp() {
	if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
		inet_proto_csum_replace4(&udph->check, skb, addr,
					 new_addr, true);
    ...
}

nf_flow_nat_port_udp() calls inet_proto_csum_replace2(..., false). For
CHECKSUM_PARTIAL skbs, that leaves uh->check untouched.

The receiver in ns2 (or ns1 for replies) treats CHECKSUM_PARTIAL as
already valid in skb_csum_unnecessary():

	(skb->ip_summed == CHECKSUM_PARTIAL &&
	 skb_checksum_start_offset(skb) >= 0));

A wrong udph->check written by the flowtable would still be delivered.
The 4-byte payload would still match, and the test would still print
PASS. The CHECKSUM_NONE full-checksum branch never runs at all.

Would turning off tx checksum offload on the eth0 devices in ns1 and ns2
(for example ethtool -K eth0 tx off) force a real checksum that the
receiver verifies after the flowtable rewrites it?

[ ... ]

> +if ! ip netns exec "$nsr" nft add rule inet nat postrouting \
> +		oifname "veth1" masquerade; then
> +	echo "FAIL: could not add masquerade rule" >&2
> +	exit 1
> +fi
> +
> +run_case "IPv4 UDP masquerade" 4 10.0.2.99 5403
> +run_case "IPv6 UDP masquerade" 6 dead:2::99 5404

[Severity: Low]
Do these masquerade cases check that NAT actually happened?

run_case() checks three things: [OFFLOAD] in the conntrack line, the slow
counter, and the orig/reply packet counts. None of these differ between a
NATed flow and a plain one. ns2 has a default route back through nsr, so
the echo works with or without SNAT.

If the conntrack entry were not NATed, these cases would pass just like
the plain ones, without ever reaching nf_flow_snat_ip() or
nf_flow_snat_ipv6().

There is one partial safeguard. If the flowtable failed to apply an
existing SNAT binding, the slow counter would probably go over 8. A
missing binding would still go unnoticed.

Could run_case() check the conntrack line for a reply tuple that points
at the router, such as dst=10.0.2.1 or dst=dead:2::1?

> +
> +exit $ret

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261113031251.2437074-2-pablo%40netfilter.org

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2026-11-14  3:23 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-13  3:12 [PATCH net-next 00/15] Netfilter updates for net-next Pablo Neira Ayuso
2026-11-13  3:12 ` [PATCH net-next 01/15] netfilter: flowtable: do not hw offload flows in teardown state Pablo Neira Ayuso
2026-11-13  3:17   ` netdev-bot+sinfo
2026-11-13  3:24     ` Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 02/15] netfilter: flowtable: fix pmtu discovery in nf_flow_tunnel_ip6ip6_push() Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 03/15] netfilter: flowtable: retrieve stats on flow deletion Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 04/15] net: ethernet: mtk_ppe: update " Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 05/15] net: airoha: " Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 06/15] netfilter: xt_time: fix monthday computation for dates past 2039 Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 07/15] netfilter: nft_flow_offload: do not offload multicast Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 08/15] netfilter: lwtunnel: expose read-only sysctl nf_hooks_lwtunnel for non init-netns Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 09/15] netfilter: flowtable: purge entries immediately when destroying the flowtable Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 10/15] selftests: netfilter: nf_queue: drop the no-op loop initializer Pablo Neira Ayuso
2026-11-13  3:12 ` [PATCH net-next 11/15] netfilter: flowtable: do not scrub as crossing netns Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 12/15] netfilter: flowtable: tunnel device poisons h_source of fast-path frames Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko
2026-11-13  3:12 ` [PATCH net-next 13/15] netfilter: flowtable: do not clear the offload bit twice Pablo Neira Ayuso
2026-11-13  3:12 ` [PATCH net-next 14/15] netfilter: nfnetlink_queue: make skb writable before writing NFQA_L2HDR Pablo Neira Ayuso
2026-11-13  3:12 ` [PATCH net-next 15/15] selftests: netfilter: add UDP flowtable offload test Pablo Neira Ayuso
2026-11-14  3:23   ` netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox