All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
* [Devel] [PATCH DRAFT vz10 0/5] Enable GRE ERSPAN inside Containers
@ 2026-08-12 13:03 Konstantin Khorenko
  2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 1/5] Revert "ve/net/gre: Disable ERSPAN support in ip_gre module" Konstantin Khorenko
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Konstantin Khorenko @ 2026-08-12 13:03 UTC (permalink / raw)


Currently ERSPAN is compiled out under CONFIG_VE by commit a6adc8063402
("ve/net/gre: Disable ERSPAN support in ip_gre module"), because at the
time it was not wired into the per-Container GRE infrastructure. This
series reverts that stub-out and integrates ERSPAN into the Container
framework for both IPv4 and IPv6, gated by a new VE_FEATURE_ERSPAN bit,
and fixes a couple of pre-existing defects in the IPv6 GRE
containerization found along the way.

ERSPAN is a Cisco traffic-mirroring protocol layered on GRE. It is a
separate device type from ip_gre/gretap, so it gets its own feature bit
rather than reusing VE_FEATURE_IPGRE. Like the other tunnel features it
is disabled by default for Containers (VE_FEATURES_DEF) and enabled for
the host (init_ve has all features set), so host behaviour is unchanged.

The series is arranged so that the pre-existing bug fixes are separate,
self-contained and backportable, with proper Fixes: tags:

  Feature work:
    1  Revert the CONFIG_VE stub-out of ERSPAN.
    2  IPv4: enable erspan in a CT under VE_FEATURE_ERSPAN
       (NETIF_F_VIRTUAL on the device, per-net gating, -EACCES on
       newlink/changelink when the feature is off).
    5  IPv6: enable ip6erspan under VE_FEATURE_ERSPAN. ip6erspan shares
       the per-net state with ip6gre/ip6gretap, so the shared ign is
       allocated when either VE_FEATURE_IPGRE or VE_FEATURE_ERSPAN is
       set and each rtnl op checks its own bit.

  Pre-existing fixes (IPv6):
    3  ip6gretap never set NETIF_F_VIRTUAL, so it could not be created
       in a CT even with VE_FEATURE_IPGRE.
       Fixes: 85f1620c362c
    4  ip6gre_newlink()/ip6erspan_newlink() dereference the per-net data
       before the VE_FEATURE_IPGRE guard that lived in newlink_common();
       a CT without the feature could NULL-deref and crash the host.
       Fixes: 1e1433063539

The six rtnl link types handled here (gre, gretap, erspan, ip6gre,
ip6gretap, ip6erspan) and the positive/negative test matrix (per-feature
gating for IPv4 and IPv6, plus the no-crash check for patch 4) are
described in the ticket. Follow-ups tracked there: a vzctl/libvzctl name
for the new bit, a dedicated kselftest for the IPGRE/ERSPAN gating, and
CRIU support for CT migration with ERSPAN devices.

Compile-tested (net/ipv4/ip_gre.o, net/ipv6/ip6_gre.o). Not yet
runtime-tested inside a Container.

https://virtuozzo.atlassian.net/browse/VSTOR-141173

Konstantin Khorenko (5):
  Revert "ve/net/gre: Disable ERSPAN support in ip_gre module"
  ve/net/gre: Enable ERSPAN support in Containers under
    VE_FEATURE_ERSPAN
  ve/net/ip6_gre: Mark ip6gretap devices as movable into a Container
  ve/net/ip6_gre: Fix NULL deref when creating ip6gre/ip6erspan without
    VE_FEATURE_IPGRE
  ve/net/ip6_gre: Enable ip6erspan support in Containers under
    VE_FEATURE_ERSPAN

 include/uapi/linux/vzcalluser.h |  1 +
 net/ipv4/ip_gre.c               | 59 ++++++++++++++-------------------
 net/ipv6/ip6_gre.c              | 30 +++++++++++------
 3 files changed, 46 insertions(+), 44 deletions(-)

-- 
2.43.0


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

