[PATCH net-next v2] ip6_gre: fix changelink conflict checks for L2 tunnels
From: Xia Yan
Date: Wed Nov 04 2026 - 10:16:40 EST
ip6gre_changelink_common() calls ip6gre_tunnel_locate(), which only
looks for ARPHRD_IP6GRE devices. The same changelink helper also handles
ip6gretap and ip6erspan devices.
This lets an L2 tunnel change its parameters to match another L2 tunnel,
since the duplicate check looks for an L3 device instead. Incoming
packets can then be delivered to the wrong interface. An L3 tunnel with
matching parameters can also make a valid L2 update fail with EEXIST.
Use ip6gre_tunnel_find() with dev->type, as the newlink paths do. Skip
collect_md entries while walking the bucket: they can coexist with
ordinary tunnels with the same lookup parameters, and their uniqueness
is checked through separate per-net pointers. Keep searching past them
so that a real duplicate later in the bucket is still rejected.
Found while comparing the duplicate checks in newlink and changelink.
Confirmed with ip link commands. For example, with an IPv6 underlay
named under already configured:
ip link add tap1 type ip6gretap local fd26:106::1 \
remote fd26:106::2 key 100 dev under
ip link add tap2 type ip6gretap local fd26:106::1 \
remote fd26:106::2 key 200 dev under
ip link set tap1 up
ip link set tap2 up
ip link set tap2 type ip6gretap key 100
Before the fix, the last command succeeds and received frames move from
tap1 to tap2. After the fix, it fails with EEXIST and tap2 is unchanged.
The collect_md case was raised during v1 review and confirmed with
namespace tests. The reproducers use existing interfaces without kernel
changes or fault injection.
Tested under UML with standalone namespace tests for ip6gretap and
ip6erspan, including duplicate updates, L2/L3 coexistence, IPv4/IPv6
traffic and normal updates. Additional tests cover both ordinary and
metadata creation orders, all four L2 kind combinations, a real duplicate
behind metadata, and L3 ioctl changes. They reproduce the collect_md
failure with v1 and pass with this change.
Full x86_64 W=1 builds with allmodconfig- and allyesconfig-derived
configurations also pass, with unchanged warning sets.
Signed-off-by: Xia Yan <yanxia@xxxxxxxxxxxxxxxxxxxx>
---
Changes in v2:
- Target net-next and drop the Fixes and stable tags, as requested by
Ido Schimmel.
- Skip collect_md entries during lookup to avoid rejecting valid updates
and to find ordinary duplicates behind metadata entries.
- Extend standalone UML validation to cover ordinary/metadata ordering,
all four L2 kind combinations, and L3 lookup paths.
v1: https://lore.kernel.org/netdev/20261028115710.27806-1-yanxia@xxxxxxxxxxxxxxxxxxxx/
Runtime and build validation used net-next 7b6ab4dbc156.
Kernel selftests were not run.
The allmodconfig- and allyesconfig-derived builds had WERROR, KASAN and
KCSAN off. The allyesconfig-derived build also had KUNIT_ALL_TESTS,
MCTP_TEST and MCTP_TRANSPORT_USBLIB_TEST off. The full default-to-derived
configuration diffs and the unchanged before/after warning sets are
retained in the validation archive.
net/ipv6/ip6_gre.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index b5d6ed8..07f7220 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -306,6 +306,9 @@ static struct ip6_tnl *ip6gre_tunnel_find(struct net *net,
head = __ip6gre_bucket(ign, parms);
hlist_for_each_entry(t, head, hash_node) {
+ if (t->parms.collect_md)
+ continue;
+
if (ipv6_addr_equal(local, &t->parms.laddr) &&
ipv6_addr_equal(remote, &t->parms.raddr) &&
key == t->parms.i_key &&
@@ -2055,7 +2058,7 @@ ip6gre_changelink_common(struct net_device *dev, struct nlattr *tb[],
ip6gre_netlink_parms(data, p_p);
- t = ip6gre_tunnel_locate(net, p_p, 0);
+ t = ip6gre_tunnel_find(net, p_p, dev->type);
if (t) {
if (t->dev != dev)
base-commit: 7b6ab4dbc156e2f96419715130b3d5738a306c93
--
2.39.0.windows.2