diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 9beaa7822b..7316a08d29 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -26,6 +26,7 @@ #include "alloc-util.h" #include "bus-util.h" #include "dhcp-lease-internal.h" +#include "event-util.h" #include "fd-util.h" #include "fileio.h" #include "netlink-util.h" @@ -2227,6 +2228,9 @@ network_file_fail: return log_oom(); STRV_FOREACH(route_str, routes_strv) { + Route *route; + _cleanup_event_source_unref_ sd_event_source *expire = NULL; + usec_t lifetime; char *prefixlen_str; int family; unsigned char prefixlen, tos, table; @@ -2240,9 +2244,11 @@ network_file_fail: *prefixlen_str ++ = '\0'; - r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%hhu", &prefixlen, &tos, &priority, &table); - if (r != 4) { - log_link_debug(link, "Failed to parse destination prefix length, tos, priority or table %s", prefixlen_str); + r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%hhu/"USEC_FMT, &prefixlen, &tos, &priority, &table, &lifetime); + if (r != 5) { + log_link_debug(link, + "Failed to parse destination prefix length, tos, priority, table or expiration %s", + prefixlen_str); continue; } @@ -2252,9 +2258,21 @@ network_file_fail: continue; } - r = route_add(link, family, &route_dst, prefixlen, tos, priority, table, NULL); + r = route_add(link, family, &route_dst, prefixlen, tos, priority, table, &route); if (r < 0) return log_link_error_errno(link, r, "Failed to add route: %m"); + + if (lifetime != USEC_INFINITY) { + r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), lifetime, + 0, route_expire_handler, route); + if (r < 0) + log_link_warning_errno(link, r, "Could not arm route expiration handler: %m"); + } + + route->lifetime = lifetime; + sd_event_source_unref(route->expire); + route->expire = expire; + expire = NULL; } } @@ -2741,8 +2759,8 @@ int link_save(Link *link) { if (r < 0) goto fail; - fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%hhu", space ? " " : "", route_str, - route->dst_prefixlen, route->tos, route->priority, route->table); + fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%hhu/"USEC_FMT, space ? " " : "", route_str, + route->dst_prefixlen, route->tos, route->priority, route->table, route->lifetime); space = true; } diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 7c0d03cdc7..f4bbd06af1 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -21,6 +21,7 @@ #include "alloc-util.h" #include "conf-parser.h" +#include "event-util.h" #include "in-addr-util.h" #include "netlink-util.h" #include "networkd-route.h" @@ -41,6 +42,7 @@ int route_new(Route **ret) { route->scope = RT_SCOPE_UNIVERSE; route->protocol = RTPROT_UNSPEC; route->table = RT_TABLE_DEFAULT; + route->lifetime = USEC_INFINITY; *ret = route; route = NULL; @@ -101,6 +103,8 @@ void route_free(Route *route) { set_remove(route->link->routes_foreign, route); } + sd_event_source_unref(route->expire); + free(route); } @@ -410,9 +414,24 @@ int route_remove(Route *route, Link *link, return 0; } +int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { + Route *route = userdata; + int r; + + assert(route); + + r = route_remove(route, route->link, NULL); + if (r < 0) + log_warning_errno(r, "Could not remove route: %m"); + + return 1; +} + int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; + _cleanup_event_source_unref_ sd_event_source *expire = NULL; + usec_t lifetime; int r; assert(link); @@ -489,10 +508,26 @@ int route_configure(Route *route, Link *link, link_ref(link); - r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL); + lifetime = route->lifetime; + + r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route); if (r < 0) return log_error_errno(r, "Could not add route: %m"); + /* TODO: drop expiration handling once it can be pushed into the kernel */ + route->lifetime = lifetime; + + if (route->lifetime != USEC_INFINITY) { + r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), + route->lifetime, 0, route_expire_handler, route); + if (r < 0) + return log_error_errno(r, "Could not arm expiration timer: %m"); + } + + sd_event_source_unref(route->expire); + route->expire = expire; + expire = NULL; + return 0; } diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 785bd1663a..d0a51838ed 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -46,6 +46,9 @@ struct Route { union in_addr_union src; union in_addr_union prefsrc; + usec_t lifetime; + sd_event_source *expire; + LIST_FIELDS(Route, routes); }; @@ -61,6 +64,8 @@ int route_add_foreign(Link *link, int family, union in_addr_union *dst, unsigned int route_update(Route *route, union in_addr_union *src, unsigned char src_prefixlen, union in_addr_union *gw, union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol); void route_drop(Route *route); +int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata); + DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free); #define _cleanup_route_free_ _cleanup_(route_freep)