From bc34ee777985cad61437903a814a5582a014ba7b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 17 Apr 2014 22:10:56 +0200 Subject: [PATCH 1/9] core: add code comment to nm_utils_get_monotonic_timestamp_*s() functions Signed-off-by: Thomas Haller --- src/NetworkManagerUtils.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index d8273362b5..3bf20625b2 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1058,6 +1058,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 +1085,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 +1112,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) From 8f8b247e34c44a679e2c94a63075b1cc3513b500 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 2 Apr 2014 13:31:20 +0200 Subject: [PATCH 2/9] core: add nm_utils_get_monotonic_timestamp_ns() function Signed-off-by: Thomas Haller --- src/NetworkManagerUtils.c | 27 +++++++++++++++++++++++++++ src/NetworkManagerUtils.h | 1 + 2 files changed, 28 insertions(+) diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 3bf20625b2..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: * 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); From 8310a039d81e3a316cf657aa9f28edabb9be125c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 2 Apr 2014 13:35:21 +0200 Subject: [PATCH 3/9] platform: fix preferred and valid lifetimes for addresses from netlink/kernel The kernel tells the address lifetimes in the 'struct ifa_cacheinfo' attribute. This contains two timestamps (cstamp and tstamp) and two relative lifetimes (ifa_prefered and ifa_valid). The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in 1/100th of a second (wrapping every 497 days). The preferred/valid times are re-adjusted everytime when sending the message and count down as the time goes by. In other words, they are anchored relatively to the moment of when kernel creates the netlink message. As platform is caching the rtnl_addr object, the information of *when* the lifetimes started counting is not available. This patch fixes reading these values by hacking the libnl object when it gets received, so that valid and preferred are instead absolute expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() -- which NM internally is used for address timestamps. There are two minor downsides to this hack: - the valid and preferred properties of a cached rtnl_addr object have an unexpected meaning, i.e. they are absolute and in a different time scale. - later when converting rtnl_addr to NMPlatformIPAddress, the base timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge of when the address was created or last modified. The timestamp property of NMPlatformIPAddress is solely there to anchor the relative timestamps lifetime and preferred. Do not use it for anything else. Another reason the timestamp property is meaningless is that its scale nm_utils_get_monotonic_timestamp_s() starts counting at process start. So addresses that existed before would have a negative or zero timestamp, which we avoid. This in turn could be solved by either allowing negative timestamps or by shifting nm_utils_get_monotonic_timestamp_*(). Both is viable, but not necessary (ATM), because the age of an address has no other apparent use then to anchor the relative timestamps. Another implication is, that we potentially could get rid of the timestamp completely, and insteat make preferred and lifetime be absolute expiries. This will be fixed properly later, by not caching libnl objects but instead native NMPlatform objects. For those we have full control over their properties. https://bugzilla.gnome.org/show_bug.cgi?id=727382 Signed-off-by: Thomas Haller --- src/platform/nm-linux-platform.c | 111 +++++++++++++++++++++++++++++-- src/platform/nm-platform.h | 13 +++- 2 files changed, 115 insertions(+), 9 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index f5911c8b70..1d40152083 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) { @@ -995,9 +1092,7 @@ init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) 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; @@ -1028,9 +1123,7 @@ init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) 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); @@ -3180,6 +3273,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.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; \ ; From 84cfd06d6a7df892615f107190848c078e0e6705 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 1 Apr 2014 22:46:46 +0200 Subject: [PATCH 4/9] core/platform: limit the preferred time to address lifetime Related: https://bugzilla.redhat.com/show_bug.cgi?id=1082041 Signed-off-by: Thomas Haller --- src/devices/nm-device.c | 2 ++ src/platform/nm-platform.c | 6 ++++++ src/rdisc/nm-lndp-rdisc.c | 2 ++ 3 files changed, 10 insertions(+) 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-platform.c b/src/platform/nm-platform.c index 463822c7d9..e43f3033f2 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)) { @@ -1610,6 +1612,8 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses) lifetime = subtract_guint32 (known_address->lifetime, shift); preferred = subtract_guint32 (known_address->lifetime, shift); + + g_warn_if_fail (known_address->preferred <= known_address->lifetime); } else lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT; @@ -1667,6 +1671,8 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses) lifetime = subtract_guint32 (known_address->lifetime, shift); preferred = subtract_guint32 (known_address->lifetime, shift); + + g_warn_if_fail (known_address->preferred <= known_address->lifetime); } else lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT; 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); From d90b9ff2c855bf4350070957a72b267f7edb7898 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 31 Mar 2014 11:19:45 +0200 Subject: [PATCH 5/9] platform: fix setting preferred time for address Before nm_platform_ip4_address_sync() set the preferred time to the same value as the address lifetime. The result was that the preferred time was always identical to valid lifetime. This will lead to the kernel using the address longer then the desired preferred time (until validity of the address expires). https://bugzilla.redhat.com/show_bug.cgi?id=1082041 https://bugzilla.redhat.com/show_bug.cgi?id=1083283 Reported-by: Kai Engert Signed-off-by: Thomas Haller --- src/platform/nm-platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index e43f3033f2..3993d59341 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1611,7 +1611,7 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses) guint32 shift = subtract_guint32 (now, known_address->timestamp + 5); lifetime = subtract_guint32 (known_address->lifetime, shift); - preferred = subtract_guint32 (known_address->lifetime, shift); + preferred = subtract_guint32 (known_address->preferred, shift); g_warn_if_fail (known_address->preferred <= known_address->lifetime); } else @@ -1670,7 +1670,7 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses) guint32 shift = subtract_guint32 (now, known_address->timestamp + 5); lifetime = subtract_guint32 (known_address->lifetime, shift); - preferred = subtract_guint32 (known_address->lifetime, shift); + preferred = subtract_guint32 (known_address->preferred, shift); g_warn_if_fail (known_address->preferred <= known_address->lifetime); } else From e1410b5a88cbfc33d79ba43cef317d6990a7e7d1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 31 Mar 2014 11:19:45 +0200 Subject: [PATCH 6/9] platform: refactor calculating valid/preferred times when adding address Replace the calls to subtract_guint32() by _rebase_relative_time_on_now() and _address_get_lifetime(). Signed-off-by: Thomas Haller --- src/platform/nm-platform.c | 73 +++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 3993d59341..d8b6b76579 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1559,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; } /** @@ -1606,16 +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->preferred, shift); - - g_warn_if_fail (known_address->preferred <= known_address->lifetime); - } 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; @@ -1665,16 +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->preferred, shift); - - g_warn_if_fail (known_address->preferred <= known_address->lifetime); - } 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, From 2ff046e5aac6e7851d2cb2712094086341fc8b63 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 2 Apr 2014 14:31:12 +0200 Subject: [PATCH 7/9] platform: change address_to_string functions to show remaining lifetime/preferred times Change the to_string functions to convert the lifetime/preferred values to the time remaining when the function is evaluated. These functions are used for printing/debugging, so it's more sensible to show the remaining time. On the other hand, for debugging, it's better to see the raw values (also). In addition to the remaining time we keep to print the timestamps+now if the address is not permanent. So when inspecting the logs it is possible to figure out the real values. Signed-off-by: Thomas Haller --- src/platform/nm-platform.c | 64 +++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index d8b6b76579..2d8b453e71 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2072,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 * @@ -2139,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)"); @@ -2157,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, @@ -2210,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)"); @@ -2229,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 : "", From 441f337412abf3bdf515c3eed04eaccc0d32e949 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 3 Apr 2014 10:09:47 +0200 Subject: [PATCH 8/9] platform: refactor setting the source of platform addresses to NM_PLATFORM_SOURCE_KERNEL Moving setting the source of the address to the init_* functions. This also has the advantage, that the platform internal to_string functions have the proper source set. Signed-off-by: Thomas Haller --- src/platform/nm-linux-platform.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 1d40152083..d43774542a 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1090,6 +1090,7 @@ 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); _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); @@ -1121,6 +1122,7 @@ 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); _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); @@ -3179,10 +3181,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); - } } } @@ -3201,10 +3201,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); - } } } From 58b318b53a30fbc38ad2fc1b447af760d311e7e5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 2 Apr 2014 16:54:08 +0200 Subject: [PATCH 9/9] platform: raise address changed signals for lifetime update When only the lifetime of an address changes, we did not get a platform signal as libnl does not consider the time fields in nl_object_diff(). Workaround by comparing the timestamps manually. Signed-off-by: Thomas Haller --- src/platform/nm-linux-platform.c | 43 +++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index d43774542a..6fd6ee5489 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1705,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 @@ -1722,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; @@ -1735,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)", @@ -1747,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); @@ -1807,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);