* [Devel] [PATCH DRAFT vz10 1/5] Revert "ve/net/gre: Disable ERSPAN support in ip_gre module"
  2026-08-12 13:03 [Devel] [PATCH DRAFT vz10 0/5] Enable GRE ERSPAN inside Containers Konstantin Khorenko
@ 2026-08-12 13:03 ` Konstantin Khorenko
  2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 2/5] ve/net/gre: Enable ERSPAN support in Containers under VE_FEATURE_ERSPAN Konstantin Khorenko
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Khorenko @ 2026-08-12 13:03 UTC (permalink / raw)


This reverts commit a6adc8063402a38c6d951113453df6bf8ddcfb0e.

ERSPAN was disabled under CONFIG_VE because at the time it was not wired
into the per-Container GRE infrastructure: erspan devices did not set
NETIF_F_VIRTUAL, so register_netdevice() would reject them inside a CT,
and erspan_net_ops was not gated by any VE feature bit. Rather than
carry the feature permanently disabled, bring the code back so that the
following commit can properly containerize ERSPAN under a dedicated
VE feature bit.

https://virtuozzo.atlassian.net/browse/VSTOR-141173

Feature: net: disable ERSPAN support in ip_gre module
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 net/ipv4/ip_gre.c | 33 ++++-----------------------------
 1 file changed, 4 insertions(+), 29 deletions(-)

diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index f618aae250334..d776eb8d9f76c 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -1134,7 +1134,6 @@ static int ipgre_tap_validate(struct nlattr *tb[], struct nlattr *data[],
 	return ipgre_tunnel_validate(tb, data, extack);
 }
 
-#ifndef CONFIG_VE
 static int erspan_validate(struct nlattr *tb[], struct nlattr *data[],
 			   struct netlink_ext_ack *extack)
 {
@@ -1174,7 +1173,6 @@ static int erspan_validate(struct nlattr *tb[], struct nlattr *data[],
 
 	return 0;
 }
-#endif
 
 static int ipgre_netlink_parms(struct net_device *dev,
 				struct nlattr *data[],
@@ -1245,7 +1243,6 @@ static int ipgre_netlink_parms(struct net_device *dev,
 	return 0;
 }
 
-#ifndef CONFIG_VE
 static int erspan_netlink_parms(struct net_device *dev,
 				struct nlattr *data[],
 				struct nlattr *tb[],
@@ -1289,7 +1286,6 @@ static int erspan_netlink_parms(struct net_device *dev,
 
 	return 0;
 }
-#endif
 
 /* This function returns true when ENCAP attributes are present in the nl msg */
 static bool ipgre_netlink_encap_parms(struct nlattr *data[],
@@ -1428,7 +1424,6 @@ static int ipgre_newlink(struct net *src_net, struct net_device *dev,
 	return ip_tunnel_newlink(dev, tb, &p, fwmark);
 }
 
-#ifndef CONFIG_VE
 static int erspan_newlink(struct net *src_net, struct net_device *dev,
 			  struct nlattr *tb[], struct nlattr *data[],
 			  struct netlink_ext_ack *extack)
@@ -1446,7 +1441,6 @@ static int erspan_newlink(struct net *src_net, struct net_device *dev,
 		return err;
 	return ip_tunnel_newlink(dev, tb, &p, fwmark);
 }
-#endif
 
 static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
 			    struct nlattr *data[],
@@ -1477,7 +1471,6 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
 	return 0;
 }
 
-#ifndef CONFIG_VE
 static int erspan_changelink(struct net_device *dev, struct nlattr *tb[],
 			     struct nlattr *data[],
 			     struct netlink_ext_ack *extack)
@@ -1504,7 +1497,6 @@ static int erspan_changelink(struct net_device *dev, struct nlattr *tb[],
 
 	return 0;
 }
-#endif
 
 static size_t ipgre_get_size(const struct net_device *dev)
 {
@@ -1602,7 +1594,6 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	return -EMSGSIZE;
 }
 
-#ifndef CONFIG_VE
 static int erspan_fill_info(struct sk_buff *skb, const struct net_device *dev)
 {
 	struct ip_tunnel *t = netdev_priv(dev);
@@ -1643,7 +1634,6 @@ static void erspan_setup(struct net_device *dev)
 	ip_tunnel_setup(dev, erspan_net_id);
 	t->erspan_ver = 1;
 }
-#endif
 
 static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = {
 	[IFLA_GRE_LINK]		= { .type = NLA_U32 },
@@ -1699,7 +1689,6 @@ static struct rtnl_link_ops ipgre_tap_ops __read_mostly = {
 	.get_link_net	= ip_tunnel_get_link_net,
 };
 
-#ifndef CONFIG_VE
 static struct rtnl_link_ops erspan_link_ops __read_mostly = {
 	.kind		= "erspan",
 	.maxtype	= IFLA_GRE_MAX,
@@ -1714,7 +1703,6 @@ static struct rtnl_link_ops erspan_link_ops __read_mostly = {
 	.fill_info	= erspan_fill_info,
 	.get_link_net	= ip_tunnel_get_link_net,
 };
-#endif
 
 struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
 					u8 name_assign_type)
@@ -1786,7 +1774,6 @@ static struct pernet_operations ipgre_tap_net_ops = {
 	.size = sizeof(struct ip_tunnel_net),
 };
 
-#ifndef CONFIG_VE
 static int __net_init erspan_init_net(struct net *net)
 {
 	return ip_tunnel_init_net(net, erspan_net_id,
@@ -1806,7 +1793,6 @@ static struct pernet_operations erspan_net_ops = {
 	.id   = &erspan_net_id,
 	.size = sizeof(struct ip_tunnel_net),
 };
-#endif
 
 static int __init ipgre_init(void)
 {
@@ -1821,11 +1807,11 @@ static int __init ipgre_init(void)
 	err = register_pernet_device(&ipgre_tap_net_ops);
 	if (err < 0)
 		goto pnet_tap_failed;
-#ifndef CONFIG_VE
+
 	err = register_pernet_device(&erspan_net_ops);
 	if (err < 0)
 		goto pnet_erspan_failed;
-#endif
+
 	err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
 	if (err < 0) {
 		pr_info("%s: can't add protocol\n", __func__);
@@ -1839,27 +1825,22 @@ static int __init ipgre_init(void)
 	err = rtnl_link_register(&ipgre_tap_ops);
 	if (err < 0)
 		goto tap_ops_failed;
-#ifndef CONFIG_VE
+
 	err = rtnl_link_register(&erspan_link_ops);
 	if (err < 0)
 		goto erspan_link_failed;
-#endif
 
 	return 0;
 
-#ifndef CONFIG_VE
 erspan_link_failed:
-#endif
 	rtnl_link_unregister(&ipgre_tap_ops);
 tap_ops_failed:
 	rtnl_link_unregister(&ipgre_link_ops);
 rtnl_link_failed:
 	gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
 add_proto_failed:
-#ifndef CONFIG_VE
 	unregister_pernet_device(&erspan_net_ops);
 pnet_erspan_failed:
-#endif
 	unregister_pernet_device(&ipgre_tap_net_ops);
 pnet_tap_failed:
 	unregister_pernet_device(&ipgre_net_ops);
@@ -1870,15 +1851,11 @@ static void __exit ipgre_fini(void)
 {
 	rtnl_link_unregister(&ipgre_tap_ops);
 	rtnl_link_unregister(&ipgre_link_ops);
-#ifndef CONFIG_VE
 	rtnl_link_unregister(&erspan_link_ops);
-#endif
 	gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
 	unregister_pernet_device(&ipgre_tap_net_ops);
 	unregister_pernet_device(&ipgre_net_ops);
-#ifndef CONFIG_VE
 	unregister_pernet_device(&erspan_net_ops);
-#endif
 }
 
 module_init(ipgre_init);
@@ -1887,9 +1864,7 @@ MODULE_DESCRIPTION("IPv4 GRE tunnels over IP library");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_RTNL_LINK("gre");
 MODULE_ALIAS_RTNL_LINK("gretap");
-#ifndef CONFIG_VE
 MODULE_ALIAS_RTNL_LINK("erspan");
-MODULE_ALIAS_NETDEV("erspan0");
-#endif
 MODULE_ALIAS_NETDEV("gre0");
 MODULE_ALIAS_NETDEV("gretap0");
+MODULE_ALIAS_NETDEV("erspan0");
-- 
2.43.0


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

* [Devel] [PATCH DRAFT vz10 2/5] ve/net/gre: Enable ERSPAN support in Containers under VE_FEATURE_ERSPAN
  2026-08-12 13:03 [Devel] [PATCH DRAFT vz10 0/5] Enable GRE ERSPAN inside Containers Konstantin Khorenko
  2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 1/5] Revert "ve/net/gre: Disable ERSPAN support in ip_gre module" Konstantin Khorenko
@ 2026-08-12 13:03 ` Konstantin Khorenko
  2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 3/5] ve/net/ip6_gre: Mark ip6gretap devices as movable into a Container Konstantin Khorenko
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Khorenko @ 2026-08-12 13:03 UTC (permalink / raw)


