]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/ndisc: do not remove static routes when received RA with zero lifetime
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 24 Jun 2024 07:20:27 +0000 (16:20 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 24 Jun 2024 18:15:59 +0000 (03:15 +0900)
Similar to the previous commit, but for preventing from removing static
routes on receiving RA with zero lifetime.

Fixes a regresson caused by 479d3e1994a2e4ff7070dc2a0cb1615af7120b0c.
Fixes #33346.

src/network/networkd-ndisc.c

index 0c43965ea60df7531176d068a26f7f65302fa5ff..50d284b26ac43464c03142762059aece9f31e231 100644 (file)
@@ -304,18 +304,44 @@ static int ndisc_remove_route(Route *route, Link *link) {
         if (r < 0)
                 return r;
 
-        if (route->pref_set) {
-                ndisc_set_route_priority(link, route);
-                return route_remove_and_cancel(route, link->manager);
-        }
-
-        uint8_t pref;
+        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);
-                r = route_remove_and_cancel(route, link->manager);
-                if (r < 0)
-                        return r;
+
+                /* Unfortunately, we cannot directly pass 'route' to route_remove_and_cancel() here, as the
+                 * same or similar route may be configured or requested statically. */
+
+                /* First, check if the route is already requested. If there is an existing route, and also an
+                 * existing pending request, then the source may be updated by the request. So, we first need
+                 * to check the source of the requested route. */
+                if (route_get_request(link->manager, route, &req) >= 0) {
+                        existing = ASSERT_PTR(req->userdata);
+                        if (existing->source == NETWORK_CONFIG_SOURCE_STATIC)
+                                continue;
+
+                        r = route_remove_and_cancel(existing, link->manager);
+                        if (r < 0)
+                                return r;
+                }
+
+                /* Then, check if the route exists. */
+                if (route_get(link->manager, route, &existing) >= 0) {
+                        if (existing->source == NETWORK_CONFIG_SOURCE_STATIC)
+                                continue;
+
+                        r = route_remove_and_cancel(existing, link->manager);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         return 0;