[PATCH nf v4 1/1] netfilter: nf_dup: reject TEE and IPv4/IPv6/netdev nft dup in userns
flat view
DORMANTno replies
SUPERSEDED
From: Zihan Xi <hidden>
Date: 2026-09-26 09:19:50
Also in:
netfilter-devel, stable
Subsystem:
netfilter, networking [general], the rest · Maintainers:
Pablo Neira Ayuso, Florian Westphal, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Revision v4 of 3 in this series.
Revisions (3)
- v4 current
- v5 [diff vs current]
- v6 [diff vs current]
TEE and the IPv4, IPv6, and netdev nft dup expressions allow packet
duplication in network namespaces owned by non-initial user
namespaces. No use case for these features in such namespaces has
been identified.
Reject these targets and expressions with -EPERM during rule or
expression setup. Keep packet-processing fast paths unchanged.
This restricts duplication only in non-initial-userns-owned network
namespaces. It does not change the existing asynchronous recursion
behavior in init_user_ns-owned network namespaces.
Fixes: cd58bcd9787e ("netfilter: xt_TEE: have cloned packet travel through Xtables too")
Fixes: d877f07112f1 ("netfilter: nf_tables: add nft_dup expression")
Fixes: 502061f81d3e ("netfilter: nf_tables: add packet duplication to the netdev family")
Cc: stable@vger.kernel.org
Reported-by: Vega <redacted>
Assisted-by: LLM
Co-developed-by: Luxing Yin <redacted>
Signed-off-by: Luxing Yin <redacted>
Signed-off-by: Zihan Xi <redacted>
---
changes in v4:
- correct Fixes attribution for the TEE, IPv4/IPv6 nft dup, and netdev
nft dup frontends; fcd53c51d037 only added a synchronous guard
- clarify that this restricts non-initial user namespaces and does not
fix asynchronous recursion in init_user_ns-owned network namespaces
- state explicitly that the netdev loop was not runtime-reproduced
- v3 Link:
https://lore.kernel.org/all/cover.1790042930.git.zihanx@nebusec.ai/ (local)
changes in v3:
- reroll the fix against the latest nf.git main after no follow-up on v2
- add the Co-developed-by trailer and matching Signed-off-by
- v2 Link:
https://lore.kernel.org/all/cover.1788425393.git.zihanx@nebusec.ai/ (local)
changes in v2:
- drop the persistent struct sk_buff::nf_duplicated field and nf_copy()
change from v1
- disable IPv4/IPv6 duplication in non-initial user namespaces
- v1 Link:
https://lore.kernel.org/all/cover.1787903722.git.zihanx@nebusec.ai/ (local)
---
net/ipv4/netfilter/nft_dup_ipv4.c | 4 ++++
net/ipv6/netfilter/nft_dup_ipv6.c | 4 ++++
net/netfilter/nft_dup_netdev.c | 4 ++++
net/netfilter/xt_TEE.c | 4 ++++
4 files changed, 16 insertions(+)
diff --git a/net/ipv4/netfilter/nft_dup_ipv4.c b/net/ipv4/netfilter/nft_dup_ipv4.c
index d53a65ddbd7b..db62e30456e4 100644
--- a/net/ipv4/netfilter/nft_dup_ipv4.c
+++ b/net/ipv4/netfilter/nft_dup_ipv4.c@@ -7,6 +7,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/netlink.h> +#include <linux/user_namespace.h> #include <linux/netfilter.h> #include <linux/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
@@ -37,6 +38,9 @@ static int nft_dup_ipv4_init(const struct nft_ctx *ctx, struct nft_dup_ipv4 *priv = nft_expr_priv(expr); int err; + if (ctx->net->user_ns != &init_user_ns) + return -EPERM; + if (tb[NFTA_DUP_SREG_ADDR] == NULL) return -EINVAL;
diff --git a/net/ipv6/netfilter/nft_dup_ipv6.c b/net/ipv6/netfilter/nft_dup_ipv6.c
index 95ec27b3971c..6219ef57633e 100644
--- a/net/ipv6/netfilter/nft_dup_ipv6.c
+++ b/net/ipv6/netfilter/nft_dup_ipv6.c@@ -7,6 +7,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/netlink.h> +#include <linux/user_namespace.h> #include <linux/netfilter.h> #include <linux/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
@@ -35,6 +36,9 @@ static int nft_dup_ipv6_init(const struct nft_ctx *ctx, struct nft_dup_ipv6 *priv = nft_expr_priv(expr); int err; + if (ctx->net->user_ns != &init_user_ns) + return -EPERM; + if (tb[NFTA_DUP_SREG_ADDR] == NULL) return -EINVAL;
diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c
index 06866799e946..8d9dfd18475c 100644
--- a/net/netfilter/nft_dup_netdev.c
+++ b/net/netfilter/nft_dup_netdev.c@@ -7,6 +7,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/netlink.h> +#include <linux/user_namespace.h> #include <linux/netfilter.h> #include <linux/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
@@ -37,6 +38,9 @@ static int nft_dup_netdev_init(const struct nft_ctx *ctx, { struct nft_dup_netdev *priv = nft_expr_priv(expr); + if (ctx->net->user_ns != &init_user_ns) + return -EPERM; + if (tb[NFTA_DUP_SREG_DEV] == NULL) return -EINVAL;
diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c
index 5d34ceb893ed..51c643987526 100644
--- a/net/netfilter/xt_TEE.c
+++ b/net/netfilter/xt_TEE.c@@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/skbuff.h> #include <linux/route.h> +#include <linux/user_namespace.h> #include <linux/netfilter/x_tables.h> #include <net/net_namespace.h> #include <net/netns/generic.h>
@@ -95,6 +96,9 @@ static int tee_tg_check(const struct xt_tgchk_param *par) struct xt_tee_tginfo *info = par->targinfo; struct xt_tee_priv *priv; + if (par->net->user_ns != &init_user_ns) + return -EPERM; + /* 0.0.0.0 and :: not allowed */ if (memcmp(&info->gw, &tee_zero_address, sizeof(tee_zero_address)) == 0)
--
2.43.0