ipv6: Validate RTA_GATEWAY of RTA_MULTIPATH in rtm_to_fib6_config().

We will perform RTM_NEWROUTE and RTM_DELROUTE under RCU, and then
we want to perform some validation out of the RCU scope.

When creating / removing an IPv6 route with RTA_MULTIPATH,
inet6_rtm_newroute() / inet6_rtm_delroute() validates RTA_GATEWAY
in each multipath entry.

Let's do that in rtm_to_fib6_config().

Note that now RTM_DELROUTE returns an error for RTA_MULTIPATH with
0 entries, which was accepted but should result in -EINVAL as
RTM_NEWROUTE.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250418000443.43734-2-kuniyu@amazon.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Kuniyuki Iwashima
2025-04-17 17:03:42 -07:00
committed by Paolo Abeni
parent abcec3ed92
commit 4cb4861d8c

View File

@@ -5051,6 +5051,44 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
[RTA_FLOWLABEL] = { .type = NLA_BE32 },
};
static int rtm_to_fib6_multipath_config(struct fib6_config *cfg,
struct netlink_ext_ack *extack)
{
struct rtnexthop *rtnh;
int remaining;
remaining = cfg->fc_mp_len;
rtnh = (struct rtnexthop *)cfg->fc_mp;
if (!rtnh_ok(rtnh, remaining)) {
NL_SET_ERR_MSG(extack, "Invalid nexthop configuration - no valid nexthops");
return -EINVAL;
}
do {
int attrlen = rtnh_attrlen(rtnh);
if (attrlen > 0) {
struct nlattr *nla, *attrs;
attrs = rtnh_attrs(rtnh);
nla = nla_find(attrs, attrlen, RTA_GATEWAY);
if (nla) {
if (nla_len(nla) < sizeof(cfg->fc_gateway)) {
NL_SET_ERR_MSG(extack,
"Invalid IPv6 address in RTA_GATEWAY");
return -EINVAL;
}
}
}
rtnh = rtnh_next(rtnh, &remaining);
} while (rtnh_ok(rtnh, remaining));
return lwtunnel_valid_encap_type_attr(cfg->fc_mp, cfg->fc_mp_len,
extack, true);
}
static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
struct fib6_config *cfg,
struct netlink_ext_ack *extack)
@@ -5165,9 +5203,7 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
cfg->fc_mp_len,
extack, true);
err = rtm_to_fib6_multipath_config(cfg, extack);
if (err < 0)
goto errout;
}
@@ -5287,19 +5323,6 @@ static bool ip6_route_mpath_should_notify(const struct fib6_info *rt)
return should_notify;
}
static int fib6_gw_from_attr(struct in6_addr *gw, struct nlattr *nla,
struct netlink_ext_ack *extack)
{
if (nla_len(nla) < sizeof(*gw)) {
NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_GATEWAY");
return -EINVAL;
}
*gw = nla_get_in6_addr(nla);
return 0;
}
static int ip6_route_multipath_add(struct fib6_config *cfg,
struct netlink_ext_ack *extack)
{
@@ -5340,18 +5363,11 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
nla = nla_find(attrs, attrlen, RTA_GATEWAY);
if (nla) {
err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
extack);
if (err)
goto cleanup;
r_cfg.fc_gateway = nla_get_in6_addr(nla);
r_cfg.fc_flags |= RTF_GATEWAY;
}
r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
/* RTA_ENCAP_TYPE length checked in
* lwtunnel_valid_encap_type_attr
*/
r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
if (nla)
r_cfg.fc_encap_type = nla_get_u16(nla);
@@ -5384,12 +5400,6 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
rtnh = rtnh_next(rtnh, &remaining);
}
if (list_empty(&rt6_nh_list)) {
NL_SET_ERR_MSG(extack,
"Invalid nexthop configuration - no valid nexthops");
return -EINVAL;
}
/* for add and replace send one notification with all nexthops.
* Skip the notification in fib6_add_rt2node and send one with
* the full route when done
@@ -5511,21 +5521,15 @@ static int ip6_route_multipath_del(struct fib6_config *cfg,
nla = nla_find(attrs, attrlen, RTA_GATEWAY);
if (nla) {
err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
extack);
if (err) {
last_err = err;
goto next_rtnh;
}
r_cfg.fc_gateway = nla_get_in6_addr(nla);
r_cfg.fc_flags |= RTF_GATEWAY;
}
}
err = ip6_route_del(&r_cfg, extack);
if (err)
last_err = err;
next_rtnh:
rtnh = rtnh_next(rtnh, &remaining);
}