diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 33afdb0335..78e011a855 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -67,8 +67,8 @@ int address_new(Address **ret) { *address = (Address) { .family = AF_UNSPEC, .scope = RT_SCOPE_UNIVERSE, - .cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME, - .cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME, + .lifetime_valid_usec = USEC_INFINITY, + .lifetime_preferred_usec = USEC_INFINITY, .set_broadcast = -1, .duplicate_address_detection = ADDRESS_FAMILY_IPV6, }; @@ -224,6 +224,41 @@ static bool address_may_set_broadcast(const Address *a, const Link *link) { return !streq_ptr(link->kind, "wireguard"); } +static struct ifa_cacheinfo *address_set_cinfo(const Address *a, struct ifa_cacheinfo *cinfo) { + usec_t now_usec; + + assert(a); + assert(cinfo); + + now_usec = now(clock_boottime_or_monotonic()); + + *cinfo = (struct ifa_cacheinfo) { + .ifa_valid = MIN(usec_sub_unsigned(a->lifetime_valid_usec, now_usec) / USEC_PER_SEC, UINT32_MAX), + .ifa_prefered = MIN(usec_sub_unsigned(a->lifetime_preferred_usec, now_usec) / USEC_PER_SEC, UINT32_MAX), + }; + + return cinfo; +} + +static void address_set_lifetime(Address *a, const struct ifa_cacheinfo *cinfo) { + usec_t now_usec; + + assert(a); + assert(cinfo); + + now_usec = now(clock_boottime_or_monotonic()); + + if (cinfo->ifa_valid == UINT32_MAX) + a->lifetime_valid_usec = USEC_INFINITY; + else + a->lifetime_valid_usec = usec_add(cinfo->ifa_valid * USEC_PER_SEC, now_usec); + + if (cinfo->ifa_prefered == UINT32_MAX) + a->lifetime_preferred_usec = USEC_INFINITY; + else + a->lifetime_preferred_usec = usec_add(cinfo->ifa_prefered * USEC_PER_SEC, now_usec); +} + static uint32_t address_prefix(const Address *a) { assert(a); @@ -553,16 +588,16 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union return false; } -const char* format_lifetime(char *buf, size_t l, uint32_t lifetime) { +const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) { assert(buf); assert(l > 4); - if (lifetime == CACHE_INFO_INFINITY_LIFE_TIME) + if (lifetime_usec == USEC_INFINITY) return "forever"; sprintf(buf, "for "); /* format_timespan() never fails */ - assert_se(format_timespan(buf + 4, l - 4, lifetime * USEC_PER_SEC, USEC_PER_SEC)); + assert_se(format_timespan(buf + 4, l - 4, usec_sub_unsigned(lifetime_usec, now(clock_boottime_or_monotonic())), USEC_PER_SEC)); return buf; } @@ -586,8 +621,8 @@ static void log_address_debug(const Address *address, const char *str, const Lin log_link_debug(link, "%s %s address (%s): %s%s%s/%u (valid %s, preferred %s), flags: %s", str, strna(network_config_source_to_string(address->source)), strna(state), strnull(addr), peer ? " peer " : "", strempty(peer), address->prefixlen, - FORMAT_LIFETIME(address->cinfo.ifa_valid), - FORMAT_LIFETIME(address->cinfo.ifa_prefered), + FORMAT_LIFETIME(address->lifetime_valid_usec), + FORMAT_LIFETIME(address->lifetime_preferred_usec), strna(flags_str)); } @@ -684,7 +719,7 @@ bool link_address_is_dynamic(const Link *link, const Address *address) { assert(link); assert(address); - if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME) + if (address->lifetime_preferred_usec != USEC_INFINITY) return true; /* Even when the address is leased from a DHCP server, networkd assign the address @@ -983,7 +1018,8 @@ static int address_configure( return log_link_error_errno(link, r, "Could not append IFA_LABEL attribute: %m"); } - r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo); + r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO, + address_set_cinfo(address, &(struct ifa_cacheinfo) {})); if (r < 0) return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m"); @@ -1192,6 +1228,7 @@ int request_process_address(Request *req) { int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(address_freep) Address *tmp = NULL; + struct ifa_cacheinfo cinfo; Link *link = NULL; uint16_t type; Address *address = NULL; @@ -1326,7 +1363,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, assert_not_reached(); } - r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &tmp->cinfo); + r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); if (r < 0 && r != -ENODATA) { log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m"); return 0; @@ -1340,10 +1377,11 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, /* update flags and etc. */ address->flags = tmp->flags; address->scope = tmp->scope; - address->cinfo = tmp->cinfo; + address_set_lifetime(address, &cinfo); address_enter_configured(address); log_address_debug(address, "Received updated", link); } else { + address_set_lifetime(tmp, &cinfo); address_enter_configured(tmp); log_address_debug(tmp, "Received new", link); @@ -1602,7 +1640,7 @@ int config_parse_lifetime( Network *network = userdata; _cleanup_(address_free_or_set_invalidp) Address *n = NULL; - uint32_t k; + usec_t k; int r; assert(filename); @@ -1622,7 +1660,7 @@ int config_parse_lifetime( /* We accept only "forever", "infinity", empty, or "0". */ if (STR_IN_SET(rvalue, "forever", "infinity", "")) - k = CACHE_INFO_INFINITY_LIFE_TIME; + k = USEC_INFINITY; else if (streq(rvalue, "0")) k = 0; else { @@ -1631,7 +1669,7 @@ int config_parse_lifetime( return 0; } - n->cinfo.ifa_prefered = k; + n->lifetime_preferred_usec = k; TAKE_PTR(n); return 0; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 20e957ccaf..75edfd70dc 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -11,8 +11,7 @@ #include "in-addr-util.h" #include "networkd-link.h" #include "networkd-util.h" - -#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU +#include "time-util.h" typedef struct Address Address; typedef struct Manager Manager; @@ -37,11 +36,15 @@ struct Address { int set_broadcast; struct in_addr broadcast; - struct ifa_cacheinfo cinfo; union in_addr_union in_addr; union in_addr_union in_addr_peer; + /* These are absolute points in time, and NOT timespans/durations. + * Must be specified with clock_boottime_or_monotonic(). */ + usec_t lifetime_valid_usec; + usec_t lifetime_preferred_usec; + bool scope_set:1; bool ip_masquerade_done:1; AddressFamily duplicate_address_detection; @@ -51,7 +54,7 @@ struct Address { address_ready_callback_t callback; }; -const char* format_lifetime(char *buf, size_t l, uint32_t lifetime) _warn_unused_result_; +const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) _warn_unused_result_; /* Note: the lifetime of the compound literal is the immediately surrounding block, * see C11 §6.5.2.5, and * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */ diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index a8d6498423..1ee0052a75 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -841,10 +841,10 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * static int dhcp4_request_address(Link *link, bool announce) { _cleanup_(address_freep) Address *addr = NULL; - uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; struct in_addr address, netmask, server; unsigned prefixlen; Address *existing; + usec_t lifetime_usec; int r; assert(link); @@ -864,10 +864,15 @@ static int dhcp4_request_address(Link *link, bool announce) { return log_link_debug_errno(link, r, "DHCP error: failed to get DHCP server IP address: %m"); if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) { - r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); + uint32_t lifetime_sec; + + r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime_sec); if (r < 0) return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); - } + + lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(clock_boottime_or_monotonic())); + } else + lifetime_usec = USEC_INFINITY; prefixlen = in4_addr_netmask_to_prefixlen(&netmask); @@ -908,8 +913,8 @@ static int dhcp4_request_address(Link *link, bool announce) { addr->provider.in = server; addr->family = AF_INET; addr->in_addr.in.s_addr = address.s_addr; - addr->cinfo.ifa_prefered = lifetime; - addr->cinfo.ifa_valid = lifetime; + addr->lifetime_preferred_usec = lifetime_usec; + addr->lifetime_valid_usec = lifetime_usec; addr->prefixlen = prefixlen; if (prefixlen <= 30) addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr; @@ -1698,7 +1703,6 @@ int config_parse_dhcp_fallback_lease_lifetime( void *userdata) { Network *network = userdata; - uint32_t k; assert(filename); assert(section); @@ -1712,15 +1716,13 @@ int config_parse_dhcp_fallback_lease_lifetime( } /* We accept only "forever" or "infinity". */ - if (STR_IN_SET(rvalue, "forever", "infinity")) - k = CACHE_INFO_INFINITY_LIFE_TIME; - else { + if (!STR_IN_SET(rvalue, "forever", "infinity")) { log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid LeaseLifetime= value, ignoring: %s", rvalue); return 0; } - network->dhcp_fallback_lease_lifetime = k; + network->dhcp_fallback_lease_lifetime = UINT32_MAX; return 0; } diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index ee834d50fa..10a4496d48 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -43,7 +43,7 @@ bool link_dhcp6_pd_is_enabled(Link *link) { } static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { - uint32_t lifetime_preferred, lifetime_valid; + uint32_t lifetime_preferred_sec, lifetime_valid_sec; struct in6_addr pd_prefix; uint8_t pd_prefix_len; @@ -52,7 +52,7 @@ static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { sd_dhcp6_lease_reset_pd_prefix_iter(lease); - return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0; + return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred_sec, &lifetime_valid_sec) >= 0; } static void link_remove_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) { @@ -287,7 +287,7 @@ static int dhcp6_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link return 1; } -static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, usec_t timestamp_usec, uint32_t lifetime_sec) { +static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, usec_t lifetime_usec) { _cleanup_(route_freep) Route *route = NULL; Route *existing; int r; @@ -309,7 +309,7 @@ static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, use route->dst_prefixlen = 64; route->protocol = RTPROT_DHCP; route->priority = link->network->dhcp6_pd_route_metric; - route->lifetime = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); + route->lifetime_usec = lifetime_usec; if (route_get(NULL, link, route, &existing) < 0) link->dhcp6_pd_configured = false; @@ -359,15 +359,15 @@ static void log_dhcp6_pd_address(Link *link, const Address *address) { log_link_full(link, log_level, "DHCPv6-PD address %s (valid %s, preferred %s)", strna(buffer), - FORMAT_LIFETIME(address->cinfo.ifa_valid), - FORMAT_LIFETIME(address->cinfo.ifa_prefered)); + FORMAT_LIFETIME(address->lifetime_valid_usec), + FORMAT_LIFETIME(address->lifetime_preferred_usec)); } static int dhcp6_pd_request_address( Link *link, const struct in6_addr *prefix, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { + usec_t lifetime_preferred_usec, + usec_t lifetime_valid_usec) { _cleanup_set_free_ Set *addresses = NULL; struct in6_addr *a; @@ -396,8 +396,8 @@ static int dhcp6_pd_request_address( address->family = AF_INET6; address->in_addr.in6 = *a; address->prefixlen = 64; - address->cinfo.ifa_prefered = lifetime_preferred; - address->cinfo.ifa_valid = lifetime_valid; + address->lifetime_preferred_usec = lifetime_preferred_usec; + address->lifetime_valid_usec = lifetime_valid_usec; SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp6_pd_manage_temporary_address); address->route_metric = link->network->dhcp6_pd_route_metric; @@ -420,9 +420,8 @@ static int dhcp6_pd_request_address( static int dhcp6_pd_assign_prefix( Link *link, const struct in6_addr *prefix, - usec_t timestamp_usec, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { + usec_t lifetime_preferred_usec, + usec_t lifetime_valid_usec) { int r; @@ -431,16 +430,16 @@ static int dhcp6_pd_assign_prefix( assert(prefix); if (link->network->dhcp6_pd_announce) { - r = radv_add_prefix(link, prefix, 64, lifetime_preferred, lifetime_valid); + r = radv_add_prefix(link, prefix, 64, lifetime_preferred_usec, lifetime_valid_usec); if (r < 0) return r; } - r = dhcp6_pd_request_route(link, prefix, timestamp_usec, lifetime_valid); + r = dhcp6_pd_request_route(link, prefix, lifetime_valid_usec); if (r < 0) return r; - r = dhcp6_pd_request_address(link, prefix, lifetime_preferred, lifetime_valid); + r = dhcp6_pd_request_address(link, prefix, lifetime_preferred_usec, lifetime_valid_usec); if (r < 0) return r; @@ -528,9 +527,8 @@ static int dhcp6_pd_prefix_distribute( Link *dhcp6_link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, - usec_t timestamp_usec, - uint32_t lifetime_preferred, - uint32_t lifetime_valid, + usec_t lifetime_preferred_usec, + usec_t lifetime_valid_usec, bool assign_preferred_subnet_id) { Link *link; @@ -562,7 +560,7 @@ static int dhcp6_pd_prefix_distribute( continue; (void) in6_addr_prefix_to_string(&assigned_prefix, 64, &buf); - r = dhcp6_pd_assign_prefix(link, &assigned_prefix, timestamp_usec, lifetime_preferred, lifetime_valid); + r = dhcp6_pd_assign_prefix(link, &assigned_prefix, lifetime_preferred_usec, lifetime_valid_usec); if (r < 0) { log_link_warning_errno(link, r, "Failed to assign/update prefix %s: %m", strna(buf)); if (link == dhcp6_link) @@ -748,7 +746,7 @@ static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li return 1; } -static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *addr, uint8_t prefixlen, usec_t timestamp_usec, uint32_t lifetime_sec) { +static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *addr, uint8_t prefixlen, usec_t lifetime_usec) { _cleanup_(route_freep) Route *route = NULL; _cleanup_free_ char *buf = NULL; Route *existing; @@ -777,7 +775,7 @@ static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *ad route->type = RTN_UNREACHABLE; route->protocol = RTPROT_DHCP; route->priority = DHCP_ROUTE_METRIC; - route->lifetime = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); + route->lifetime_usec = lifetime_usec; if (route_get(link->manager, NULL, route, &existing) < 0) link->dhcp6_configured = false; @@ -851,22 +849,26 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { } for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) { - uint32_t lifetime_preferred, lifetime_valid; + uint32_t lifetime_preferred_sec, lifetime_valid_sec; + usec_t lifetime_preferred_usec, lifetime_valid_usec; struct in6_addr pd_prefix; uint8_t pd_prefix_len; r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix, &pd_prefix_len, - &lifetime_preferred, &lifetime_valid); + &lifetime_preferred_sec, &lifetime_valid_sec); if (r < 0) break; + lifetime_preferred_usec = usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec); + lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec); + r = dhcp6_pd_prefix_add(dhcp6_link, &pd_prefix, pd_prefix_len); if (r < 0) return r; if (r == 0) continue; - r = dhcp6_request_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len, timestamp_usec, lifetime_valid); + r = dhcp6_request_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len, lifetime_valid_usec); if (r < 0) return r; @@ -898,9 +900,8 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { r = dhcp6_pd_prefix_distribute(dhcp6_link, &pd_prefix, pd_prefix_len, - timestamp_usec, - lifetime_preferred, - lifetime_valid, + lifetime_preferred_usec, + lifetime_valid_usec, true); if (r < 0) return r; @@ -908,9 +909,8 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { r = dhcp6_pd_prefix_distribute(dhcp6_link, &pd_prefix, pd_prefix_len, - timestamp_usec, - lifetime_preferred, - lifetime_valid, + lifetime_preferred_usec, + lifetime_valid_usec, false); if (r < 0) return r; @@ -975,8 +975,8 @@ static void log_dhcp6_address(Link *link, const Address *address) { log_link_warning(link, "DHCPv6 address %s/%u (valid %s, preferred %s) conflicts the address %s/%u%s.", strna(buffer), address->prefixlen, - FORMAT_LIFETIME(address->cinfo.ifa_valid), - FORMAT_LIFETIME(address->cinfo.ifa_prefered), + FORMAT_LIFETIME(address->lifetime_valid_usec), + FORMAT_LIFETIME(address->lifetime_preferred_usec), strna(buffer), existing->prefixlen, by_ndisc ? " assigned by NDisc. Please try to use or update IPv6Token= setting " "to change the address generated by NDISC, or disable UseAutonomousPrefix=" : ""); @@ -985,15 +985,15 @@ static void log_dhcp6_address(Link *link, const Address *address) { simple_log: log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s, preferred %s)", strna(buffer), address->prefixlen, - FORMAT_LIFETIME(address->cinfo.ifa_valid), - FORMAT_LIFETIME(address->cinfo.ifa_prefered)); + FORMAT_LIFETIME(address->lifetime_valid_usec), + FORMAT_LIFETIME(address->lifetime_preferred_usec)); } static int dhcp6_request_address( Link *link, const struct in6_addr *ip6_addr, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { + usec_t lifetime_preferred_usec, + usec_t lifetime_valid_usec) { _cleanup_(address_freep) Address *addr = NULL; Address *existing; @@ -1008,8 +1008,8 @@ static int dhcp6_request_address( addr->in_addr.in6 = *ip6_addr; addr->flags = IFA_F_NOPREFIXROUTE; addr->prefixlen = 128; - addr->cinfo.ifa_prefered = lifetime_preferred; - addr->cinfo.ifa_valid = lifetime_valid; + addr->lifetime_preferred_usec = lifetime_preferred_usec; + addr->lifetime_valid_usec = lifetime_valid_usec; log_dhcp6_address(link, addr); @@ -1031,6 +1031,7 @@ static int dhcp6_request_address( } static int dhcp6_address_acquired(Link *link) { + usec_t timestamp_usec; int r; assert(link); @@ -1040,15 +1041,21 @@ static int dhcp6_address_acquired(Link *link) { if (!link->network->dhcp6_use_address) return 0; + r = sd_dhcp6_lease_get_timestamp(link->dhcp6_lease, clock_boottime_or_monotonic(), ×tamp_usec); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to get timestamp of DHCPv6 lease: %m"); + for (sd_dhcp6_lease_reset_address_iter(link->dhcp6_lease);;) { - uint32_t lifetime_preferred, lifetime_valid; + uint32_t lifetime_preferred_sec, lifetime_valid_sec; struct in6_addr ip6_addr; - r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr, &lifetime_preferred, &lifetime_valid); + r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr, &lifetime_preferred_sec, &lifetime_valid_sec); if (r < 0) break; - r = dhcp6_request_address(link, &ip6_addr, lifetime_preferred, lifetime_valid); + r = dhcp6_request_address(link, &ip6_addr, + usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec), + usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec)); if (r < 0) return r; } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 956cc27380..2bcb25daad 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -291,23 +291,29 @@ static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) { static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; + usec_t lifetime_usec, timestamp_usec; struct in6_addr gateway; - uint32_t mtu = 0; + uint16_t lifetime_sec; unsigned preference; - uint16_t lifetime; - usec_t time_now; + uint32_t mtu = 0; int r; assert(link); assert(rt); - r = sd_ndisc_router_get_lifetime(rt, &lifetime); + r = sd_ndisc_router_get_lifetime(rt, &lifetime_sec); if (r < 0) return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m"); - if (lifetime == 0) /* not a default router */ + if (lifetime_sec == 0) /* not a default router */ return 0; + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + + lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); + r = sd_ndisc_router_get_address(rt, &gateway); if (r < 0) return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); @@ -327,10 +333,6 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m"); - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); - if (link->network->ipv6_accept_ra_use_mtu) { r = sd_ndisc_router_get_mtu(rt, &mtu); if (r < 0 && r != -ENODATA) @@ -345,7 +347,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route->pref = preference; route->gw_family = AF_INET6; route->gw.in6 = gateway; - route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC); + route->lifetime_usec = lifetime_usec; route->mtu = mtu; r = ndisc_request_route(TAKE_PTR(route), link, rt); @@ -367,7 +369,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { route->gw.in6 = gateway; if (!route->pref_set) route->pref = preference; - route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC); + route->lifetime_usec = lifetime_usec; if (route->mtu == 0) route->mtu = mtu; @@ -380,19 +382,17 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { } static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) { - uint32_t lifetime_valid, lifetime_preferred; + uint32_t lifetime_valid_sec, lifetime_preferred_sec; + usec_t lifetime_valid_usec, lifetime_preferred_usec, timestamp_usec; _cleanup_set_free_ Set *addresses = NULL; struct in6_addr prefix, *a; unsigned prefixlen; - usec_t time_now; int r; assert(link); assert(rt); - /* Do not use clock_boottime_or_monotonic() here, as the kernel internally manages cstamp and - * tstamp with jiffies, and it is not increased while the system is suspended. */ - r = sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &time_now); + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -413,23 +413,26 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r return 0; } - r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid); + r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_sec); if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m"); - if (lifetime_valid == 0) { + if (lifetime_valid_sec == 0) { log_link_debug(link, "Ignoring prefix as its valid lifetime is zero."); return 0; } - r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred); + r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_sec); if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m"); /* The preferred lifetime is never greater than the valid lifetime */ - if (lifetime_preferred > lifetime_valid) + if (lifetime_preferred_sec > lifetime_valid_sec) return 0; + lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec); + lifetime_preferred_usec = usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec); + r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses); if (r < 0) return log_link_error_errno(link, r, "Failed to generate SLAAC addresses: %m"); @@ -446,8 +449,8 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r address->in_addr.in6 = *a; address->prefixlen = prefixlen; address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; - address->cinfo.ifa_valid = lifetime_valid; - address->cinfo.ifa_prefered = lifetime_preferred; + address->lifetime_valid_usec = lifetime_valid_usec; + address->lifetime_preferred_usec = lifetime_preferred_usec; /* See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by * honoring all valid lifetimes to improve the reaction of SLAAC to renumbering events. @@ -456,9 +459,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r if (r > 0) { /* If the address is already assigned, but not valid anymore, then refuse to * update the address, and it will be removed. */ - if (e->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME && - usec_add(e->cinfo.tstamp / 100 * USEC_PER_SEC, - e->cinfo.ifa_valid * USEC_PER_SEC) < time_now) + if (e->lifetime_valid_usec < timestamp_usec) continue; } @@ -472,15 +473,22 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; - usec_t time_now; - uint32_t lifetime; + usec_t timestamp_usec; + uint32_t lifetime_sec; unsigned prefixlen; int r; assert(link); assert(rt); - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_sec); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m"); + + if (lifetime_sec == 0) + return 0; + + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -488,10 +496,6 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix length: %m"); - r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime); - if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m"); - r = route_new(&route); if (r < 0) return log_oom(); @@ -499,7 +503,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { route->family = AF_INET6; route->flags = RTM_F_PREFIX; route->dst_prefixlen = prefixlen; - route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC); + route->lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6); if (r < 0) @@ -514,19 +518,19 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; - struct in6_addr gateway, dst; - uint32_t lifetime; unsigned preference, prefixlen; - usec_t time_now; + struct in6_addr gateway, dst; + uint32_t lifetime_sec; + usec_t timestamp_usec; int r; assert(link); - r = sd_ndisc_router_route_get_lifetime(rt, &lifetime); + r = sd_ndisc_router_route_get_lifetime(rt, &lifetime_sec); if (r < 0) return log_link_error_errno(link, r, "Failed to get route lifetime from RA: %m"); - if (lifetime == 0) + if (lifetime_sec == 0) return 0; r = sd_ndisc_router_route_get_address(rt, &dst); @@ -568,7 +572,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m"); - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -582,7 +586,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { route->gw_family = AF_INET6; route->dst.in6 = dst; route->dst_prefixlen = prefixlen; - route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC); + route->lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) @@ -607,10 +611,10 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( free); static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { - uint32_t lifetime; + usec_t lifetime_usec, timestamp_usec; + uint32_t lifetime_sec; const struct in6_addr *a; struct in6_addr router; - usec_t time_now; bool updated = false; int n, r; @@ -621,21 +625,23 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get router address from RA: %m"); - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); - r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime); + r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime_sec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RDNSS lifetime: %m"); + if (lifetime_sec == 0) + return 0; + + lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); + n = sd_ndisc_router_rdnss_get_addresses(rt, &a); if (n < 0) return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); - if (lifetime == 0) - return 0; - if (n >= (int) NDISC_RDNSS_MAX) { log_link_warning(link, "Too many RDNSS records per link. Only first %i records will be used.", NDISC_RDNSS_MAX); n = NDISC_RDNSS_MAX; @@ -651,7 +657,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (rdnss) { rdnss->marked = false; rdnss->router = router; - rdnss->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC); + rdnss->lifetime_usec = lifetime_usec; continue; } @@ -662,7 +668,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { *x = (NDiscRDNSS) { .address = a[j], .router = router, - .valid_until = usec_add(time_now, lifetime * USEC_PER_SEC), + .lifetime_usec = lifetime_usec, }; r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x)); @@ -696,9 +702,9 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { _cleanup_strv_free_ char **l = NULL; + usec_t lifetime_usec, timestamp_usec; struct in6_addr router; - uint32_t lifetime; - usec_t time_now; + uint32_t lifetime_sec; bool updated = false; char **j; int r; @@ -710,21 +716,23 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get router address from RA: %m"); - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); - r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime); + r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime_sec); if (r < 0) return log_link_error_errno(link, r, "Failed to get DNSSL lifetime: %m"); + if (lifetime_sec == 0) + return 0; + + lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); + r = sd_ndisc_router_dnssl_get_domains(rt, &l); if (r < 0) return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); - if (lifetime == 0) - return 0; - if (strv_length(l) >= NDISC_DNSSL_MAX) { log_link_warning(link, "Too many DNSSL records per link. Only first %i records will be used.", NDISC_DNSSL_MAX); STRV_FOREACH(j, l + NDISC_DNSSL_MAX) @@ -745,12 +753,12 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (dnssl) { dnssl->marked = false; dnssl->router = router; - dnssl->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC); + dnssl->lifetime_usec = lifetime_usec; continue; } s->router = router; - s->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC); + s->lifetime_usec = lifetime_usec; r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s)); if (r < 0) @@ -1042,11 +1050,11 @@ void ndisc_vacuum(Link *link) { time_now = now(clock_boottime_or_monotonic()); SET_FOREACH(r, link->ndisc_rdnss) - if (r->valid_until < time_now) + if (r->lifetime_usec < time_now) free(set_remove(link->ndisc_rdnss, r)); SET_FOREACH(d, link->ndisc_dnssl) - if (d->valid_until < time_now) + if (d->lifetime_usec < time_now) free(set_remove(link->ndisc_dnssl, d)); } diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index ed50ebbf9a..acb97a892e 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -19,7 +19,9 @@ typedef struct NDiscRDNSS { /* Used when GC'ing old DNS servers when configuration changes. */ bool marked; struct in6_addr router; - usec_t valid_until; + /* This is an absolute point in time, and NOT a timespan/duration. + * Must be specified with clock_boottime_or_monotonic(). */ + usec_t lifetime_usec; struct in6_addr address; } NDiscRDNSS; @@ -27,7 +29,9 @@ typedef struct NDiscDNSSL { /* Used when GC'ing old domains when configuration changes. */ bool marked; struct in6_addr router; - usec_t valid_until; + /* This is an absolute point in time, and NOT a timespan/duration. + * Must be specified with clock_boottime_or_monotonic(). */ + usec_t lifetime_usec; /* The domain name follows immediately. */ } NDiscDNSSL; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 73e9bce52a..c3a4ca6a3a 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -629,10 +629,11 @@ int radv_add_prefix( Link *link, const struct in6_addr *prefix, uint8_t prefix_len, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { + usec_t lifetime_preferred_usec, + usec_t lifetime_valid_usec) { _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; + usec_t now_usec; int r; assert(link); @@ -640,6 +641,8 @@ int radv_add_prefix( if (!link->radv) return 0; + now_usec = now(clock_boottime_or_monotonic()); + r = sd_radv_prefix_new(&p); if (r < 0) return r; @@ -648,11 +651,11 @@ int radv_add_prefix( if (r < 0) return r; - r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred); + r = sd_radv_prefix_set_preferred_lifetime(p, usec_sub_unsigned(lifetime_preferred_usec, now_usec) / USEC_PER_SEC); if (r < 0) return r; - r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid); + r = sd_radv_prefix_set_valid_lifetime(p, usec_sub_unsigned(lifetime_valid_usec, now_usec) / USEC_PER_SEC); if (r < 0) return r; diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index d51fe323dc..6fd27ef39e 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -64,7 +64,7 @@ int link_request_radv_addresses(Link *link); int radv_update_mac(Link *link); int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, - uint32_t lifetime_preferred, uint32_t lifetime_valid); + usec_t lifetime_preferred_usec, usec_t lifetime_valid_usec); int request_process_radv(Request *req); int link_request_radv(Link *link); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 29e4b2b609..4dc6ecda62 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -186,7 +186,7 @@ int route_new(Route **ret) { .protocol = RTPROT_UNSPEC, .type = RTN_UNICAST, .table = RT_TABLE_MAIN, - .lifetime = USEC_INFINITY, + .lifetime_usec = USEC_INFINITY, .quickack = -1, .fast_open_no_cookie = -1, .gateway_onlink = -1, @@ -1240,7 +1240,7 @@ static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo manager = route->manager ?: route->link->manager; - if (route->lifetime == USEC_INFINITY) + if (route->lifetime_usec == USEC_INFINITY) return 0; if (cacheinfo && cacheinfo->rta_expires != 0) @@ -1248,7 +1248,7 @@ static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo return 0; r = event_reset_time(manager->event, &route->expire, clock_boottime_or_monotonic(), - route->lifetime, 0, route_expire_handler, route, 0, "route-expiration", true); + route->lifetime_usec, 0, route_expire_handler, route, 0, "route-expiration", true); if (r < 0) return r; @@ -1392,9 +1392,9 @@ static int route_configure( if (r < 0) return r; - if (route->lifetime != USEC_INFINITY) { + if (route->lifetime_usec != 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)); + MIN(DIV_ROUND_UP(usec_sub_unsigned(route->lifetime_usec, now(clock_boottime_or_monotonic())), USEC_PER_SEC), UINT32_MAX)); if (r < 0) return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m"); } @@ -3039,7 +3039,7 @@ static int route_section_verify(Route *route, Network *network) { return -EINVAL; /* Currently, we do not support static route with finite lifetime. */ - assert(route->lifetime == USEC_INFINITY); + assert(route->lifetime_usec == USEC_INFINITY); if (route->gateway_from_dhcp_or_ra) { if (route->gw_family == AF_UNSPEC) { diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 3e817de3ac..612d7d2809 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -61,7 +61,10 @@ typedef struct Route { union in_addr_union prefsrc; OrderedSet *multipath_routes; - usec_t lifetime; + /* This is an absolute point in time, and NOT a timespan/duration. + * Must be specified with clock_boottime_or_monotonic(). */ + usec_t lifetime_usec; + /* Used when kernel does not support RTA_EXPIRES attribute. */ sd_event_source *expire; } Route; diff --git a/src/network/test-networkd-address.c b/src/network/test-networkd-address.c index 7c1d65a433..564c76f848 100644 --- a/src/network/test-networkd-address.c +++ b/src/network/test-networkd-address.c @@ -2,21 +2,26 @@ #include "networkd-address.h" #include "tests.h" +#include "time-util.h" -static void test_FORMAT_LIFETIME_one(uint32_t lifetime, const char *expected) { +static void test_FORMAT_LIFETIME_one(usec_t lifetime, const char *expected) { const char *t = FORMAT_LIFETIME(lifetime); - log_debug("%"PRIu32 " → \"%s\" (expected \"%s\")", lifetime, t, expected); + log_debug(USEC_FMT " → \"%s\" (expected \"%s\")", lifetime, t, expected); assert_se(streq(t, expected)); } static void test_FORMAT_LIFETIME(void) { + usec_t now_usec; + log_info("/* %s */", __func__); - test_FORMAT_LIFETIME_one(0, "for 0"); - test_FORMAT_LIFETIME_one(1, "for 1s"); - test_FORMAT_LIFETIME_one(3 * (USEC_PER_WEEK/USEC_PER_SEC), "for 3w"); - test_FORMAT_LIFETIME_one(CACHE_INFO_INFINITY_LIFE_TIME, "forever"); + now_usec = now(CLOCK_MONOTONIC); + + test_FORMAT_LIFETIME_one(now_usec, "for 0"); + test_FORMAT_LIFETIME_one(usec_add(now_usec, 2 * USEC_PER_SEC - 1), "for 1s"); + test_FORMAT_LIFETIME_one(usec_add(now_usec, 3 * USEC_PER_WEEK + USEC_PER_SEC - 1), "for 3w"); + test_FORMAT_LIFETIME_one(USEC_INFINITY, "forever"); } int main(int argc, char *argv[]) {