1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-22 13:33:56 +03:00

network: address: use usec_t for handling lifetime

This drops stuct ifa_cacheinfo from Address, and store lifetime with
usec_t.

Why? Now, all requests of address configurations are once stored in
the request queue, and will be processed when it is ready. So, the
timestamp value passed to the kernel must be evaluated on configure.

This also fixes the following two issues.
- Time values in struct ifa_cacheinfo are stored in uint32_t.
  So, the validity check of the address configured by NDisc may fail on
  long running systems.
- If a system uses DHCPv6PD, when an interface may appear or be
  reconfigured later, then the lifetime value may be inappropriate.
  We need to adjust the lifetime with the current time and the timestamp
  of the lease.
This commit is contained in:
Yu Watanabe 2021-10-21 02:21:59 +09:00
parent 03ccc4b423
commit 16bc8635d5
8 changed files with 152 additions and 95 deletions

View File

@ -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;

View File

@ -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 */

View File

@ -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;
}

View File

@ -44,7 +44,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;
@ -53,7 +53,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) {
@ -288,7 +288,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;
@ -310,7 +310,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 = 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;
@ -360,15 +360,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;
@ -397,8 +397,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;
@ -421,9 +421,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;
@ -432,16 +431,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;
@ -529,9 +528,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;
@ -563,7 +561,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)
@ -749,7 +747,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;
@ -778,7 +776,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 = 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;
@ -852,22 +850,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;
@ -899,9 +901,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;
@ -909,9 +910,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;
@ -976,8 +976,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=" : "");
@ -986,15 +986,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;
@ -1009,8 +1009,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);
@ -1032,6 +1032,7 @@ static int dhcp6_request_address(
}
static int dhcp6_address_acquired(Link *link) {
usec_t timestamp_usec;
int r;
assert(link);
@ -1041,15 +1042,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(), &timestamp_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;
}

View File

@ -382,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(), &timestamp_usec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
@ -415,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");
@ -448,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.
@ -458,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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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[]) {