mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-29 11:37:59 -04:00
ipv4: Fix fib_nlmsg_size() for RTA_VIA nexthops
fib_nlmsg_size() still estimates nexthop space as if every gateway is
encoded as an IPv4 RTA_GATEWAY attribute. IPv4 routes can also carry an
IPv6 gateway, which fib_nexthop_info() dumps as RTA_VIA.
As a result, route notifications can allocate an skb that is too small.
fib_dump_info() then fails with -EMSGSIZE and rtmsg_fib() hits the
WARN_ON() that marks such failures as a fib_nlmsg_size() bug. With
panic_on_warn set, this becomes a kernel panic.
Mirror the actual nexthop dump layout in fib_nlmsg_size(): account for
IPv6 nexthop gateways dumped as RTA_VIA, for the no-header rtnexthop
layout used inside RTA_MULTIPATH, and for RTA_FLOW only when it is
actually present.
Fixes: d15662682d ("ipv4: Allow ipv6 gateway with ipv4 routes")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Link: https://patch.msgid.link/6f53fa797fcaeb26966432ed7ae9bb87c4961f37.1785411220.git.zihanx@nebusec.ai
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -490,6 +490,34 @@ int ip_fib_check_default(__be32 gw, struct net_device *dev)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static size_t fib_nexthop_nlmsg_size(const struct fib_nh_common *nhc,
|
||||
bool skip_oif)
|
||||
{
|
||||
size_t nhsize = 0;
|
||||
|
||||
switch (nhc->nhc_gw_family) {
|
||||
case AF_INET:
|
||||
nhsize += nla_total_size(4); /* RTA_GATEWAY */
|
||||
break;
|
||||
case AF_INET6:
|
||||
nhsize += nla_total_size(sizeof(struct rtvia) +
|
||||
sizeof(struct in6_addr));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!skip_oif && nhc->nhc_dev)
|
||||
nhsize += nla_total_size(4); /* RTA_OIF */
|
||||
|
||||
if (nhc->nhc_lwtstate) {
|
||||
/* RTA_ENCAP */
|
||||
nhsize += lwtunnel_get_encap_size(nhc->nhc_lwtstate);
|
||||
/* RTA_ENCAP_TYPE */
|
||||
nhsize += nla_total_size(2);
|
||||
}
|
||||
|
||||
return nhsize;
|
||||
}
|
||||
|
||||
size_t fib_nlmsg_size(struct fib_info *fi)
|
||||
{
|
||||
size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg))
|
||||
@@ -507,32 +535,35 @@ size_t fib_nlmsg_size(struct fib_info *fi)
|
||||
payload += nla_total_size(4); /* RTA_NH_ID */
|
||||
|
||||
if (nhs) {
|
||||
size_t nh_encapsize = 0;
|
||||
/* Also handles the special case nhs == 1 */
|
||||
|
||||
/* each nexthop is packed in an attribute */
|
||||
size_t nhsize = nla_total_size(sizeof(struct rtnexthop));
|
||||
size_t mpsize = 0;
|
||||
unsigned int i;
|
||||
|
||||
/* may contain flow and gateway attribute */
|
||||
nhsize += 2 * nla_total_size(4);
|
||||
|
||||
/* grab encap info */
|
||||
for (i = 0; i < fib_info_num_path(fi); i++) {
|
||||
struct fib_nh_common *nhc = fib_info_nhc(fi, i);
|
||||
size_t nhsize;
|
||||
|
||||
if (nhc->nhc_lwtstate) {
|
||||
/* RTA_ENCAP_TYPE */
|
||||
nh_encapsize += lwtunnel_get_encap_size(
|
||||
nhc->nhc_lwtstate);
|
||||
/* RTA_ENCAP */
|
||||
nh_encapsize += nla_total_size(2);
|
||||
nhsize = fib_nexthop_nlmsg_size(nhc, nhs != 1);
|
||||
|
||||
if (nhs != 1)
|
||||
nhsize += NLA_ALIGN(sizeof(struct rtnexthop));
|
||||
|
||||
#ifdef CONFIG_IP_ROUTE_CLASSID
|
||||
if (nhc->nhc_family == AF_INET) {
|
||||
struct fib_nh *nh;
|
||||
|
||||
nh = container_of(nhc, struct fib_nh, nh_common);
|
||||
if (nh->nh_tclassid)
|
||||
nhsize += nla_total_size(4);
|
||||
}
|
||||
#endif
|
||||
if (nhs == 1)
|
||||
payload += nhsize;
|
||||
else
|
||||
mpsize += nhsize;
|
||||
}
|
||||
|
||||
/* all nexthops are packed in a nested attribute */
|
||||
payload += nla_total_size((nhs * nhsize) + nh_encapsize);
|
||||
|
||||
if (nhs != 1)
|
||||
payload += nla_total_size(mpsize);
|
||||
}
|
||||
|
||||
return payload;
|
||||
|
||||
Reference in New Issue
Block a user