[PATCH net v2 2/5] ip_gre: validate netlink attributes before changing the tunnel
From: Eric Dumazet <edumazet@google.com>
Date: 2026-09-16 10:02:02
Subsystem:
networking [general], networking [ipv4/ipv6], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds
ipgre_netlink_parms() and erspan_netlink_parms() write into the live
tunnel before all attributes have been validated, so a rejected
changelink leaves it half updated.
A request carrying IFLA_GRE_COLLECT_METADATA and an invalid
IFLA_GRE_IGNORE_DF returns -EINVAL, but dev->type has already become
ARPHRD_NONE, breaking the interface for good.
Parse the ERSPAN attributes into local variables and commit them only
once everything is validated, hence erspan_netlink_parms() now calls
ipgre_netlink_parms() last. Same reason for moving
IFLA_GRE_COLLECT_METADATA after the IFLA_GRE_IGNORE_DF validation.
Only the parsers become all-or-nothing: ip_tunnel_encap_setup() still
runs before them, ip_tunnel_changelink() after them.
Fixes: e271c7b4420d ("gre: do not keep the GRE header around in collect medata mode")
Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/ip_gre.c | 55 ++++++++++++++++++++++++++++++-----------------
1 file changed, 35 insertions(+), 20 deletions(-)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 82309efd417e0f1f6554e7028be8e05d769e932d..dad3d054bd15612a3ebe1cf27e6e8c5d9e6896e9 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c@@ -1233,12 +1233,6 @@ static int ipgre_netlink_parms(struct net_device *dev, parms->iph.frag_off = htons(IP_DF); } - if (data[IFLA_GRE_COLLECT_METADATA]) { - t->collect_md = true; - if (dev->type == ARPHRD_IPGRE) - dev->type = ARPHRD_NONE; - } - if (data[IFLA_GRE_IGNORE_DF]) { if (nla_get_u8(data[IFLA_GRE_IGNORE_DF]) && (parms->iph.frag_off & htons(IP_DF)))
@@ -1246,6 +1240,16 @@ static int ipgre_netlink_parms(struct net_device *dev, t->ignore_df = !!nla_get_u8(data[IFLA_GRE_IGNORE_DF]); } + /* All attributes parsed here have been validated, we can change @dev + * and @t. This only makes this parser all-or-nothing, the caller can + * still fail in ip_tunnel_changelink(). + */ + if (data[IFLA_GRE_COLLECT_METADATA]) { + t->collect_md = true; + if (dev->type == ARPHRD_IPGRE) + dev->type = ARPHRD_NONE; + } + if (data[IFLA_GRE_FWMARK]) *fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);
@@ -1259,40 +1263,51 @@ static int erspan_netlink_parms(struct net_device *dev, __u32 *fwmark) { struct ip_tunnel *t = netdev_priv(dev); + u8 erspan_ver = t->erspan_ver; + u32 index = t->index; + u16 hwid = t->hwid; + u8 dir = t->dir; int err; - err = ipgre_netlink_parms(dev, data, tb, parms, fwmark); - if (err) - return err; if (!data) - return 0; + return ipgre_netlink_parms(dev, data, tb, parms, fwmark); if (data[IFLA_GRE_ERSPAN_VER]) { - t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]); + erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]); - if (t->erspan_ver > 2) + if (erspan_ver > 2) return -EINVAL; } - if (t->erspan_ver == 1) { + if (erspan_ver == 1) { if (data[IFLA_GRE_ERSPAN_INDEX]) { - t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]); - if (t->index & ~INDEX_MASK) + index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]); + if (index & ~INDEX_MASK) return -EINVAL; } - } else if (t->erspan_ver == 2) { + } else if (erspan_ver == 2) { if (data[IFLA_GRE_ERSPAN_DIR]) { - t->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]); - if (t->dir & ~(DIR_MASK >> DIR_OFFSET)) + dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]); + if (dir & ~(DIR_MASK >> DIR_OFFSET)) return -EINVAL; } if (data[IFLA_GRE_ERSPAN_HWID]) { - t->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]); - if (t->hwid & ~(HWID_MASK >> HWID_OFFSET)) + hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]); + if (hwid & ~(HWID_MASK >> HWID_OFFSET)) return -EINVAL; } } + err = ipgre_netlink_parms(dev, data, tb, parms, fwmark); + if (err) + return err; + + /* All attributes parsed here have been validated, we can change @t. */ + t->erspan_ver = erspan_ver; + t->index = index; + t->hwid = hwid; + t->dir = dir; + return 0; }
--
2.55.0.1032.g73a4cd73de-goog