]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/route: forget IPv4 non-local routes when an interface went down
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 8 Nov 2024 13:07:53 +0000 (22:07 +0900)
committerLuca Boccassi <bluca@debian.org>
Mon, 11 Nov 2024 13:59:41 +0000 (13:59 +0000)
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
src/network/networkd-route.c
src/network/networkd-route.h

index ef314975ded280e72408283fed5ababbe7b132be..ea09753dc5196a2383ac233d95cd5074c6f2d361 100644 (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;
 
index 3f775fc13ce432c4d3e2be973ffe50d3811602d4..5104223396bc48670ebd5bfb0bb93ac4076a0f62 100644 (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;
index bfe52ca3373d1aabb2ae142c106fc86dcb1ed1ba..f391d957140b5e9eff61a0eff383ecfae84af3a2 100644 (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,