[PATCH nf v3 1/1] netfilter: nf_dup: disable duplication in user namespaces

From: Zihan Xi

Date: Tue Sep 22 2026 - 05:54:14 EST


nf_dup_ipv4() and nf_dup_ipv6() send a cloned packet through
ip_local_out() or ip6_local_out(), so the clone can traverse netfilter
hooks again. A network namespace owned by a non-initial user namespace
can combine NFQUEUE with TEE or nftables dup and retain the clone until
a later verdict resumes it. The transient in_nf_duplicate task guard
has already been cleared by then, so the resumed clone can be
duplicated again and generate packets without bound.

There is no sensible use case for packet duplication in a user
namespace. Skip IPv4 and IPv6 duplication when the network namespace
is not owned by the initial user namespace. Keep the existing TEE/dup
behavior in the initial user namespace.

Fixes: cd58bcd9787e ("netfilter: xt_TEE: have cloned packet travel through Xtables too")
Cc: stable@xxxxxxxxxxxxxxx
Reported-by: Vega <vega@xxxxxxxxxx>
Assisted-by: LLM
Co-developed-by: Luxing Yin <root@xxxxxxxxxx>
Signed-off-by: Luxing Yin <root@xxxxxxxxxx>
Signed-off-by: Zihan Xi <zihanx@xxxxxxxxxx>
---
changes in v3:
- rerolled the unchanged fix against the latest nf.git main
after no follow-up was received on v2
- add the required Co-developed-by trailer and matching
Signed-off-by
- v2 Link: https://lore.kernel.org/all/cover.1788425393.git.zihanx@xxxxxxxxxx/
changes in v2:
- drop the persistent struct sk_buff::nf_duplicated field and nf_copy()
change from v1
- disable IPv4/IPv6 duplication in network namespaces owned by a
non-initial user namespace, as preferred on review
- v1 Link: https://lore.kernel.org/all/cover.1787903722.git.zihanx@xxxxxxxxxx/

net/ipv4/netfilter/nf_dup_ipv4.c | 3 +++
net/ipv6/netfilter/nf_dup_ipv6.c | 3 +++
2 files changed, 6 insertions(+)

diff --git a/net/ipv4/netfilter/nf_dup_ipv4.c b/net/ipv4/netfilter/nf_dup_ipv4.c
index 9a773502f10ac..c33dae248c477 100644
--- a/net/ipv4/netfilter/nf_dup_ipv4.c
+++ b/net/ipv4/netfilter/nf_dup_ipv4.c
@@ -53,6 +53,9 @@ void nf_dup_ipv4(struct net *net, struct sk_buff *skb, unsigned int hooknum,
{
struct iphdr *iph;

+ if (net->user_ns != &init_user_ns)
+ return;
+
local_bh_disable();
if (current->in_nf_duplicate)
goto out;
diff --git a/net/ipv6/netfilter/nf_dup_ipv6.c b/net/ipv6/netfilter/nf_dup_ipv6.c
index 6da3102b7c1b3..a5f6f074a7e91 100644
--- a/net/ipv6/netfilter/nf_dup_ipv6.c
+++ b/net/ipv6/netfilter/nf_dup_ipv6.c
@@ -47,6 +47,9 @@ static bool nf_dup_ipv6_route(struct net *net, struct sk_buff *skb,
void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum,
const struct in6_addr *gw, int oif)
{
+ if (net->user_ns != &init_user_ns)
+ return;
+
local_bh_disable();
if (current->in_nf_duplicate)
goto out;
--
2.43.0