Commit a6adc8063402 ("ve/net/gre: Disable ERSPAN support in ip_gre
module") compiled ERSPAN out under CONFIG_VE because it was not wired
into the per-Container GRE infrastructure. Its revert brings the code
back; this commit integrates ERSPAN into the Container framework so it
can be used inside a CT, gated by a dedicated feature bit.

ERSPAN is a Cisco-specific traffic mirroring protocol built on top of
GRE, but it is a separate device type from ip_gre/gretap and has its
own pernet id (erspan_net_id). Reusing VE_FEATURE_IPGRE would tie the
two together, so introduce a standalone VE_FEATURE_ERSPAN bit. It is
disabled by default for Containers (VE_FEATURES_DEF) and enabled for
the host (init_ve has all features set), so host behaviour is
unchanged.

Integration mirrors what is already done for ip_gre/gretap:

 - erspan_setup() marks the device NETIF_F_VIRTUAL so that
   register_netdevice() permits it inside a non-super VE;

 - erspan_init_net() frees its net_generic slot and skips device
   creation when the feature is off, so erspan_net_id is NULL for such
   a CT; the lookup path is already guarded against a NULL itn in
   ip_tunnel_lookup(), so the rx/error paths are safe;

 - device creation and reconfiguration (erspan_newlink/changelink)
   reject the operation with -EACCES when the feature is off, before
   reaching ip_tunnel_newlink() which would dereference the NULL
   net_generic slot.

The VE feature check is dropped from ipgre_newlink_encap_setup() and
done explicitly in each rtnl link operation via ve_feature_set(), so
the ip_gre/gretap paths keep checking VE_FEATURE_IPGRE while the erspan
paths check VE_FEATURE_ERSPAN.

https://virtuozzo.atlassian.net/browse/VSTOR-141173

Feature: net: ERSPAN support in Containers
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 include/uapi/linux/vzcalluser.h |  1 +
 net/ipv4/ip_gre.c               | 26 +++++++++++++++++++++-----
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/include/uapi/linux/vzcalluser.h b/include/uapi/linux/vzcalluser.h
index 000e3ee107add..a716e0b41ef30 100644
--- a/include/uapi/linux/vzcalluser.h
+++ b/include/uapi/linux/vzcalluser.h
@@ -49,6 +49,7 @@ struct vzctl_ve_configure {
 #define VE_FEATURE_NFSD		(1ULL << 8)
 #define VE_FEATURE_TIME		(1ULL << 9)
 #define VE_FEATURE_BPF		(1ULL << 10)
+#define VE_FEATURE_ERSPAN	(1ULL << 11)
 
 #define VE_FEATURES_OLD		(VE_FEATURE_SYSFS)
 #define VE_FEATURES_DEF		(VE_FEATURE_SYSFS | VE_FEATURE_DEF_PERMS)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index d776eb8d9f76c..b4db5112c3853 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -1390,11 +1390,6 @@ ipgre_newlink_encap_setup(struct net_device *dev, struct nlattr *data[])
 {
 	struct ip_tunnel_encap ipencap;
 
-#ifdef CONFIG_VE
-	if (!(dev_net(dev)->owner_ve->features & VE_FEATURE_IPGRE))
-		return -EACCES;
-#endif
-
 	if (ipgre_netlink_encap_parms(data, &ipencap)) {
 		struct ip_tunnel *t = netdev_priv(dev);
 		int err = ip_tunnel_encap_setup(t, &ipencap);
@@ -1414,6 +1409,9 @@ static int ipgre_newlink(struct net *src_net, struct net_device *dev,
 	__u32 fwmark = 0;
 	int err;
 
+	if (!ve_feature_set(dev_net(dev)->owner_ve, IPGRE))
+		return -EACCES;
+
 	err = ipgre_newlink_encap_setup(dev, data);
 	if (err)
 		return err;
@@ -1432,6 +1430,9 @@ static int erspan_newlink(struct net *src_net, struct net_device *dev,
 	__u32 fwmark = 0;
 	int err;
 
+	if (!ve_feature_set(dev_net(dev)->owner_ve, ERSPAN))
+		return -EACCES;
+
 	err = ipgre_newlink_encap_setup(dev, data);
 	if (err)
 		return err;
@@ -1451,6 +1452,9 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
 	__u32 fwmark = t->fwmark;
 	int err;
 
+	if (!ve_feature_set(dev_net(dev)->owner_ve, IPGRE))
+		return -EACCES;
+
 	err = ipgre_newlink_encap_setup(dev, data);
 	if (err)
 		return err;
@@ -1480,6 +1484,9 @@ static int erspan_changelink(struct net_device *dev, struct nlattr *tb[],
 	__u32 fwmark = t->fwmark;
 	int err;
 
+	if (!ve_feature_set(dev_net(dev)->owner_ve, ERSPAN))
+		return -EACCES;
+
 	err = ipgre_newlink_encap_setup(dev, data);
 	if (err)
 		return err;
@@ -1631,6 +1638,9 @@ static void erspan_setup(struct net_device *dev)
 	dev->netdev_ops = &erspan_netdev_ops;
 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+#ifdef CONFIG_VE
+	dev->ve_features = NETIF_F_VIRTUAL;
+#endif
 	ip_tunnel_setup(dev, erspan_net_id);
 	t->erspan_ver = 1;
 }
@@ -1776,6 +1786,12 @@ static struct pernet_operations ipgre_tap_net_ops = {
 
 static int __net_init erspan_init_net(struct net *net)
 {
+#ifdef CONFIG_VE
+	if (!(net->owner_ve->features & VE_FEATURE_ERSPAN)) {
+		net_generic_free(net, erspan_net_id);
+		return 0;
+	}
+#endif
 	return ip_tunnel_init_net(net, erspan_net_id,
 				  &erspan_link_ops, "erspan0");
 }
-- 
2.43.0


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

* [Devel] [PATCH DRAFT vz10 3/5] ve/net/ip6_gre: Mark ip6gretap devices as movable into a Container
  2026-08-12 13:03 [Devel] [PATCH DRAFT vz10 0/5] Enable GRE ERSPAN inside Containers Konstantin Khorenko
  2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 1/5] Revert "ve/net/gre: Disable ERSPAN support in ip_gre module" Konstantin Khorenko
  2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 2/5] ve/net/gre: Enable ERSPAN support in Containers under VE_FEATURE_ERSPAN Konstantin Khorenko
@ 2026-08-12 13:03 ` Konstantin Khorenko
  2026-08-12 13:04 ` [Devel] [PATCH DRAFT vz10 4/5] ve/net/ip6_gre: Fix NULL deref when creating ip6gre/ip6erspan without VE_FEATURE_IPGRE Konstantin Khorenko
  2026-08-12 13:04 ` [Devel] [PATCH DRAFT vz10 5/5] ve/net/ip6_gre: Enable ip6erspan support in Containers under VE_FEATURE_ERSPAN Konstantin Khorenko
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Khorenko @ 2026-08-12 13:03 UTC (permalink / raw)


Commit 85f1620c362c ("ve/net/ipv6 tunnels: Enable GRE netdevice register
inside container") added NETIF_F_VIRTUAL to ip6gre_tunnel_setup() so that
the base ip6gre device can be registered inside a non-super VE, but it
missed ip6gre_tap_setup(). As a result, even with VE_FEATURE_IPGRE
enabled, a Container could not create an ip6gretap device:
register_netdevice() rejected it with -EPERM in ve_is_dev_movable().

Set NETIF_F_VIRTUAL for ip6gretap devices as well, matching ip6gre and
the IPv4 gretap path.

Fixes: 85f1620c362c ("ve/net/ipv6 tunnels: Enable GRE netdevice register inside container")
https://virtuozzo.atlassian.net/browse/VSTOR-141173

Feature: net: whitelist allowed Container network devices
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 net/ipv6/ip6_gre.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index be6c7d0672933..ba32cc39fc882 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1960,6 +1960,9 @@ static void ip6gre_tap_setup(struct net_device *dev)
 	dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+#ifdef CONFIG_VE
+	dev->ve_features = NETIF_F_VIRTUAL;
+#endif
 	netif_keep_dst(dev);
 }
 
-- 
2.43.0


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

* [Devel] [PATCH DRAFT vz10 4/5] ve/net/ip6_gre: Fix NULL deref when creating ip6gre/ip6erspan without VE_FEATURE_IPGRE
  2026-08-12 13:03 [Devel] [PATCH DRAFT vz10 0/5] Enable GRE ERSPAN inside Containers Konstantin Khorenko
                   ` (2 preceding siblings ...)
  2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 3/5] ve/net/ip6_gre: Mark ip6gretap devices as movable into a Container Konstantin Khorenko
@ 2026-08-12 13:04 ` Konstantin Khorenko
  2026-08-12 13:04 ` [Devel] [PATCH DRAFT vz10 5/5] ve/net/ip6_gre: Enable ip6erspan support in Containers under VE_FEATURE_ERSPAN Konstantin Khorenko
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Khorenko @ 2026-08-12 13:04 UTC (permalink / raw)


When a Container is not granted VE_FEATURE_IPGRE, ip6gre_init_net()
frees the ip6gre_net_id net_generic slot, so net_generic() returns NULL
for that netns. The guard added by commit 1e1433063539 ("net/gre:
Consider VE_FEATURE_IPGRE on new net creation") was placed in
ip6gre_newlink_common(), but both ip6gre_newlink() and
ip6erspan_newlink() dereference the per-net data (ign) earlier - via
rtnl_dereference(ign->collect_md_tun[_erspan]) and
ip6gre_tunnel_find() -> __ip6gre_bucket() - before ever reaching
ip6gre_newlink_common(). Both rtnl link ops are registered
unconditionally, so a Container without the feature can trigger this by
requesting an "ip6gre" or "ip6erspan" link, dereferencing a NULL ign
and crashing the host.

Move the guard to the top of ip6gre_newlink() and ip6erspan_newlink(),
right after ign is fetched and before it is dereferenced, and drop the
now-redundant check in ip6gre_newlink_common() (its only two callers now
guarantee a non-NULL ign). The changelink paths are not affected: they
operate on an already existing tunnel device, which can only exist when
the feature is on and ign is non-NULL.

Fixes: 1e1433063539 ("net/gre: Consider VE_FEATURE_IPGRE on new net creation")
https://virtuozzo.atlassian.net/browse/VSTOR-141173

Feature: ve: per-CT features management
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 net/ipv6/ip6_gre.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index ba32cc39fc882..fe867528a0c5b 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -2006,15 +2006,6 @@ static int ip6gre_newlink_common(struct net *src_net, struct net_device *dev,
 	struct ip6_tnl *nt;
 	struct ip_tunnel_encap ipencap;
 	int err;
-#ifdef CONFIG_VE
-	struct net *net = dev_net(dev);
-	struct ip6gre_net *ign;
-
-	ign = net_generic(net, ip6gre_net_id);
-
-	if (!ign) /* no VE_FEATURE_IPGRE */
-		return -EACCES;
-#endif
 
 	nt = netdev_priv(dev);
 
@@ -2054,6 +2045,11 @@ static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
 	ip6gre_netlink_parms(data, &nt->parms);
 	ign = net_generic(net, ip6gre_net_id);
 
+#ifdef CONFIG_VE
+	if (!ign) /* no VE_FEATURE_IPGRE */
+		return -EACCES;
+#endif
+
 	if (nt->parms.collect_md) {
 		if (rtnl_dereference(ign->collect_md_tun))
 			return -EEXIST;
@@ -2291,6 +2287,11 @@ static int ip6erspan_newlink(struct net *src_net, struct net_device *dev,
 	ip6erspan_set_version(data, &nt->parms);
 	ign = net_generic(net, ip6gre_net_id);
 
+#ifdef CONFIG_VE
+	if (!ign) /* no VE_FEATURE_IPGRE */
+		return -EACCES;
+#endif
+
 	if (nt->parms.collect_md) {
 		if (rtnl_dereference(ign->collect_md_tun_erspan))
 			return -EEXIST;
-- 
2.43.0


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

* [Devel] [PATCH DRAFT vz10 5/5] ve/net/ip6_gre: Enable ip6erspan support in Containers under VE_FEATURE_ERSPAN
  2026-08-12 13:03 [Devel] [PATCH DRAFT vz10 0/5] Enable GRE ERSPAN inside Containers Konstantin Khorenko
                   ` (3 preceding siblings ...)
  2026-08-12 13:04 ` [Devel] [PATCH DRAFT vz10 4/5] ve/net/ip6_gre: Fix NULL deref when creating ip6gre/ip6erspan without VE_FEATURE_IPGRE Konstantin Khorenko
@ 2026-08-12 13:04 ` Konstantin Khorenko
  4 siblings, 0 replies; 6+ messages in thread
From: Konstantin Khorenko @ 2026-08-12 13:04 UTC (permalink / raw)


Mirror the IPv4 ERSPAN containerization for IPv6. ip6erspan shares the
per-net state (ip6gre_net_id / struct ip6gre_net) and the fallback
device gating with ip6gre/ip6gretap, so unlike IPv4 it cannot use a
separate pernet id. Instead the shared ign is allocated whenever either
VE_FEATURE_IPGRE or VE_FEATURE_ERSPAN is set for the Container, and each
rtnl link operation checks its own feature bit via ve_feature_set().

Changes:

 - ip6gre_init_net() keeps the ign net_generic slot when the CT has
   VE_FEATURE_IPGRE *or* VE_FEATURE_ERSPAN; otherwise the slot is freed
   and lookups keep returning NULL as before.

 - ip6erspan_tap_setup() marks the device NETIF_F_VIRTUAL so that
   register_netdevice() permits it inside a non-super VE.

 - ip6gre_newlink()/ip6gre_changelink() now explicitly require
   VE_FEATURE_IPGRE, and ip6erspan_newlink()/ip6erspan_changelink()
   require VE_FEATURE_ERSPAN. Since the shared ign may now exist due to
   either feature, the previous implicit "ign == NULL means no IPGRE"
   gate is no longer sufficient to tell the two apart, so the per-op
   feature check is mandatory. The checks sit at the top of each op,
   before any ign dereference.

ERSPAN stays disabled by default for Containers (VE_FEATURES_DEF) and
enabled for the host (init_ve has all features), so host behaviour is
unchanged.

https://virtuozzo.atlassian.net/browse/VSTOR-141173

Feature: net: ERSPAN support in Containers
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 net/ipv6/ip6_gre.c | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index fe867528a0c5b..ffc1830abed00 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1627,7 +1627,8 @@ static int __net_init ip6gre_init_net(struct net *net)
 	int err;
 
 #ifdef CONFIG_VE
-	if (!(net->owner_ve->features & VE_FEATURE_IPGRE)) {
+	if (!ve_feature_set(net->owner_ve, IPGRE) &&
+	    !ve_feature_set(net->owner_ve, ERSPAN)) {
 		net_generic_free(net, ip6gre_net_id);
 		return 0;
 	}
@@ -2042,14 +2043,12 @@ static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
 	struct ip6gre_net *ign;
 	int err;
 
+	if (!ve_feature_set(dev_net(dev)->owner_ve, IPGRE))
+		return -EACCES;
+
 	ip6gre_netlink_parms(data, &nt->parms);
 	ign = net_generic(net, ip6gre_net_id);
 
-#ifdef CONFIG_VE
-	if (!ign) /* no VE_FEATURE_IPGRE */
-		return -EACCES;
-#endif
-
 	if (nt->parms.collect_md) {
 		if (rtnl_dereference(ign->collect_md_tun))
 			return -EEXIST;
@@ -2109,6 +2108,9 @@ static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
 	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
 	struct __ip6_tnl_parm p;
 
+	if (!ve_feature_set(dev_net(dev)->owner_ve, IPGRE))
+		return -EACCES;
+
 	t = ip6gre_changelink_common(dev, tb, data, &p, extack);
 	if (IS_ERR(t))
 		return PTR_ERR(t);
@@ -2271,6 +2273,9 @@ static void ip6erspan_tap_setup(struct net_device *dev)
 	dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+#ifdef CONFIG_VE
+	dev->ve_features = NETIF_F_VIRTUAL;
+#endif
 	netif_keep_dst(dev);
 }
 
@@ -2283,15 +2288,13 @@ static int ip6erspan_newlink(struct net *src_net, struct net_device *dev,
 	struct ip6gre_net *ign;
 	int err;
 
+	if (!ve_feature_set(dev_net(dev)->owner_ve, ERSPAN))
+		return -EACCES;
+
 	ip6gre_netlink_parms(data, &nt->parms);
 	ip6erspan_set_version(data, &nt->parms);
 	ign = net_generic(net, ip6gre_net_id);
 
-#ifdef CONFIG_VE
-	if (!ign) /* no VE_FEATURE_IPGRE */
-		return -EACCES;
-#endif
-
 	if (nt->parms.collect_md) {
 		if (rtnl_dereference(ign->collect_md_tun_erspan))
 			return -EEXIST;
@@ -2331,6 +2334,9 @@ static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[],
 	struct __ip6_tnl_parm p;
 	struct ip6_tnl *t;
 
+	if (!ve_feature_set(dev_net(dev)->owner_ve, ERSPAN))
+		return -EACCES;
+
 	t = ip6gre_changelink_common(dev, tb, data, &p, extack);
 	if (IS_ERR(t))
 		return PTR_ERR(t);
-- 
2.43.0


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

end of thread, other threads:[~2026-08-12 13:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-12 13:03 [Devel] [PATCH DRAFT vz10 0/5] Enable GRE ERSPAN inside Containers Konstantin Khorenko
2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 1/5] Revert "ve/net/gre: Disable ERSPAN support in ip_gre module" Konstantin Khorenko
2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 2/5] ve/net/gre: Enable ERSPAN support in Containers under VE_FEATURE_ERSPAN Konstantin Khorenko
2026-08-12 13:03 ` [Devel] [PATCH DRAFT vz10 3/5] ve/net/ip6_gre: Mark ip6gretap devices as movable into a Container Konstantin Khorenko
2026-08-12 13:04 ` [Devel] [PATCH DRAFT vz10 4/5] ve/net/ip6_gre: Fix NULL deref when creating ip6gre/ip6erspan without VE_FEATURE_IPGRE Konstantin Khorenko
2026-08-12 13:04 ` [Devel] [PATCH DRAFT vz10 5/5] ve/net/ip6_gre: Enable ip6erspan support in Containers under VE_FEATURE_ERSPAN Konstantin Khorenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.