]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/route: also update source, status, and so on EEXIST
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 1 Sep 2024 01:39:51 +0000 (10:39 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 1 Sep 2024 02:54:32 +0000 (11:54 +0900)
Otherwise, an existing route may be labeled as foreign even after we
reconfigure it.

src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp4.c
src/network/networkd-ndisc.c
src/network/networkd-route.c
src/network/networkd-route.h

index 1ffb4888dd0a3648b3950a516d54045089784118..56555df62c7d17b19eeb56a26bd9fcfb93d4549c 100644 (file)
@@ -269,9 +269,10 @@ static int dhcp_pd_check_ready(Link *link) {
 static int dhcp_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
         int r;
 
+        assert(req);
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, route, "Failed to add prefix route for DHCP delegated subnet prefix");
+        r = route_configure_handler_internal(rtnl, m, req, "Failed to add prefix route for DHCP delegated subnet prefix");
         if (r <= 0)
                 return r;
 
@@ -662,9 +663,10 @@ void dhcp4_pd_prefix_lost(Link *uplink) {
 static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
         int r;
 
+        assert(req);
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, route, "Failed to set unreachable route for DHCPv4 delegated prefix");
+        r = route_configure_handler_internal(rtnl, m, req, "Failed to set unreachable route for DHCPv4 delegated prefix");
         if (r <= 0)
                 return r;
 
@@ -678,9 +680,10 @@ static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message
 static int dhcp6_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
         int r;
 
+        assert(req);
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, route, "Failed to set unreachable route for DHCPv6 delegated prefix");
+        r = route_configure_handler_internal(rtnl, m, req, "Failed to set unreachable route for DHCPv6 delegated prefix");
         if (r <= 0)
                 return r;
 
index b08d454d34f3a4c5859f4f3bf9b0158a2117a454..4a9459f00687532c5b06417bdbf9865d1023746a 100644 (file)
@@ -337,9 +337,10 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
         int r;
 
         assert(m);
+        assert(req);
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, route, "Could not set DHCPv4 route");
+        r = route_configure_handler_internal(rtnl, m, req, "Could not set DHCPv4 route");
         if (r <= 0)
                 return r;
 
index 880b9cbc12037078ba2b908367f03a10391503c6..21b76fafc4516d854cc824ecb6d9c773c309e766 100644 (file)
@@ -150,9 +150,10 @@ static int ndisc_check_ready(Link *link) {
 static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
         int r;
 
+        assert(req);
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, route, "Could not set NDisc route");
+        r = route_configure_handler_internal(rtnl, m, req, "Could not set NDisc route");
         if (r <= 0)
                 return r;
 
index d9a6e259db9e8ff46acfa88d8554289f54cebb86..788b2cb04ff6bddfc40ab002ff943c6ec4649422 100644 (file)
@@ -670,40 +670,97 @@ static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo
         return 1;
 }
 
