From 6954c38cf8d3c34cfa06982d3fd0257480becc13 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 8 Nov 2024 22:07:53 +0900 Subject: [PATCH] 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. --- src/network/networkd-link.c | 2 ++ src/network/networkd-route.c | 32 ++++++++++++++++++++++++++++++++ src/network/networkd-route.h | 1 + 3 files changed, 35 insertions(+) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index ef314975ded..ea09753dc51 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -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; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 3f775fc13ce..5104223396b 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include #include #include @@ -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; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index bfe52ca3373..f391d957140 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -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, -- 2.47.3