local-addresses: introduce own parser for RTA_MULTIPATH

This commit is contained in:
Yu Watanabe
2024-01-20 16:07:19 +09:00
parent eb1f9ed6a0
commit 16d95d6ff8
+107 -17
View File
@@ -229,6 +229,112 @@ static int add_local_gateway(
return add_local_address_full(list, n_list, ifindex, 0, priority, weight, family, address);
}
static int parse_nexthop_one(
struct local_address **list,
size_t *n_list,
bool allow_via,
int family,
uint32_t priority,
const struct rtnexthop *rtnh) {
bool has_gw = false;
int r;
assert(rtnh);
size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop);
for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
switch (attr->rta_type) {
case RTA_GATEWAY:
if (has_gw)
return -EBADMSG;
has_gw = true;
if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(family)))
return -EBADMSG;
union in_addr_union a;
memcpy(&a, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family));
r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, family, &a);
if (r < 0)
return r;
break;
case RTA_VIA:
if (has_gw)
return -EBADMSG;
has_gw = true;
if (!allow_via)
continue;
if (family != AF_INET)
return -EBADMSG; /* RTA_VIA is only supported for IPv4 routes. */
if (attr->rta_len != RTA_LENGTH(sizeof(RouteVia)))
return -EBADMSG;
RouteVia *via = RTA_DATA(attr);
if (via->family != AF_INET6)
return -EBADMSG; /* gateway address should be always IPv6. */
r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, via->family,
&(union in_addr_union) { .in6 = via->address.in6 });
if (r < 0)
return r;
break;
}
return 0;
}
static int parse_nexthops(
struct local_address **list,
size_t *n_list,
int ifindex,
bool allow_via,
int family,
uint32_t priority,
const struct rtnexthop *rtnh,
size_t size) {
int r;
assert(list);
assert(n_list);
assert(IN_SET(family, AF_INET, AF_INET6));
assert(rtnh || size == 0);
if (size < sizeof(struct rtnexthop))
return -EBADMSG;
for (; size >= sizeof(struct rtnexthop); ) {
if (NLMSG_ALIGN(rtnh->rtnh_len) > size)
return -EBADMSG;
if (rtnh->rtnh_len < sizeof(struct rtnexthop))
return -EBADMSG;
if (ifindex > 0 && rtnh->rtnh_ifindex != ifindex)
goto next_nexthop;
r = parse_nexthop_one(list, n_list, allow_via, family, priority, rtnh);
if (r < 0)
return r;
next_nexthop:
size -= NLMSG_ALIGN(rtnh->rtnh_len);
rtnh = RTNH_NEXT(rtnh);
}
return 0;
}
int local_gateways(
sd_netlink *context,
int ifindex,
@@ -373,25 +479,9 @@ int local_gateways(
if (r < 0 && r != -ENODATA)
return r;
if (r >= 0) {
_cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
MultipathRoute *mr;
r = rtattr_read_nexthop(rta_multipath, rta_len, family, &multipath_routes);
r = parse_nexthops(&list, &n_list, ifindex, allow_via, family, priority, rta_multipath, rta_len);
if (r < 0)
return r;
ORDERED_SET_FOREACH(mr, multipath_routes) {
if (ifindex > 0 && mr->ifindex != ifindex)
continue;
if (!allow_via && family != mr->gateway.family)
continue;
union in_addr_union a = mr->gateway.address;
r = add_local_gateway(&list, &n_list, ifi, priority, mr->weight, mr->gateway.family, &a);
if (r < 0)
return r;
}
}
}