-int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, Route *route, const char *error_msg) {
+static int route_update_by_request(Route *route, Request *req) {
+        assert(route);
+        assert(req);
+
+        Route *rt = ASSERT_PTR(req->userdata);
+
+        route->provider = rt->provider;
+        route->source = rt->source;
+        route->lifetime_usec = rt->lifetime_usec;
+
+        return 0;
+}
+
+static int route_update_on_existing_one(Request *req, Route *requested) {
+        Manager *manager = ASSERT_PTR(ASSERT_PTR(req)->manager);
+        Route *existing;
+        int r;
+
+        assert(requested);
+
+        if (route_get(manager, requested, &existing) < 0)
+                return 0;
+
+        r = route_update_by_request(existing, req);
+        if (r < 0)
+                return r;
+
+        r = route_setup_timer(existing, NULL);
+        if (r < 0)
+                return r;
+
+        /* This may be a bug in the kernel, but the MTU of an IPv6 route can be updated only when the
+         * route has an expiration timer managed by the kernel (not by us). See fib6_add_rt2node() in
+         * net/ipv6/ip6_fib.c of the kernel. */
+        if (existing->family == AF_INET6 &&
+            existing->expiration_managed_by_kernel) {
+                r = route_metric_set(&existing->metric, RTAX_MTU, route_metric_get(&requested->metric, RTAX_MTU));
+                if (r < 0)
+                        return r;
+        }
+
+        route_enter_configured(existing);
+        return 0;
+}
+
+static int route_update_on_existing(Request *req) {
+        Route *rt = ASSERT_PTR(ASSERT_PTR(req)->userdata);
+        int r;
+
+        if (rt->family == AF_INET || ordered_set_isempty(rt->nexthops))
+                return route_update_on_existing_one(req, rt);
+
+        RouteNextHop *nh;
+        ORDERED_SET_FOREACH(nh, rt->nexthops) {
+                _cleanup_(route_unrefp) Route *dup = NULL;
+
+                r = route_dup(rt, nh, &dup);
+                if (r < 0)
+                        return r;
+
+                r = route_update_on_existing_one(req, dup);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Request *req, const char *error_msg) {
         int r;
 
         assert(m);
-        assert(link);
-        assert(link->manager);
-        assert(route);
+        assert(req);
         assert(error_msg);
 
+        Link *link = ASSERT_PTR(req->link);
+
         r = sd_netlink_message_get_errno(m);
         if (r == -EEXIST) {
-                Route *existing;
-
-                if (route_get(link->manager, route, &existing) >= 0) {
-                        /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE
-                         * notification, so we need to update the timer here. */
-                        existing->lifetime_usec = route->lifetime_usec;
-                        (void) route_setup_timer(existing, NULL);
-
-                        /* This may be a bug in the kernel, but the MTU of an IPv6 route can be updated only
-                         * when the route has an expiration timer managed by the kernel (not by us).
-                         * See fib6_add_rt2node() in net/ipv6/ip6_fib.c of the kernel. */
-                        if (existing->family == AF_INET6 &&
-                            existing->expiration_managed_by_kernel) {
-                                r = route_metric_set(&existing->metric, RTAX_MTU, route_metric_get(&route->metric, RTAX_MTU));
-                                if (r < 0) {
-                                        log_oom();
-                                        link_enter_failed(link);
-                                        return 0;
-                                }
-                        }
+                /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE notification, so
+                 * here we need to update the state, provider, source, timer, and so on. */
+                r = route_update_on_existing(req);
+                if (r < 0) {
+                        log_link_warning_errno(link, r, "Failed to update existing route: %m");
+                        link_enter_failed(link);
+                        return 0;
                 }
 
-        } else if (r < 0) {
+                return 1;
+        }
+        if (r < 0) {
                 log_link_message_warning_errno(link, m, r, error_msg);
                 link_enter_failed(link);
                 return 0;
@@ -930,7 +987,7 @@ static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
 
         assert(link);
 
-        r = route_configure_handler_internal(rtnl, m, link, route, "Could not set route");
+        r = route_configure_handler_internal(rtnl, m, req, "Could not set static route");
         if (r <= 0)
                 return r;
 
@@ -1053,11 +1110,12 @@ static int process_route_one(
 
                 /* Also update information that cannot be obtained through netlink notification. */
                 if (req && req->waiting_reply) {
-                        Route *rt = ASSERT_PTR(req->userdata);
-
-                        route->source = rt->source;
-                        route->provider = rt->provider;
-                        route->lifetime_usec = rt->lifetime_usec;
+                        r = route_update_by_request(route, req);
+                        if (r < 0) {
+                                log_link_warning_errno(link, r, "Failed to update route by request: %m");
+                                link_enter_failed(link);
+                                return 0;
+                        }
                 }
 
                 route_enter_configured(route);
index 912c6e54a9fd5bef7f5a7f1de159ab22c7e96bd8..744cad2f1a5470f92904c619fe60c53f4eb43479 100644 (file)
@@ -91,7 +91,7 @@ int route_new(Route **ret);
 int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret);
 int route_dup(const Route *src, const RouteNextHop *nh, Route **ret);
 
-int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, Route *route, const char *error_msg);
+int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Request *req, const char *error_msg);
 int route_remove(Route *route, Manager *manager);
 int route_remove_and_cancel(Route *route, Manager *manager);