network/route: forget IPv4 non-local routes when an interface went down

When an interface went down, IPv4 non-local routes are removed by the
kernel without any notifications. Let's forget the routes in that case.

Fixes #35047.
This commit is contained in:
Yu Watanabe
2024-11-11 13:59:41 +00:00
committed by Luca Boccassi
parent fd2ea787bd
commit 6954c38cf8
3 changed files with 35 additions and 0 deletions
+2
View File
@@ -1895,6 +1895,8 @@ static int link_admin_state_up(Link *link) {
static int link_admin_state_down(Link *link) {
assert(link);
link_forget_routes(link);
if (!link->network)
return 0;
+32
View File
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/if.h>
#include <linux/ipv6_route.h>
#include <linux/nexthop.h>
@@ -1540,6 +1541,37 @@ int link_drop_routes(Link *link, bool only_static) {
return r;
}
void link_forget_routes(Link *link) {
assert(link);
assert(link->ifindex > 0);
assert(!FLAGS_SET(link->flags, IFF_UP));
/* When an interface went down, IPv4 non-local routes bound to the interface are silently removed by
* the kernel, without any notifications. Let's forget them in that case. Otherwise, when the link
* goes up later, the configuration order of routes may be confused by the nonexistent routes.
* See issue #35047. */
Route *route;
SET_FOREACH(route, link->manager->routes) {
// TODO: handle multipath routes
if (route->nexthop.ifindex != link->ifindex)
continue;
if (route->family != AF_INET)
continue;
// TODO: check RTN_NAT and RTN_XRESOLVE
if (!IN_SET(route->type, RTN_UNICAST, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
continue;
Request *req;
if (route_get_request(link->manager, route, &req) >= 0)
route_enter_removed(req->userdata);
route_enter_removed(route);
log_route_debug(route, "Forgetting silently removed", link->manager);
route_detach(route);
}
}
int network_add_ipv4ll_route(Network *network) {
_cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
unsigned section_line;
+1
View File
@@ -111,6 +111,7 @@ static inline int link_drop_static_routes(Link *link) {
static inline int link_drop_unmanaged_routes(Link *link) {
return link_drop_routes(link, false);
}
void link_forget_routes(Link *link);
int link_request_route(
Link *link,