Commit 4cb4861d authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Paolo Abeni
Browse files

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: default avatarKuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250418000443.43734-2-kuniyu@amazon.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent abcec3ed
Loading
Loading
Loading
Loading
+43 −39
Original line number Diff line number Diff line
@@ -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);
	}