]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: route: drop kernel version check for route expiration 21035/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 15 Oct 2021 01:07:57 +0000 (10:07 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 19 Oct 2021 11:33:43 +0000 (20:33 +0900)
Even in newer kernel version, it seems that some route type does not
support expiration, e.g. IPv4 route or unreachable route. Let's use
timer event source for such routes.

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

index 767f47a8b4c2e7905bd1e3d766febf339a2a1fc0..29e4b2b6090d92e337513d54fd8dc1fdaa9d21c2 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/nexthop.h>
 
 #include "alloc-util.h"
+#include "event-util.h"
 #include "netlink-util.h"
 #include "networkd-address.h"
 #include "networkd-ipv4ll.h"
@@ -1230,10 +1231,9 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
         return 1;
 }
 
-static int route_setup_timer(Route *route) {
+static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo) {
         Manager *manager;
-
-        /* TODO: drop expiration handling once it can be pushed into the kernel */
+        int r;
 
         assert(route);
         assert(route->manager || (route->link && route->link->manager));
@@ -1243,13 +1243,16 @@ static int route_setup_timer(Route *route) {
         if (route->lifetime == USEC_INFINITY)
                 return 0;
 
-        if (kernel_route_expiration_supported())
+        if (cacheinfo && cacheinfo->rta_expires != 0)
+                /* Assume that non-zero rta_expires means kernel will handle the route expiration. */
                 return 0;
 
-        sd_event_source_disable_unref(route->expire);
+        r = event_reset_time(manager->event, &route->expire, clock_boottime_or_monotonic(),
+                             route->lifetime, 0, route_expire_handler, route, 0, "route-expiration", true);
+        if (r < 0)
+                return r;
 
-        return sd_event_add_time(manager->event, &route->expire, clock_boottime_or_monotonic(),
-                                 route->lifetime, 0, route_expire_handler, route);
+        return 1;
 }
 
 static int append_nexthop_one(const Link *link, const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
@@ -1389,7 +1392,7 @@ static int route_configure(
         if (r < 0)
                 return r;
 
-        if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
+        if (route->lifetime != USEC_INFINITY) {
                 r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
                         MIN(DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC), UINT32_MAX));
                 if (r < 0)
@@ -1730,10 +1733,6 @@ int request_process_route(Request *req) {
                                 existing->provider = converted->routes[i]->provider;
                         }
                 }
-        } else {
-                r = route_setup_timer(route);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to setup timer for route: %m");
         }
 
         r = route_configure(route, link, req->netlink_handler);
@@ -1753,7 +1752,13 @@ int request_process_route(Request *req) {
         return 1;
 }
 
-static int process_route_one(Manager *manager, Link *link, uint16_t type, Route *in) {
+static int process_route_one(
+                Manager *manager,
+                Link *link,
+                uint16_t type,
+                Route *in,
+                const struct rta_cacheinfo *cacheinfo) {
+
         _cleanup_(route_freep) Route *tmp = in;
         Route *route = NULL;
         int r;
@@ -1772,6 +1777,12 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, Route
                         route_enter_configured(route);
                         log_route_debug(route, "Received remembered", link, manager);
 
+                        r = route_setup_timer(route, cacheinfo);
+                        if (r < 0)
+                                log_link_warning_errno(link, r, "Failed to configure expiration timer for route, ignoring: %m");
+                        if (r > 0)
+                                log_route_debug(route, "Configured expiration timer for", link, manager);
+
                 } else if (!manager->manage_foreign_routes) {
                         route_enter_configured(tmp);
                         log_route_debug(tmp, "Ignoring received", link, manager);
@@ -1816,6 +1827,8 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
         _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
         _cleanup_(route_freep) Route *tmp = NULL;
         _cleanup_free_ void *rta_multipath = NULL;
+        struct rta_cacheinfo cacheinfo;
+        bool has_cacheinfo;
         Link *link = NULL;
         uint32_t ifindex;
         uint16_t type;
@@ -2012,6 +2025,13 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 }
         }
 
+        r = sd_netlink_message_read(message, RTA_CACHEINFO, sizeof(cacheinfo), &cacheinfo);
+        if (r < 0 && r != -ENODATA) {
+                log_link_warning_errno(link, r, "rtnl: failed to read RTA_CACHEINFO attribute, ignoring: %m");
+                return 0;
+        }
+        has_cacheinfo = r >= 0;
+
         /* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
          * fib6_nh_init() in net/ipv6/route.c. However, we'd like to manage them by Manager. Hence, set
          * link to NULL here. */
@@ -2019,7 +2039,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 link = NULL;
 
         if (!route_needs_convert(tmp))
-                return process_route_one(m, link, type, TAKE_PTR(tmp));
+                return process_route_one(m, link, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
 
         r = route_convert(m, tmp, &converted);
         if (r < 0) {
@@ -2031,7 +2051,11 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
         assert(converted);
 
         for (size_t i = 0; i < converted->n; i++)
-                (void) process_route_one(m, converted->links[i] ?: link, type, TAKE_PTR(converted->routes[i]));
+                (void) process_route_one(m,
+                                         converted->links[i] ?: link,
+                                         type,
+                                         TAKE_PTR(converted->routes[i]),
+                                         has_cacheinfo ? &cacheinfo : NULL);
 
         return 1;
 }
index 7ac34c18f33992e17c0ec0303f5677db2e344686..b0020efe8993a586716a9f2eb21b5cb024ce9975 100644 (file)
@@ -242,26 +242,6 @@ int config_parse_mud_url(
         return free_and_replace(*url, unescaped);
 }
 
-/* Router lifetime can be set with netlink interface since kernel >= 4.5
- * so for the supported kernel we don't need to expire routes in userspace */
-int kernel_route_expiration_supported(void) {
-        static int cached = -1;
-        int r;
-
-        if (cached < 0) {
-                Condition c = {
-                        .type = CONDITION_KERNEL_VERSION,
-                        .parameter = (char *) ">= 4.5"
-                };
-                r = condition_test(&c, NULL);
-                if (r < 0)
-                        return r;
-
-                cached = r;
-        }
-        return cached;
-}
-
 static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
         siphash24_compress_string(c->filename, state);
         siphash24_compress(&c->line, sizeof(c->line), state);
index 39463cadbdb666dd0396d0958b88fda12c5707ce..21e8a93629768c4359317fcbf7965b212b6a5029 100644 (file)
@@ -140,8 +140,6 @@ AddressFamily dhcp_deprecated_address_family_from_string(const char *s) _pure_;
 const char *dhcp_lease_server_type_to_string(sd_dhcp_lease_server_type_t t) _const_;
 sd_dhcp_lease_server_type_t dhcp_lease_server_type_from_string(const char *s) _pure_;
 
-int kernel_route_expiration_supported(void);
-
 static inline NetworkConfigSection* network_config_section_free(NetworkConfigSection *cs) {
         return mfree(cs);
 }