]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/ndisc: remove conflicting routes on configuring routes based on newly received RA
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 2 Feb 2024 03:30:32 +0000 (12:30 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 14 Feb 2024 15:43:12 +0000 (00:43 +0900)
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.

src/network/networkd-ndisc.c
src/network/networkd-route.c
src/network/networkd-route.h

index 5f93f45b3ce0b3269c719f2496c24c4a83af5af0..e813f8c98b7b7564f6dd30bc35ff8c5c95791906 100644 (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);
index 743b4f552001234784c5a52e771c76e5b76c7b3b..ee12f1664abe46b6a2b2db8e8e1a0e5808434d86 100644 (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);
index d292716b1d6d1cc5eae93d5e407eed5529708d52..912c6e54a9fd5bef7f5a7f1de159ab22c7e96bd8 100644 (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);