Merge pull request #30975 from yuwata/network-route-several-helper-functions
network/route: introduce several helper functions
This commit is contained in:
@@ -300,6 +300,10 @@ static int dhcp_pd_request_route(Link *link, const struct in6_addr *prefix, usec
|
||||
route->priority = link->network->dhcp_pd_route_metric;
|
||||
route->lifetime_usec = lifetime_usec;
|
||||
|
||||
r = route_adjust_nexthops(route, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (route_get(NULL, link, route, &existing) < 0)
|
||||
link->dhcp_pd_configured = false;
|
||||
else
|
||||
@@ -695,6 +699,10 @@ static int dhcp_request_unreachable_route(
|
||||
route->priority = IP6_RT_PRIO_USER;
|
||||
route->lifetime_usec = lifetime_usec;
|
||||
|
||||
r = route_adjust_nexthops(route, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (route_get(link->manager, NULL, route, &existing) < 0)
|
||||
*configured = false;
|
||||
else
|
||||
@@ -789,6 +797,10 @@ static int dhcp4_pd_request_default_gateway_on_6rd_tunnel(Link *link, const stru
|
||||
route->priority = IP6_RT_PRIO_USER;
|
||||
route->lifetime_usec = lifetime_usec;
|
||||
|
||||
r = route_adjust_nexthops(route, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
|
||||
link->dhcp_pd_configured = false;
|
||||
else
|
||||
|
||||
@@ -390,6 +390,10 @@ static int dhcp4_request_route(Route *in, Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_adjust_nexthops(route, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
|
||||
link->dhcp4_configured = false;
|
||||
else
|
||||
@@ -659,7 +663,7 @@ static int dhcp4_request_semi_static_routes(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_dup(rt, &route);
|
||||
r = route_dup(rt, NULL, &route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -218,6 +218,10 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_adjust_nexthops(route, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
is_new = route_get(NULL, link, route, NULL) < 0;
|
||||
|
||||
r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages,
|
||||
@@ -340,7 +344,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
|
||||
if (route_gw->nexthop.family != AF_INET6)
|
||||
continue;
|
||||
|
||||
r = route_dup(route_gw, &route);
|
||||
r = route_dup(route_gw, NULL, &route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -170,6 +170,147 @@ int route_nexthops_compare_func(const Route *a, const Route *b) {
|
||||
}}
|
||||
}
|
||||
|
||||
static int route_nexthop_copy(const RouteNextHop *src, RouteNextHop *dest) {
|
||||
assert(src);
|
||||
assert(dest);
|
||||
|
||||
*dest = *src;
|
||||
|
||||
/* unset pointer copied in the above. */
|
||||
dest->ifname = NULL;
|
||||
|
||||
return strdup_or_null(src->ifindex == 0 ? NULL : src->ifname, &dest->ifname);
|
||||
}
|
||||
|
||||
int route_nexthops_copy(const Route *src, const RouteNextHop *nh, Route *dest) {
|
||||
assert(src);
|
||||
assert(dest);
|
||||
|
||||
if (src->nexthop_id != 0 || route_type_is_reject(src))
|
||||
return 0;
|
||||
|
||||
if (nh)
|
||||
return route_nexthop_copy(nh, &dest->nexthop);
|
||||
|
||||
if (ordered_set_isempty(src->nexthops))
|
||||
return route_nexthop_copy(&src->nexthop, &dest->nexthop);
|
||||
|
||||
/* Currently, this does not copy multipath routes. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool multipath_routes_needs_adjust(const Route *route) {
|
||||
assert(route);
|
||||
|
||||
RouteNextHop *nh;
|
||||
ORDERED_SET_FOREACH(nh, route->nexthops)
|
||||
if (route->nexthop.ifindex == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool route_nexthops_needs_adjust(const Route *route) {
|
||||
assert(route);
|
||||
|
||||
if (route->nexthop_id != 0)
|
||||
/* At this stage, the nexthop may not be configured, or may be under reconfiguring.
|
||||
* Hence, we cannot know if the nexthop is blackhole or not. */
|
||||
return route->type != RTN_BLACKHOLE;
|
||||
|
||||
if (route_type_is_reject(route))
|
||||
return false;
|
||||
|
||||
if (ordered_set_isempty(route->nexthops))
|
||||
return route->nexthop.ifindex == 0;
|
||||
|
||||
return multipath_routes_needs_adjust(route);
|
||||
}
|
||||
|
||||
static bool route_nexthop_set_ifindex(RouteNextHop *nh, Link *link) {
|
||||
assert(nh);
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
if (nh->ifindex > 0) {
|
||||
nh->ifname = mfree(nh->ifname);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If an interface name is specified, use it. Otherwise, use the interface that requests this route. */
|
||||
if (nh->ifname && link_get_by_name(link->manager, nh->ifname, &link) < 0)
|
||||
return false;
|
||||
|
||||
nh->ifindex = link->ifindex;
|
||||
nh->ifname = mfree(nh->ifname);
|
||||
return true; /* updated */
|
||||
}
|
||||
|
||||
int route_adjust_nexthops(Route *route, Link *link) {
|
||||
int r;
|
||||
|
||||
assert(route);
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
/* When an IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel sends
|
||||
* RTM_NEWROUTE netlink message with blackhole type, kernel's internal route type fib_rt_info::type
|
||||
* may not be blackhole. Thus, we cannot know the internal value. Moreover, on route removal, the
|
||||
* matching is done with the hidden value if we set non-zero type in RTM_DELROUTE message. So,
|
||||
* here let's set route type to BLACKHOLE when the nexthop is blackhole. */
|
||||
if (route->nexthop_id != 0) {
|
||||
NextHop *nexthop;
|
||||
|
||||
r = nexthop_is_ready(link->manager, route->nexthop_id, &nexthop);
|
||||
if (r <= 0)
|
||||
return r; /* r == 0 means the nexthop is under (re-)configuring.
|
||||
* We cannot use the currently remembered information. */
|
||||
|
||||
if (!nexthop->blackhole)
|
||||
return false;
|
||||
|
||||
if (route->type == RTN_BLACKHOLE)
|
||||
return false;
|
||||
|
||||
route->type = RTN_BLACKHOLE;
|
||||
return true; /* updated */
|
||||
}
|
||||
|
||||
if (route_type_is_reject(route))
|
||||
return false;
|
||||
|
||||
if (ordered_set_isempty(route->nexthops))
|
||||
return route_nexthop_set_ifindex(&route->nexthop, link);
|
||||
|
||||
if (!multipath_routes_needs_adjust(route))
|
||||
return false;
|
||||
|
||||
_cleanup_ordered_set_free_ OrderedSet *nexthops = NULL;
|
||||
for (;;) {
|
||||
_cleanup_(route_nexthop_freep) RouteNextHop *nh = NULL;
|
||||
|
||||
nh = ordered_set_steal_first(route->nexthops);
|
||||
if (!nh)
|
||||
break;
|
||||
|
||||
(void) route_nexthop_set_ifindex(nh, link);
|
||||
|
||||
r = ordered_set_ensure_put(&nexthops, &route_nexthop_hash_ops, nh);
|
||||
if (r == -EEXIST)
|
||||
continue; /* Duplicated? Let's drop the nexthop. */
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(r > 0);
|
||||
|
||||
TAKE_PTR(nh);
|
||||
}
|
||||
|
||||
ordered_set_free(route->nexthops);
|
||||
route->nexthops = TAKE_PTR(nexthops);
|
||||
return true; /* updated */
|
||||
}
|
||||
|
||||
int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret) {
|
||||
assert(manager);
|
||||
assert(nh);
|
||||
@@ -384,9 +525,6 @@ static int netlink_message_append_multipath_route(Link *link, const Route *route
|
||||
assert(route);
|
||||
assert(message);
|
||||
|
||||
if (ordered_set_isempty(route->nexthops))
|
||||
return 0;
|
||||
|
||||
rta = new(struct rtattr, 1);
|
||||
if (!rta)
|
||||
return -ENOMEM;
|
||||
@@ -397,16 +535,23 @@ static int netlink_message_append_multipath_route(Link *link, const Route *route
|
||||
};
|
||||
offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
|
||||
|
||||
RouteNextHop *nh;
|
||||
ORDERED_SET_FOREACH(nh, route->nexthops) {
|
||||
struct rtnexthop *rtnh;
|
||||
|
||||
r = append_nexthop_one(link, route, nh, &rta, offset);
|
||||
if (ordered_set_isempty(route->nexthops)) {
|
||||
r = append_nexthop_one(link, route, &route->nexthop, &rta, offset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
|
||||
offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
|
||||
} else {
|
||||
RouteNextHop *nh;
|
||||
ORDERED_SET_FOREACH(nh, route->nexthops) {
|
||||
struct rtnexthop *rtnh;
|
||||
|
||||
r = append_nexthop_one(link, route, nh, &rta, offset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
|
||||
offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_netlink_message_append_data(message, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
|
||||
@@ -425,7 +570,9 @@ int route_nexthops_set_netlink_message(Link *link, const Route *route, sd_netlin
|
||||
if (route_type_is_reject(route))
|
||||
return 0;
|
||||
|
||||
if (ordered_set_isempty(route->nexthops)) {
|
||||
/* We request IPv6 multipath routes separatedly. Even though, if weight is non-zero, we need to use
|
||||
* RTA_MULTIPATH, as we have no way to specify the weight of the nexthop. */
|
||||
if (ordered_set_isempty(route->nexthops) && route->nexthop.weight == 0) {
|
||||
|
||||
if (in_addr_is_set(route->nexthop.family, &route->nexthop.gw)) {
|
||||
if (route->nexthop.family == route->family)
|
||||
|
||||
@@ -24,6 +24,8 @@ typedef struct RouteNextHop {
|
||||
/* unsupported attributes: RTA_FLOW (IPv4 only), RTA_ENCAP_TYPE, RTA_ENCAP. */
|
||||
} RouteNextHop;
|
||||
|
||||
#define ROUTE_NEXTHOP_NULL ((const RouteNextHop) {})
|
||||
|
||||
RouteNextHop* route_nexthop_free(RouteNextHop *nh);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(RouteNextHop*, route_nexthop_free);
|
||||
|
||||
@@ -32,6 +34,10 @@ void route_nexthops_done(Route *route);
|
||||
void route_nexthops_hash_func(const Route *route, struct siphash *state);
|
||||
int route_nexthops_compare_func(const Route *a, const Route *b);
|
||||
|
||||
int route_nexthops_copy(const Route *src, const RouteNextHop *nh, Route *dest);
|
||||
bool route_nexthops_needs_adjust(const Route *route);
|
||||
int route_adjust_nexthops(Route *route, Link *link);
|
||||
|
||||
int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret);
|
||||
int route_nexthops_is_ready_to_configure(const Route *route, Link *link);
|
||||
|
||||
|
||||
@@ -336,12 +336,33 @@ static int route_get_link(Manager *manager, const Route *route, Link **ret) {
|
||||
return route_nexthop_get_link(manager, NULL, &route->nexthop, ret);
|
||||
}
|
||||
|
||||
int route_dup(const Route *src, Route **ret) {
|
||||
static int route_get_request(Link *link, const Route *route, Request **ret) {
|
||||
Request *req;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(route);
|
||||
|
||||
req = ordered_set_get(link->manager->request_queue,
|
||||
&(const Request) {
|
||||
.link = link,
|
||||
.type = REQUEST_TYPE_ROUTE,
|
||||
.userdata = (void*) route,
|
||||
.hash_func = (hash_func_t) route_hash_func,
|
||||
.compare_func = (compare_func_t) route_compare_func,
|
||||
});
|
||||
if (!req)
|
||||
return -ENOENT;
|
||||
|
||||
if (ret)
|
||||
*ret = req;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int route_dup(const Route *src, const RouteNextHop *nh, Route **ret) {
|
||||
_cleanup_(route_freep) Route *dest = NULL;
|
||||
int r;
|
||||
|
||||
/* This does not copy mulipath routes. */
|
||||
|
||||
assert(src);
|
||||
assert(ret);
|
||||
|
||||
@@ -355,10 +376,15 @@ int route_dup(const Route *src, Route **ret) {
|
||||
dest->wireguard = NULL;
|
||||
dest->section = NULL;
|
||||
dest->link = NULL;
|
||||
dest->nexthop = ROUTE_NEXTHOP_NULL;
|
||||
dest->nexthops = NULL;
|
||||
dest->metric = ROUTE_METRIC_NULL;
|
||||
dest->expire = NULL;
|
||||
|
||||
r = route_nexthops_copy(src, nh, dest);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_metric_copy(&src->metric, &dest->metric);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -476,7 +502,7 @@ static int route_convert(Manager *manager, Link *link, const Route *route, Conve
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_dup(route, &c->routes[0]);
|
||||
r = route_dup(route, NULL, &c->routes[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -499,7 +525,7 @@ static int route_convert(Manager *manager, Link *link, const Route *route, Conve
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_dup(route, &c->routes[i]);
|
||||
r = route_dup(route, NULL, &c->routes[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -523,7 +549,7 @@ static int route_convert(Manager *manager, Link *link, const Route *route, Conve
|
||||
size_t i = 0;
|
||||
RouteNextHop *nh;
|
||||
ORDERED_SET_FOREACH(nh, route->nexthops) {
|
||||
r = route_dup(route, &c->routes[i]);
|
||||
r = route_dup(route, NULL, &c->routes[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1129,7 +1155,7 @@ static int route_process_request(Request *req, Link *link, Route *route) {
|
||||
if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) {
|
||||
_cleanup_(route_freep) Route *tmp = NULL;
|
||||
|
||||
r = route_dup(converted->routes[i], &tmp);
|
||||
r = route_dup(converted->routes[i], NULL, &tmp);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
@@ -1215,7 +1241,7 @@ int link_request_route(
|
||||
if (consume_object)
|
||||
tmp = route;
|
||||
else {
|
||||
r = route_dup(route, &tmp);
|
||||
r = route_dup(route, NULL, &tmp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -1345,29 +1371,17 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
|
||||
}
|
||||
|
||||
void route_cancel_request(Route *route, Link *link) {
|
||||
Request *req;
|
||||
|
||||
assert(route);
|
||||
|
||||
link = route->link ?: link;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
link = ASSERT_PTR(route->link ?: link);
|
||||
|
||||
if (!route_is_requesting(route))
|
||||
return;
|
||||
|
||||
req = ordered_set_get(link->manager->request_queue,
|
||||
&(Request) {
|
||||
.link = link,
|
||||
.type = REQUEST_TYPE_ROUTE,
|
||||
.userdata = route,
|
||||
.hash_func = (hash_func_t) route_hash_func,
|
||||
.compare_func = (compare_func_t) route_compare_func,
|
||||
});
|
||||
|
||||
if (req)
|
||||
Request *req;
|
||||
if (route_get_request(link, route, &req) >= 0)
|
||||
request_detach(req);
|
||||
|
||||
route_cancel_requesting(route);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ DEFINE_SECTION_CLEANUP_FUNCTIONS(Route, route_free);
|
||||
|
||||
int route_new(Route **ret);
|
||||
int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret);
|
||||
int route_dup(const Route *src, Route **ret);
|
||||
int route_dup(const Route *src, const RouteNextHop *nh, Route **ret);
|
||||
|
||||
int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, Route *route, const char *error_msg);
|
||||
int route_remove(Route *route);
|
||||
|
||||
Reference in New Issue
Block a user