diff options
author | Thomas Haller <thaller@redhat.com> | 2014-06-19 23:20:43 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2014-06-19 23:20:43 +0200 |
commit | 7475d8c28d43bc85231f89d0537fafe6f8c3dcaf (patch) | |
tree | b1c28cdaaee3e7829547d2a4e32f399b37bbbb0b | |
parent | ca85ecd145c9035659a0e432fb0f5e1abd2c3f7b (diff) | |
parent | 6f013183e8265c8498d52d223c6ee1ce3cdf07d5 (diff) | |
download | NetworkManager-7475d8c28d43bc85231f89d0537fafe6f8c3dcaf.tar.gz |
platform: merge branch 'th/bgo727382_platform_fix_addr_lifetime' (part 2)
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
-rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient-utils.c | 6 | ||||
-rw-r--r-- | src/platform/nm-linux-platform.c | 124 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 45 | ||||
-rw-r--r-- | src/platform/nm-platform.h | 20 | ||||
-rw-r--r-- | src/platform/tests/dump.c | 7 |
5 files changed, 157 insertions, 45 deletions
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c index 3e7173aec3..8527e6c0fb 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c @@ -27,6 +27,7 @@ #include "nm-dhcp-dhclient-utils.h" #include "nm-ip4-config.h" #include "nm-utils.h" +#include "NetworkManagerUtils.h" #define CLIENTID_TAG "send dhcp-client-identifier" #define CLIENTID_FORMAT CLIENTID_TAG " \"%s\"; # added by NetworkManager" @@ -530,6 +531,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, GSList *parsed = NULL, *iter, *leases = NULL; char **line, **split = NULL; GHashTable *hash = NULL; + gint32 now_monotonic_ts; g_return_val_if_fail (contents != NULL, NULL); @@ -570,6 +572,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, g_date_time_ref (now); else now = g_date_time_new_now_utc (); + now_monotonic_ts = nm_utils_get_monotonic_timestamp_s (); for (iter = parsed; iter; iter = g_slist_next (iter)) { NMIP4Config *ip4; @@ -593,7 +596,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, continue; /* scale expiry to seconds (and CLAMP into the range of guint32) */ - expiry = CLAMP (expiry / G_TIME_SPAN_SECOND, 0, G_MAXUINT32-1); + expiry = CLAMP (expiry / G_TIME_SPAN_SECOND, 0, NM_PLATFORM_LIFETIME_PERMANENT-1); if (expiry <= 0) { /* the address is already expired. Don't even add it. */ continue; @@ -624,6 +627,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, if (!address.plen) address.plen = nm_utils_ip4_get_default_prefix (address.address); + address.timestamp = now_monotonic_ts; address.lifetime = address.preferred = expiry; address.source = NM_PLATFORM_SOURCE_DHCP; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 09733a45a8..f5da671380 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1032,6 +1032,92 @@ hack_empty_master_iff_lower_up (NMPlatform *platform, struct nl_object *object) rtnl_link_unset_flags (rtnllink, IFF_LOWER_UP); } +static guint32 +_get_remaining_time (guint32 start_timestamp, guint32 end_timestamp) +{ + /* Return the remaining time between @start_timestamp until @end_timestamp. + * + * If @end_timestamp is NM_PLATFORM_LIFETIME_PERMANENT, it returns + * NM_PLATFORM_LIFETIME_PERMANENT. If @start_timestamp already passed + * @end_timestamp it returns 0. Beware, NMPlatformIPAddress treats a @lifetime + * of 0 as permanent. + */ + if (end_timestamp == NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT; + if (start_timestamp >= end_timestamp) + return 0; + return end_timestamp - start_timestamp; +} + +/* _timestamp_nl_to_ms: + * @timestamp_nl: a timestamp from ifa_cacheinfo. + * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current + * uptime and how often timestamp_nl wrapped. + * + * Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds. + * The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts + * with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However, + * the uint32 counter wraps every 497 days of uptime, so we have to compensate + * for that. */ +static gint64 +_timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) +{ + const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100); + gint64 timestamp_nl_ms; + + /* convert timestamp from 1/100th of a second to msec. */ + timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100); + + /* timestamp wraps every 497 days. Try to compensate for that.*/ + if (timestamp_nl_ms > monotonic_ms) { + /* timestamp_nl_ms is in the future. Truncate it to *now* */ + timestamp_nl_ms = monotonic_ms; + } else if (monotonic_ms >= WRAP_INTERVAL) { + timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL; + if (timestamp_nl_ms > monotonic_ms) + timestamp_nl_ms -= WRAP_INTERVAL; + } + + return timestamp_nl_ms; +} + +static guint32 +_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr) +{ + guint32 last_update_time = rtnl_addr_get_last_update_time ((struct rtnl_addr *) rtnladdr); + struct timespec tp; + gint64 now_nl, now_nm, result; + + /* timestamp is unset. Default to 1. */ + if (!last_update_time) + return 1; + + /* do all the calculations in milliseconds scale */ + + clock_gettime (CLOCK_MONOTONIC, &tp); + now_nm = nm_utils_get_monotonic_timestamp_ms (); + now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) + + (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); + + result = now_nm - (now_nl - _timestamp_nl_to_ms (last_update_time, now_nl)); + + /* converting the last_update_time into nm_utils_get_monotonic_timestamp_ms() scale is + * a good guess but fails in the following situations: + * + * - If the address existed before start of the process, the timestamp in nm scale would + * be negative or zero. In this case we default to 1. + * - during hibernation, the CLOCK_MONOTONIC/last_update_time drifts from + * nm_utils_get_monotonic_timestamp_ms() scale. + */ + if (result <= 1000) + return 1; + + if (result > now_nm) + return now_nm / 1000; + + return result / 1000; +} + static void _init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr *rtnladdr) { @@ -1057,26 +1143,26 @@ _init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr 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". + if (a_valid <= 1) { + /* Since we want to have positive @timestamp and @valid != 0, + * we must handle this case special. */ + address->timestamp = 1; + address->lifetime = 1; /* Extend the lifetime by one second */ + address->preferred = 0; /* no longer preferred. */ + return; + } + + /* _rtnl_addr_last_update_time_to_nm() might be wrong, so don't rely on + * timestamp to have any meaning beyond anchoring the relative durations + * @lifetime and @preferred. */ - 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; + address->timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr); + + /* We would expect @timestamp to be less then @a_valid. Just to be sure, + * fix it up. */ + address->timestamp = MIN (address->timestamp, a_valid - 1); + address->lifetime = _get_remaining_time (address->timestamp, a_valid); + address->preferred = _get_remaining_time (address->timestamp, a_preferred); } static gboolean diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 71c60ca5bf..542b0faab2 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1436,6 +1436,7 @@ nm_platform_ip4_address_add (int ifindex, addr.address = address; addr.peer_address = peer_address; addr.plen = plen; + addr.timestamp = 0; /* set it at zero, which to_string will treat as *now* */ addr.lifetime = lifetime; addr.preferred = preferred; if (label) @@ -1470,6 +1471,7 @@ nm_platform_ip6_address_add (int ifindex, addr.address = address; addr.peer_address = peer_address; addr.plen = plen; + addr.timestamp = 0; /* set it to zero, which to_string will treat as *now* */ addr.lifetime = lifetime; addr.preferred = preferred; addr.flags = flags; @@ -1571,6 +1573,17 @@ _rebase_relative_time_on_now (guint32 timestamp, guint32 duration, guint32 now, if (duration == NM_PLATFORM_LIFETIME_PERMANENT) return NM_PLATFORM_LIFETIME_PERMANENT; + if (timestamp == 0) { + /* if the @timestamp is zero, assume it was just left unset and that the relative + * @duration starts counting from @now. This is convenient to construct an address + * and print it in nm_platform_ip4_address_to_string(). + * + * In general it does not make sense to set the @duration without anchoring at + * @timestamp because you don't know the absolute expiration time when looking + * at the address at a later moment. */ + timestamp = now; + } + /* For timestamp > now, just accept it and calculate the expected(?) result. */ t = (gint64) timestamp + (gint64) duration - (gint64) now; @@ -1597,13 +1610,18 @@ _address_get_lifetime (const NMPlatformIPAddress *address, guint32 now, guint32 if (!lifetime) return FALSE; preferred = _rebase_relative_time_on_now (address->timestamp, address->preferred, now, padding); - if (preferred > lifetime) { - g_warn_if_reached (); - preferred = lifetime; - } *out_lifetime = lifetime; - *out_preferred = preferred; + *out_preferred = MIN (preferred, lifetime); + + /* Assert that non-permanent addresses have a (positive) @timestamp. _rebase_relative_time_on_now() + * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always + * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. + */ + g_return_val_if_fail ( address->timestamp != 0 + || ( address->lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && address->preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE); + g_return_val_if_fail (preferred <= lifetime, TRUE); } return TRUE; } @@ -2089,12 +2107,6 @@ _lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *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; @@ -2494,31 +2506,32 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route int nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) { - gint64 ta, tb; + gint64 ta = 0, tb = 0; _CMP_POINTER (a, b); if (a->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0) ta = G_MAXINT64; - else + else if (a->timestamp) ta = ((gint64) a->timestamp) + a->lifetime; if (b->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || b->lifetime == 0) tb = G_MAXINT64; - else + else if (b->timestamp) tb = ((gint64) b->timestamp) + b->lifetime; if (ta == tb) { /* if the lifetime is equal, compare the preferred time. */ + ta = tb = 0; if (a->preferred == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0 /* liftime==0 means permanent! */) ta = G_MAXINT64; - else + else if (a->timestamp) ta = ((gint64) a->timestamp) + a->preferred; if (b->preferred == NM_PLATFORM_LIFETIME_PERMANENT|| b->lifetime == 0) tb = G_MAXINT64; - else + else if (b->timestamp) tb = ((gint64) b->timestamp) + b->preferred; if (ta == tb) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index f7b14b7b9b..96b6cfabbd 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -167,10 +167,22 @@ typedef struct { NMPlatformSource source; \ \ /* 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. + * + * The rules are: + * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses + * are permanent. This rule is so that unset addresses (calloc) are permanent by default. + * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly + * set to 0). Such addresses are permanent. + * 3 Non permanent addreses should (almost) always have @timestamp > 0. 0 is not a valid timestamp + * and never returned by nm_utils_get_monotonic_timestamp_s(). In this case @valid/@preferred + * is anchored at @timestamp. + * 4 Non permanent addresses with @timestamp == 0 are implicitely anchored at *now*, thus the time + * moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add(). + * + * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the + * lease was received. Addresses from kernel might have the @timestamp based on the last modification + * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring + * @lifetime and @preferred. */ \ guint32 timestamp; \ guint32 lifetime; /* seconds since timestamp */ \ diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c index 9276d18439..e97ef138cb 100644 --- a/src/platform/tests/dump.c +++ b/src/platform/tests/dump.c @@ -13,7 +13,6 @@ dump_interface (NMPlatformLink *link) GArray *ip4_addresses; const NMPlatformIP6Address *ip6_address; const NMPlatformIP4Address *ip4_address; - char addrstr[INET6_ADDRSTRLEN]; GArray *ip6_routes; GArray *ip4_routes; const NMPlatformIP6Route *ip6_route; @@ -73,14 +72,12 @@ dump_interface (NMPlatformLink *link) for (i = 0; i < ip4_addresses->len; i++) { ip4_address = &g_array_index (ip4_addresses, NMPlatformIP4Address, i); - inet_ntop (AF_INET, &ip4_address->address, addrstr, sizeof (addrstr)); - printf (" ip4-address %s/%d lifetime %u preferred %u\n", addrstr, ip4_address->plen, ip4_address->lifetime, ip4_address->preferred); + printf (" ip4-address %s\n", nm_platform_ip4_address_to_string (ip4_address)); } for (i = 0; i < ip6_addresses->len; i++) { ip6_address = &g_array_index (ip6_addresses, NMPlatformIP6Address, i); - inet_ntop (AF_INET6, &ip6_address->address, addrstr, sizeof (addrstr)); - printf (" ip6-address %s/%d lifetime %u preferred %u\n", addrstr, ip6_address->plen, ip6_address->lifetime, ip6_address->preferred); + printf (" ip6-address %s\n", nm_platform_ip6_address_to_string (ip6_address)); } g_array_unref (ip4_addresses); |