From: Yuya Kusakabe <yuya.kusakabe@gmail.com>
To: David Ahern <dsahern@kernel.org>,
Ido Schimmel <idosch@nvidia.com>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
Justin Iurman <justin.iurman@gmail.com>,
Alexander Aring <alex.aring@gmail.com>,
Steffen Klassert <steffen.klassert@secunet.com>,
Herbert Xu <herbert@gondor.apana.org.au>,
Nikolay Aleksandrov <razor@blackwall.org>,
Eyal Birger <eyal.birger@gmail.com>,
Nicolas Dichtel <nicolas.dichtel@6wind.com>,
Xin Long <lucien.xin@gmail.com>, Shuah Khan <shuah@kernel.org>
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org,
Yuya Kusakabe <yuya.kusakabe@gmail.com>
Subject: [PATCH net 3/4] net: ip_tunnel: accept tunnel options without NLA_F_NESTED
Date: Wed, 23 Sep 2026 09:36:58 +0900 [thread overview]
Message-ID: <20260923-lwt-encap-noflag-v1-3-8de7ab6c86e9@gmail.com> (raw)
In-Reply-To: <20260923-lwt-encap-noflag-v1-0-8de7ab6c86e9@gmail.com>
ip_tun_fill_encap_opts() and its helpers dump LWTUNNEL_IP_OPTS,
LWTUNNEL_IP6_OPTS and the geneve, vxlan and erspan options nested in
them without NLA_F_NESTED. Commit ed02551f58b9 ("lwtunnel: change to
use nla_parse_nested on new options") made the parsing of all of them
strict, on the grounds that new attributes should be strict from the
start, but left the dump as it was, and the two sides have disagreed
ever since. So "ip route restore" cannot send back the ip and ip6
encap routes with tunnel options saved by "ip route save":
# ip route add 192.0.2.0/24 encap ip id 1 dst 198.51.100.2 \
geneve_opts 0:0:12121212 dev dummy0
# ip route save 192.0.2.0/24 > route.bin
# ip route del 192.0.2.0/24
# ip route restore < route.bin
Error: NLA_F_NESTED is missing.
The flag is required at three levels:
- ip_tun_policy and ip6_tun_policy validate LWTUNNEL_IP_OPTS and
LWTUNNEL_IP6_OPTS strictly through .strict_start_type.
- ip_tun_parse_opts() validates the options nested in them with
nla_validate(), which is strict as well.
- ip_tun_parse_opts_geneve(), ip_tun_parse_opts_vxlan() and
ip_tun_parse_opts_erspan() parse each option with
nla_parse_nested().
Start the strict validation after LWTUNNEL_IP(6)_OPTS, validate the
options with lwtunnel_nla_validate(), which is nla_validate() without
the NLA_F_NESTED check, and parse each option with lwtunnel_nla_parse().
netlink has no validation level that keeps the other strict checks and
drops that one, so lwtunnel_nla_validate() clears the flag when calling
__nla_validate(). Nothing else is relaxed: unknown option types and
trailing bytes after the last option are still rejected, and attributes
added to ip_tun_policy and ip6_tun_policy later are still validated
strictly.
Fixes: ed02551f58b9 ("lwtunnel: change to use nla_parse_nested on new options")
Fixes: 2f1d370b997a ("lwtunnel: add support for multiple geneve opts")
Fixes: 7b6a70f73764 ("lwtunnel: be STRICT to validate the new LWTUNNEL_IP(6)_OPTS")
Assisted-by: LLM
Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
include/net/lwtunnel.h | 22 ++++++++++++++++++++++
net/ipv4/ip_tunnel_core.c | 24 ++++++++++++++----------
2 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h
index 046978d6224c..59d7ec7b04f2 100644
--- a/include/net/lwtunnel.h
+++ b/include/net/lwtunnel.h
@@ -80,6 +80,28 @@ static inline int lwtunnel_nla_parse(struct nlattr *tb[], int maxtype,
extack);
}
+/**
+ * lwtunnel_nla_validate - validate the attributes nested in an lwtunnel encap
+ * @nla: encap attribute passed to &lwtunnel_encap_ops.build_state, or an
+ * attribute nested in it
+ * @maxtype: maximum attribute type to be expected
+ * @policy: validation policy
+ * @extack: extended ACK report struct
+ *
+ * Like nla_validate(), except that NLA_F_NESTED is not required on the
+ * attributes nested in @nla, for the reason given for lwtunnel_nla_parse().
+ *
+ * Return: 0 on success or a negative error code.
+ */
+static inline int lwtunnel_nla_validate(const struct nlattr *nla, int maxtype,
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
+{
+ return __nla_validate(nla_data(nla), nla_len(nla), maxtype, policy,
+ NL_VALIDATE_STRICT & ~NL_VALIDATE_NESTED,
+ extack);
+}
+
#ifdef CONFIG_LWTUNNEL
DECLARE_STATIC_KEY_FALSE(nf_hooks_lwtunnel_enabled);
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index bab42b9e277f..c87827ffc347 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -466,7 +466,9 @@ int skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst,
EXPORT_SYMBOL(skb_tunnel_check_pmtu);
static const struct nla_policy ip_tun_policy[LWTUNNEL_IP_MAX + 1] = {
- [LWTUNNEL_IP_UNSPEC] = { .strict_start_type = LWTUNNEL_IP_OPTS },
+ [LWTUNNEL_IP_UNSPEC] = {
+ .strict_start_type = LWTUNNEL_IP_OPTS + 1
+ },
[LWTUNNEL_IP_ID] = { .type = NLA_U64 },
[LWTUNNEL_IP_DST] = { .type = NLA_U32 },
[LWTUNNEL_IP_SRC] = { .type = NLA_U32 },
@@ -509,8 +511,8 @@ static int ip_tun_parse_opts_geneve(struct nlattr *attr,
struct nlattr *tb[LWTUNNEL_IP_OPT_GENEVE_MAX + 1];
int data_len, err;
- err = nla_parse_nested(tb, LWTUNNEL_IP_OPT_GENEVE_MAX, attr,
- geneve_opt_policy, extack);
+ err = lwtunnel_nla_parse(tb, LWTUNNEL_IP_OPT_GENEVE_MAX, attr,
+ geneve_opt_policy, extack);
if (err)
return err;
@@ -546,8 +548,8 @@ static int ip_tun_parse_opts_vxlan(struct nlattr *attr,
struct nlattr *tb[LWTUNNEL_IP_OPT_VXLAN_MAX + 1];
int err;
- err = nla_parse_nested(tb, LWTUNNEL_IP_OPT_VXLAN_MAX, attr,
- vxlan_opt_policy, extack);
+ err = lwtunnel_nla_parse(tb, LWTUNNEL_IP_OPT_VXLAN_MAX, attr,
+ vxlan_opt_policy, extack);
if (err)
return err;
@@ -575,8 +577,8 @@ static int ip_tun_parse_opts_erspan(struct nlattr *attr,
int err;
u8 ver;
- err = nla_parse_nested(tb, LWTUNNEL_IP_OPT_ERSPAN_MAX, attr,
- erspan_opt_policy, extack);
+ err = lwtunnel_nla_parse(tb, LWTUNNEL_IP_OPT_ERSPAN_MAX, attr,
+ erspan_opt_policy, extack);
if (err)
return err;
@@ -626,8 +628,8 @@ static int ip_tun_parse_opts(struct nlattr *attr, struct ip_tunnel_info *info,
if (!attr)
return 0;
- err = nla_validate(nla_data(attr), nla_len(attr), LWTUNNEL_IP_OPTS_MAX,
- ip_opts_policy, extack);
+ err = lwtunnel_nla_validate(attr, LWTUNNEL_IP_OPTS_MAX,
+ ip_opts_policy, extack);
if (err)
return err;
@@ -975,7 +977,9 @@ static const struct lwtunnel_encap_ops ip_tun_lwt_ops = {
};
static const struct nla_policy ip6_tun_policy[LWTUNNEL_IP6_MAX + 1] = {
- [LWTUNNEL_IP6_UNSPEC] = { .strict_start_type = LWTUNNEL_IP6_OPTS },
+ [LWTUNNEL_IP6_UNSPEC] = {
+ .strict_start_type = LWTUNNEL_IP6_OPTS + 1
+ },
[LWTUNNEL_IP6_ID] = { .type = NLA_U64 },
[LWTUNNEL_IP6_DST] = { .len = sizeof(struct in6_addr) },
[LWTUNNEL_IP6_SRC] = { .len = sizeof(struct in6_addr) },
--
2.50.1
next prev parent reply other threads:[~2026-09-23 0:37 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-23 0:36 [PATCH net 0/4] net: lwtunnel: accept encap attributes without NLA_F_NESTED Yuya Kusakabe
2026-09-23 0:36 ` [PATCH net 1/4] net: lwtunnel: accept RTA_ENCAP " Yuya Kusakabe
2026-09-27 1:24 ` netdev-bot+sashiko
2026-09-23 0:36 ` [PATCH net 2/4] xfrm: " Yuya Kusakabe
2026-09-23 0:36 ` Yuya Kusakabe [this message]
2026-09-27 1:25 ` [PATCH net 3/4] net: ip_tunnel: accept tunnel options " netdev-bot+sashiko
2026-09-23 0:36 ` [PATCH net 4/4] selftests: net: add lwtunnel route save and restore test Yuya Kusakabe
2026-09-27 1:25 ` netdev-bot+sashiko
2026-09-23 11:29 ` [PATCH net 0/4] net: lwtunnel: accept encap attributes without NLA_F_NESTED Ido Schimmel
2026-09-23 11:59 ` Yuya Kusakabe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260923-lwt-encap-noflag-v1-3-8de7ab6c86e9@gmail.com \
--to=yuya.kusakabe@gmail.com \
--cc=alex.aring@gmail.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@google.com \
--cc=eyal.birger@gmail.com \
--cc=herbert@gondor.apana.org.au \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=justin.iurman@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=lucien.xin@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=nicolas.dichtel@6wind.com \
--cc=pabeni@redhat.com \
--cc=razor@blackwall.org \
--cc=shuah@kernel.org \
--cc=steffen.klassert@secunet.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox