#include <linux/nexthop.h>
#include "alloc-util.h"
+#include "event-util.h"
#include "netlink-util.h"
#include "networkd-address.h"
#include "networkd-ipv4ll.h"
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));
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) {
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)
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);
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;
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);
_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;
}
}
+ 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. */
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) {
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;
}