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>
This commit is contained in:
Thomas Haller 2014-06-19 23:20:43 +02:00
commit 7475d8c28d
5 changed files with 157 additions and 45 deletions

View file

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

View file

@ -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".
*/
address->timestamp = 1;
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;
}
/* 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;
/* _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 = _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

View file

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

View file

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

View file

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