diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index d8273362b5..d300d39210 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1050,6 +1050,33 @@ monotonic_timestamp_get (struct timespec *tp) } } +/** + * nm_utils_get_monotonic_timestamp_ns: + * + * Returns: a monotonically increasing time stamp in nanoseconds, + * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. + * + * The returned value will start counting at an undefined point + * in the past and will always be positive. + * + * All the nm_utils_get_monotonic_timestamp_*s functions return the same + * timestamp but in different scales (nsec, usec, msec, sec). + **/ +gint64 +nm_utils_get_monotonic_timestamp_ns (void) +{ + struct timespec tp; + + monotonic_timestamp_get (&tp); + + /* Although the result will always be positive, we return a signed + * integer, which makes it easier to calculate time differences (when + * you want to subtract signed values). + **/ + return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * NM_UTILS_NS_PER_SECOND + + tp.tv_nsec; +} + /** * nm_utils_get_monotonic_timestamp_us: * @@ -1058,6 +1085,9 @@ monotonic_timestamp_get (struct timespec *tp) * * The returned value will start counting at an undefined point * in the past and will always be positive. + * + * All the nm_utils_get_monotonic_timestamp_*s functions return the same + * timestamp but in different scales (nsec, usec, msec, sec). **/ gint64 nm_utils_get_monotonic_timestamp_us (void) @@ -1082,6 +1112,9 @@ nm_utils_get_monotonic_timestamp_us (void) * * The returned value will start counting at an undefined point * in the past and will always be positive. + * + * All the nm_utils_get_monotonic_timestamp_*s functions return the same + * timestamp but in different scales (nsec, usec, msec, sec). **/ gint64 nm_utils_get_monotonic_timestamp_ms (void) @@ -1106,6 +1139,9 @@ nm_utils_get_monotonic_timestamp_ms (void) * * This value wraps after roughly 68 years which should be fine for any * practical purpose. + * + * All the nm_utils_get_monotonic_timestamp_*s functions return the same + * timestamp but in different scales (nsec, usec, msec, sec). **/ gint32 nm_utils_get_monotonic_timestamp_s (void) diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 18a0d90fee..a09a30e23c 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -104,6 +104,7 @@ NMConnection *nm_utils_match_connection (GSList *connections, gint64 nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback); #define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000) +gint64 nm_utils_get_monotonic_timestamp_ns (void); gint64 nm_utils_get_monotonic_timestamp_us (void); gint64 nm_utils_get_monotonic_timestamp_ms (void); gint32 nm_utils_get_monotonic_timestamp_s (void); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index a67401c3f5..445611ddca 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -3601,6 +3601,8 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device address.timestamp = discovered_address->timestamp; address.lifetime = discovered_address->lifetime; address.preferred = discovered_address->preferred; + if (address.preferred > address.lifetime) + address.preferred = address.lifetime; address.source = NM_PLATFORM_SOURCE_RDISC; address.flags = ifa_flags; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index f5911c8b70..6fd6ee5489 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -136,6 +136,53 @@ _nl_has_capability (int capability) /******************************************************************/ +static guint32 +_get_expiry (guint32 now_s, guint32 lifetime_s) +{ + gint64 t = ((gint64) now_s) + ((gint64) lifetime_s); + + return MIN (t, NM_PLATFORM_LIFETIME_PERMANENT - 1); +} + +/* The rtnl_addr object contains relative lifetimes @valid and @preferred + * that count in seconds, starting from the moment when the kernel constructed + * the netlink message. + * + * There is also a field rtnl_addr_last_update_time(), which is the absolute + * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address + * was modified (wrapping every 497 days). + * Immediately at the time when the address was last modified, #NOW and @last_update_time + * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. + * However, this is not true in general. As time goes by, whenever kernel sends a new address + * via netlink, the lifetimes keep counting down. + * + * As we cache the rtnl_addr object we must know the absolute expiries. + * As a hack, modify the relative timestamps valid and preferred into absolute + * timestamps of scale nm_utils_get_monotonic_timestamp_s(). + **/ +static void +_rtnl_addr_hack_lifetimes_rel_to_abs (struct rtnl_addr *rtnladdr) +{ + guint32 a_valid = rtnl_addr_get_valid_lifetime (rtnladdr); + guint32 a_preferred = rtnl_addr_get_preferred_lifetime (rtnladdr); + guint32 now; + + if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT && + a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) + return; + + now = (guint32) nm_utils_get_monotonic_timestamp_s (); + + if (a_preferred > a_valid) + a_preferred = a_valid; + + if (a_valid != NM_PLATFORM_LIFETIME_PERMANENT) + rtnl_addr_set_valid_lifetime (rtnladdr, _get_expiry (now, a_valid)); + rtnl_addr_set_preferred_lifetime (rtnladdr, _get_expiry (now, a_preferred)); +} + +/******************************************************************/ + /* libnl library workarounds and additions */ /* Automatic deallocation of local variables */ @@ -402,6 +449,9 @@ get_kernel_object (struct nl_sock *sock, struct nl_object *needle) nl_cache_free (cache); + if (object && (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS)) + _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); + if (object) debug ("get_kernel_object for type %d returned %p", type, object); else @@ -982,6 +1032,53 @@ hack_empty_master_iff_lower_up (NMPlatform *platform, struct nl_object *object) rtnl_link_unset_flags (rtnllink, IFF_LOWER_UP); } +static void +_init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr *rtnladdr) +{ + guint32 a_valid = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr); + guint32 a_preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr); + + /* the meaning of the valid and preferred lifetimes is different from the + * original meaning. See _rtnl_addr_hack_lifetimes_rel_to_abs(). + * Beware: this function expects hacked rtnl_addr objects. + */ + + if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT && + a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) { + address->timestamp = 0; + address->lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + address->preferred = NM_PLATFORM_LIFETIME_PERMANENT; + return; + } + + /* The valies are hacked and absolute expiry times. They must + * be positive and preferred<=valid. */ + g_assert (a_preferred <= a_valid && + a_valid > 0 && + a_preferred > 0); + + /* The correct timestamp value would probably be rtnl_addr_get_last_update_time() + * (after converting into the proper time scale). + * But this is relatively complicated to convert and we don't actually need it. + * Especially, because rtnl_addr_get_last_update_time() might be negative in + * nm_utils_get_monotonic_timestamp_s() scale -- which we want to avoid. + * + * Remember: timestamp has no meaning, beyond anchoring the relative lifetimes + * at some point in time. We choose this point to be "1 second". + */ + address->timestamp = 1; + + /* account for the timestamp==1 by incrementing valid/preferred -- unless + * it is NM_PLATFORM_LIFETIME_PERMANENT or already NM_PLATFORM_LIFETIME_PERMANENT-1 + * (i.e. the largest non-permanent lifetime). */ + if (a_valid < NM_PLATFORM_LIFETIME_PERMANENT - 1) + a_valid += 1; + if (a_preferred < NM_PLATFORM_LIFETIME_PERMANENT - 1) + a_preferred += 1; + address->lifetime = a_valid; + address->preferred = a_preferred + 1; +} + static gboolean init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) { @@ -993,11 +1090,10 @@ init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) memset (address, 0, sizeof (*address)); + address->source = NM_PLATFORM_SOURCE_KERNEL; address->ifindex = rtnl_addr_get_ifindex (rtnladdr); address->plen = rtnl_addr_get_prefixlen (rtnladdr); - address->timestamp = nm_utils_get_monotonic_timestamp_s (); - address->lifetime = rtnl_addr_get_valid_lifetime (rtnladdr); - address->preferred = rtnl_addr_get_preferred_lifetime (rtnladdr); + _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) { g_return_val_if_reached (FALSE); return FALSE; @@ -1026,11 +1122,10 @@ init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) memset (address, 0, sizeof (*address)); + address->source = NM_PLATFORM_SOURCE_KERNEL; address->ifindex = rtnl_addr_get_ifindex (rtnladdr); address->plen = rtnl_addr_get_prefixlen (rtnladdr); - address->timestamp = nm_utils_get_monotonic_timestamp_s (); - address->lifetime = rtnl_addr_get_valid_lifetime (rtnladdr); - address->preferred = rtnl_addr_get_preferred_lifetime (rtnladdr); + _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); address->flags = rtnl_addr_get_flags (rtnladdr); if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) { g_return_val_if_reached (FALSE); @@ -1610,6 +1705,22 @@ ref_object (struct nl_object *obj, void *data) *out = obj; } +static gboolean +_rtnl_addr_timestamps_equal_fuzzy (guint32 ts1, guint32 ts2) +{ + guint32 diff; + + if (ts1 == ts2) + return TRUE; + if (ts1 == NM_PLATFORM_LIFETIME_PERMANENT || + ts2 == NM_PLATFORM_LIFETIME_PERMANENT) + return FALSE; + + /** accept the timestamps as equal if they are within two seconds. */ + diff = ts1 > ts2 ? ts1 - ts2 : ts2 - ts1; + return diff <= 2; +} + /* This function does all the magic to avoid race conditions caused * by concurrent usage of synchronous commands and an asynchronous cache. This * might be a nice future addition to libnl but it requires to do all operations @@ -1627,6 +1738,7 @@ event_notification (struct nl_msg *msg, gpointer user_data) auto_nl_object struct nl_object *kernel_object = NULL; int event; int nle; + ObjectType type; event = nlmsg_hdr (msg)->nlmsg_type; @@ -1640,8 +1752,10 @@ event_notification (struct nl_msg *msg, gpointer user_data) nl_msg_parse (msg, ref_object, &object); g_return_val_if_fail (object, NL_OK); + type = object_type_from_nl_object (object); + if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - if (object_type_from_nl_object (object) == OBJECT_TYPE_LINK) { + if (type == OBJECT_TYPE_LINK) { const char *name = rtnl_link_get_name ((struct rtnl_link *) object); debug ("netlink event (type %d) for link: %s (%d, family %d)", @@ -1652,7 +1766,7 @@ event_notification (struct nl_msg *msg, gpointer user_data) debug ("netlink event (type %d)", event); } - cache = choose_cache (platform, object); + cache = choose_cache_by_type (platform, type); cached_object = nm_nl_cache_search (cache, object); kernel_object = get_kernel_object (priv->nlh, object); @@ -1712,8 +1826,24 @@ event_notification (struct nl_msg *msg, gpointer user_data) * This also catches notifications for internal addition or change, unless * another action occured very soon after it. */ - if (!nl_object_diff (kernel_object, cached_object)) - return NL_OK; + if (!nl_object_diff (kernel_object, cached_object)) { + if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) { + struct rtnl_addr *c = (struct rtnl_addr *) cached_object; + struct rtnl_addr *k = (struct rtnl_addr *) kernel_object; + + /* libnl nl_object_diff() ignores differences in timestamp. Let's care about + * them (if they are large enough). + * + * Note that these valid and preferred timestamps are absolute, after + * _rtnl_addr_hack_lifetimes_rel_to_abs(). */ + if ( _rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_preferred_lifetime (c), + rtnl_addr_get_preferred_lifetime (k)) + && _rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_valid_lifetime (c), + rtnl_addr_get_valid_lifetime (k))) + return NL_OK; + } else + return NL_OK; + } /* Handle external change */ nl_cache_remove (cached_object); nle = nl_cache_add (cache, kernel_object); @@ -3086,10 +3216,8 @@ ip4_address_get_all (NMPlatform *platform, int ifindex) for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { if (_address_match ((struct rtnl_addr *) object, AF_INET, ifindex)) { - if (init_ip4_address (&address, (struct rtnl_addr *) object)) { - address.source = NM_PLATFORM_SOURCE_KERNEL; + if (init_ip4_address (&address, (struct rtnl_addr *) object)) g_array_append_val (addresses, address); - } } } @@ -3108,10 +3236,8 @@ ip6_address_get_all (NMPlatform *platform, int ifindex) for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { if (_address_match ((struct rtnl_addr *) object, AF_INET6, ifindex)) { - if (init_ip6_address (&address, (struct rtnl_addr *) object)) { - address.source = NM_PLATFORM_SOURCE_KERNEL; + if (init_ip6_address (&address, (struct rtnl_addr *) object)) g_array_append_val (addresses, address); - } } } @@ -3180,6 +3306,12 @@ build_rtnl_addr (int family, rtnl_addr_set_prefixlen (rtnladdr, plen); if (lifetime) { + /* note that here we set the relative timestamps (ticking from *now*). + * Contrary to the rtnl_addr objects from our cache, which have absolute + * timestamps (see _rtnl_addr_hack_lifetimes_rel_to_abs()). + * + * This is correct, because we only use build_rtnl_addr() for + * add_object(), delete_object() and cache search (ip_address_exists). */ rtnl_addr_set_valid_lifetime (rtnladdr, lifetime); rtnl_addr_set_preferred_lifetime (rtnladdr, preferred); } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 463822c7d9..2d8b453e71 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1425,6 +1425,7 @@ nm_platform_ip4_address_add (int ifindex, g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (lifetime > 0, FALSE); + g_return_val_if_fail (preferred <= lifetime, FALSE); g_return_val_if_fail (klass->ip4_address_add, FALSE); g_return_val_if_fail (!label || strlen (label) < sizeof (((NMPlatformIP4Address *) NULL)->label), FALSE); @@ -1459,6 +1460,7 @@ nm_platform_ip6_address_add (int ifindex, g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (lifetime > 0, FALSE); + g_return_val_if_fail (preferred <= lifetime, FALSE); g_return_val_if_fail (klass->ip6_address_add, FALSE); if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { @@ -1557,14 +1559,53 @@ array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address return FALSE; } -/* Compute (a - b) in an overflow-safe manner. */ +/** + * Takes a pair @timestamp and @duration, and returns the remaining duration based + * on the new timestamp @now. + */ static guint32 -subtract_guint32 (guint32 a, guint32 b) +_rebase_relative_time_on_now (guint32 timestamp, guint32 duration, guint32 now) { - if (a == G_MAXUINT32) - return G_MAXUINT32; + gint64 t; - return a > b ? a - b : 0; + if (duration == NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT; + + /* For timestamp>now, just accept it and calculate the expected(?) result. */ + t = (gint64) timestamp + (gint64) duration - (gint64) now; + + /* Pad the timestamp by 5 seconds to avoid potential races. */ + t += 5; + + if (t <= 0) + return 0; + if (t >= NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT - 1; + return t; +} + +static gboolean +_address_get_lifetime (const NMPlatformIPAddress *address, guint32 now, guint32 *out_lifetime, guint32 *out_preferred) +{ + gint32 lifetime, preferred; + + if (address->lifetime == 0) { + *out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + *out_preferred = NM_PLATFORM_LIFETIME_PERMANENT; + } else { + lifetime = _rebase_relative_time_on_now (address->timestamp, address->lifetime, now); + if (!lifetime) + return FALSE; + preferred = _rebase_relative_time_on_now (address->timestamp, address->preferred, now); + if (preferred > lifetime) { + g_warn_if_reached (); + preferred = lifetime; + } + + *out_lifetime = lifetime; + *out_preferred = preferred; + } + return TRUE; } /** @@ -1604,14 +1645,8 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses) const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i); guint32 lifetime, preferred; - if (known_address->lifetime) { - /* Pad the timestamp by 5 seconds to avoid potential races. */ - guint32 shift = subtract_guint32 (now, known_address->timestamp + 5); - - lifetime = subtract_guint32 (known_address->lifetime, shift); - preferred = subtract_guint32 (known_address->lifetime, shift); - } else - lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT; + if (!_address_get_lifetime ((NMPlatformIPAddress *) known_address, now, &lifetime, &preferred)) + continue; if (!nm_platform_ip4_address_add (ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label)) return FALSE; @@ -1661,14 +1696,8 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses) const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i); guint32 lifetime, preferred; - if (known_address->lifetime) { - /* Pad the timestamp by 5 seconds to avoid potential races. */ - guint32 shift = subtract_guint32 (now, known_address->timestamp + 5); - - lifetime = subtract_guint32 (known_address->lifetime, shift); - preferred = subtract_guint32 (known_address->lifetime, shift); - } else - lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT; + if (!_address_get_lifetime ((NMPlatformIPAddress *) known_address, now, &lifetime, &preferred)) + continue; if (!nm_platform_ip6_address_add (ifindex, known_address->address, known_address->peer_address, known_address->plen, @@ -2043,6 +2072,32 @@ _to_string_dev (int ifindex, char *buf, size_t size) buf[0] = 0; } +static const char * +_lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size) +{ + if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT) + return "forever"; + + g_snprintf (buf, buf_size, "%dsec", + _rebase_relative_time_on_now (timestamp, lifetime, now)); + return buf; +} + + +static const char * +_lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, guint32 lifetime, char *buf, size_t buf_size) +{ + if (timestamp == 0) { + if (preferred == NM_PLATFORM_LIFETIME_PERMANENT && lifetime == NM_PLATFORM_LIFETIME_PERMANENT) + return ""; + if (preferred == 0 && lifetime == 0) + return " lifetime unset"; + } + g_snprintf (buf, buf_size, " lifetime %d-%u[%u,%u]", + (signed) now, (unsigned) timestamp, (unsigned) preferred, (unsigned) lifetime); + return buf; +} + static char to_string_buffer[256]; const char * @@ -2110,7 +2165,10 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) char s_peer[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; char str_label[32]; + char str_lft[30], str_pref[30], str_time[50]; char *str_peer = NULL; + const char *str_lft_p, *str_pref_p, *str_time_p; + gint32 now = nm_utils_get_monotonic_timestamp_s (); g_return_val_if_fail (address, "(unknown)"); @@ -2128,9 +2186,18 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) else str_label[0] = 0; - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %u pref %u time %u%s%s%s src %s", - s_address, address->plen, (guint)address->lifetime, (guint)address->preferred, - (guint)address->timestamp, + str_lft_p = _lifetime_to_string (address->timestamp, + address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT, + now, str_lft, sizeof (str_lft)), + str_pref_p = (address->lifetime == address->preferred) + ? str_lft_p + : ( _lifetime_to_string (address->timestamp, + address->lifetime ? MIN (address->preferred, address->lifetime) : NM_PLATFORM_LIFETIME_PERMANENT, + now, str_pref, sizeof (str_pref)) ); + str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time)); + + g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + s_address, address->plen, str_lft_p, str_pref_p, str_time_p, str_peer ? str_peer : "", str_dev, str_label, @@ -2181,9 +2248,12 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) char s_flags[256]; char s_address[INET6_ADDRSTRLEN]; char s_peer[INET6_ADDRSTRLEN]; - char *str_flags; + char str_lft[30], str_pref[30], str_time[50]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char *str_flags; char *str_peer = NULL; + const char *str_lft_p, *str_pref_p, *str_time_p; + gint32 now = nm_utils_get_monotonic_timestamp_s (); g_return_val_if_fail (address, "(unknown)"); @@ -2200,9 +2270,18 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) str_flags = s_flags[0] ? g_strconcat (" flags ", s_flags, NULL) : NULL; - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %u pref %u time %u%s%s%s src %s", - s_address, address->plen, (guint)address->lifetime, (guint)address->preferred, - (guint)address->timestamp, + str_lft_p = _lifetime_to_string (address->timestamp, + address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT, + now, str_lft, sizeof (str_lft)), + str_pref_p = (address->lifetime == address->preferred) + ? str_lft_p + : ( _lifetime_to_string (address->timestamp, + address->lifetime ? MIN (address->preferred, address->lifetime) : NM_PLATFORM_LIFETIME_PERMANENT, + now, str_pref, sizeof (str_pref)) ); + str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time)); + + g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + s_address, address->plen, str_lft_p, str_pref_p, str_time_p, str_peer ? str_peer : "", str_dev, str_flags ? str_flags : "", diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index cde551b258..f7b14b7b9b 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -165,9 +165,16 @@ typedef struct { #define __NMPlatformIPAddress_COMMON \ __NMPlatformObject_COMMON; \ NMPlatformSource source; \ - guint32 timestamp; /* nm_utils_get_monotonic_timestamp_s() */ \ - guint32 lifetime; /* seconds */ \ - guint32 preferred; /* seconds */ \ + \ + /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*(). + * This value is mainly used to anchor the relative lifetime and preferred values. + * For addresses originating from DHCP it might be set to nm_utils_get_monotonic_timestamp_s() + * of when the lease was received. For addresses from platform/kernel it is set to 1. + * For permanent addresses it is mostly set to 0. + */ \ + guint32 timestamp; \ + guint32 lifetime; /* seconds since timestamp */ \ + guint32 preferred; /* seconds since timestamp */ \ int plen; \ ; diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index f5efa1ec7f..cf0267d1c6 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -547,6 +547,8 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) address.timestamp = now; address.lifetime = ndp_msg_opt_prefix_valid_time (msg, offset); address.preferred = ndp_msg_opt_prefix_preferred_time (msg, offset); + if (address.preferred > address.lifetime) + address.preferred = address.lifetime; fill_address_from_mac (&address.address, lladdr);