network/ndisc: remove conflicting routes on configuring routes based on newly received RA

The linux kernel does not update several parameters, e.g. RTA_PREF.
Hence, when we configure routes based on a RA, we need to remove
existing conflicting routes.

Fixes #28426 and #28439.
This commit is contained in:
Yu Watanabe 2024-02-02 12:30:32 +09:00
parent 7027cdbd79
commit 972f1d17ab
3 changed files with 43 additions and 2 deletions

View File

@ -205,7 +205,6 @@ static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
route->provider.in6 = router;
if (!route->table_set)
route->table = link_get_ipv6_accept_ra_route_table(link);
ndisc_set_route_priority(link, route);
if (!route->protocol_set)
route->protocol = RTPROT_RA;
r = route_metric_set(&route->metric, RTAX_MTU, mtu);
@ -222,6 +221,47 @@ static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
if (r < 0)
return r;
uint8_t pref, pref_original = route->pref;
FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
Route *existing;
Request *req;
/* If the preference is specified by the user config (that is, for semi-static routes),
* rather than RA, then only search conflicting routes that have the same preference. */
if (route->pref_set && pref != pref_original)
continue;
route->pref = pref;
ndisc_set_route_priority(link, route);
/* Note, here do not call route_remove_and_cancel() with 'route' directly, otherwise
* existing route(s) may be removed needlessly. */
if (route_get(link->manager, route, &existing) >= 0) {
/* Found an existing route that may conflict with this route. */
if (!route_can_update(existing, route)) {
log_link_debug(link, "Found an existing route that conflicts with new route based on a received RA, removing.");
r = route_remove_and_cancel(existing, link->manager);
if (r < 0)
return r;
}
}
if (route_get_request(link->manager, route, &req) >= 0) {
existing = ASSERT_PTR(req->userdata);
if (!route_can_update(existing, route)) {
log_link_debug(link, "Found a pending route request that conflicts with new request based on a received RA, cancelling.");
r = route_remove_and_cancel(existing, link->manager);
if (r < 0)
return r;
}
}
}
/* The preference (and priority) may be changed in the above loop. Restore it. */
route->pref = pref_original;
ndisc_set_route_priority(link, route);
is_new = route_get(link->manager, route, NULL) < 0;
r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);

View File

@ -352,7 +352,7 @@ static int route_get_link(Manager *manager, const Route *route, Link **ret) {
return route_nexthop_get_link(manager, &route->nexthop, ret);
}
static int route_get_request(Manager *manager, const Route *route, Request **ret) {
int route_get_request(Manager *manager, const Route *route, Request **ret) {
Request *req;
assert(manager);

View File

@ -96,6 +96,7 @@ int route_remove(Route *route, Manager *manager);
int route_remove_and_cancel(Route *route, Manager *manager);
int route_get(Manager *manager, const Route *route, Route **ret);
int route_get_request(Manager *manager, const Route *route, Request **ret);
bool route_can_update(const Route *existing, const Route *requesting);