about summary refs log tree commit diff
path: root/shared/nm-utils
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2019-01-25 11:24:41 +0100
committerSebastien Bacher <seb128@ubuntu.com>2019-01-25 11:24:41 +0100
commitbbae86d3d2997a853ca0365e8eb7a3ca7489ee09 (patch)
tree1f7fa49b47ab13aea3effbe839559d221f6323b4 /shared/nm-utils
parent404ebe62622150e77e311777dff8617eb974e834 (diff)
New upstream version 1.15.2
Diffstat (limited to 'shared/nm-utils')
-rw-r--r--shared/nm-utils/nm-dedup-multi.c6
-rw-r--r--shared/nm-utils/nm-dedup-multi.h8
-rw-r--r--shared/nm-utils/nm-errno.c65
-rw-r--r--shared/nm-utils/nm-errno.h135
-rw-r--r--shared/nm-utils/nm-glib.h24
-rw-r--r--shared/nm-utils/nm-hash-utils.c57
-rw-r--r--shared/nm-utils/nm-hash-utils.h62
-rw-r--r--shared/nm-utils/nm-io-utils.c6
-rw-r--r--shared/nm-utils/nm-logging-fwd.h111
-rw-r--r--shared/nm-utils/nm-macros-internal.h119
-rw-r--r--shared/nm-utils/nm-random-utils.c2
-rw-r--r--shared/nm-utils/nm-secret-utils.h2
-rw-r--r--shared/nm-utils/nm-shared-utils.c356
-rw-r--r--shared/nm-utils/nm-shared-utils.h270
-rw-r--r--shared/nm-utils/nm-test-utils.h175
-rw-r--r--shared/nm-utils/nm-time-utils.c273
-rw-r--r--shared/nm-utils/nm-time-utils.h45
-rw-r--r--shared/nm-utils/nm-udev-utils.c31
-rw-r--r--shared/nm-utils/nm-vpn-plugin-utils.c46
-rw-r--r--shared/nm-utils/tests/test-shared-general.c221
-rw-r--r--shared/nm-utils/unaligned.h24
21 files changed, 1833 insertions, 205 deletions
diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c
index fc134e25..852c207c 100644
--- a/shared/nm-utils/nm-dedup-multi.c
+++ b/shared/nm-utils/nm-dedup-multi.c
@@ -159,7 +159,7 @@ _entry_unpack (const NMDedupMultiEntry *entry,
 	ASSERT_idx_type (*out_idx_type);
 
 	/* for lookup of the head, we allow to omit object, but only
-	 * if the idx_type does not parition the objects. Otherwise, we
+	 * if the idx_type does not partition the objects. Otherwise, we
 	 * require a obj to compare. */
 	nm_assert (   !*out_lookup_head
 	           || (   *out_obj
@@ -441,10 +441,10 @@ nm_dedup_multi_index_add (NMDedupMultiIndex *self,
  *   object will be placed in. You can omit this, and it will be automatically
  *   detected (at the expense of an additional hash lookup).
  *   Basically, this is the result of nm_dedup_multi_index_lookup_obj(),
- *   with the pecularity that if you know that @obj is not yet tracked,
+ *   with the peculiarity that if you know that @obj is not yet tracked,
  *   you may specify %NM_DEDUP_MULTI_ENTRY_MISSING.
  * @head_existing: an optional argument to safe a lookup for the head. If specified,
- *   it must be identical to nm_dedup_multi_index_lookup_head(), with the pecularity
+ *   it must be identical to nm_dedup_multi_index_lookup_head(), with the peculiarity
  *   that if the head is not yet tracked, you may specify %NM_DEDUP_MULTI_HEAD_ENTRY_MISSING
  * @out_entry: if give, return the added entry. This entry may have already exists (update)
  *   or be newly created. If @obj is not partitionable according to @idx_type, @obj
diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-utils/nm-dedup-multi.h
index 8d482de9..845b4c3e 100644
--- a/shared/nm-utils/nm-dedup-multi.h
+++ b/shared/nm-utils/nm-dedup-multi.h
@@ -47,7 +47,7 @@ typedef enum _NMDedupMultiIdxMode {
 	NM_DEDUP_MULTI_IDX_MODE_APPEND,
 
 	/* like NM_DEDUP_MULTI_IDX_MODE_APPEND, but if the object
-	 * is already in teh cache, move it to the end. */
+	 * is already in the cache, move it to the end. */
 	NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE,
 } NMDedupMultiIdxMode;
 
@@ -85,7 +85,7 @@ static inline const NMDedupMultiObj *
 nm_dedup_multi_obj_ref (const NMDedupMultiObj *obj)
 {
 	/* ref and unref accept const pointers. Objects is supposed to be shared
-	 * and kept immutable. Disallowing to take/retrun a reference to a const
+	 * and kept immutable. Disallowing to take/return a reference to a const
 	 * NMPObject is cumbersome, because callers are precisely expected to
 	 * keep a ref on the otherwise immutable object. */
 
@@ -131,12 +131,12 @@ void nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self,
  * routes by ifindex. As the ifindex is dynamic, it does not create an
  * idx-type instance for each ifindex. Instead, it has one idx-type for
  * all routes. But whenever accessing NMDedupMultiIndex with an NMDedupMultiObj,
- * the partitioning NMDedupMultiIdxType takes into accound the NMDedupMultiObj
+ * the partitioning NMDedupMultiIdxType takes into account the NMDedupMultiObj
  * instance to associate it with the right list.
  *
  * Hence, a NMDedupMultiIdxEntry has a list of possibly multiple NMDedupMultiHeadEntry
  * instances, which each is the head for a list of NMDedupMultiEntry instances.
- * In the platform example, the NMDedupMultiHeadEntry parition the indexed objects
+ * In the platform example, the NMDedupMultiHeadEntry partition the indexed objects
  * by their ifindex. */
 struct _NMDedupMultiIdxType {
 	union {
diff --git a/shared/nm-utils/nm-errno.c b/shared/nm-utils/nm-errno.c
new file mode 100644
index 00000000..c87f0b48
--- /dev/null
+++ b/shared/nm-utils/nm-errno.c
@@ -0,0 +1,65 @@
+/* NetworkManager -- Network link manager
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-errno.h"
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int,
+	NM_UTILS_LOOKUP_DEFAULT (NULL),
+
+	NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC,          "NME_UNSPEC"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_BUG,             "NME_BUG"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO,    "NME_NATIVE_ERRNO"),
+
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE,     "NME_NL_ATTRSIZE"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK,     "NME_NL_BAD_SOCK"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR,    "NME_NL_DUMP_INTR"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC,    "NME_NL_MSG_TRUNC"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"),
+
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND,    "not-found"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS,       "exists"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE,   "wrong-type"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE,    "not-slave"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE,  "no-firmware"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP,    "not-supported"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK,      "netlink"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU, "cant-set-mtu"),
+);
+
+const char *
+nm_strerror (int nmerr)
+{
+	const char *s;
+
+	nmerr = nm_errno (nmerr);
+
+	if (nmerr >= _NM_ERRNO_RESERVED_FIRST) {
+		s = _geterror (nmerr);
+		if (s)
+			return s;
+	}
+	return g_strerror (nmerr);
+}
diff --git a/shared/nm-utils/nm-errno.h b/shared/nm-utils/nm-errno.h
new file mode 100644
index 00000000..c3008f1f
--- /dev/null
+++ b/shared/nm-utils/nm-errno.h
@@ -0,0 +1,135 @@
+/* NetworkManager -- Network link manager
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#ifndef __NM_ERRNO_H__
+#define __NM_ERRNO_H__
+
+#include <errno.h>
+
+/*****************************************************************************/
+
+enum {
+	_NM_ERRNO_MININT         = G_MININT,
+	_NM_ERRNO_MAXINT         = G_MAXINT,
+	_NM_ERRNO_RESERVED_FIRST = 100000,
+
+	/* an unspecified error. */
+	NME_UNSPEC = _NM_ERRNO_RESERVED_FIRST,
+
+	/* A bug, for example when an assertion failed.
+	 * Should never happen. */
+	NME_BUG,
+
+	/* a native error number (from <errno.h>) cannot be mapped as
+	 * an nm-error, because it is in the range [_NM_ERRNO_RESERVED_FIRST,
+	 * _NM_ERRNO_RESERVED_LAST]. */
+	NME_NATIVE_ERRNO,
+
+	/* netlink errors. */
+	NME_NL_SEQ_MISMATCH,
+	NME_NL_MSG_TRUNC,
+	NME_NL_MSG_TOOSHORT,
+	NME_NL_DUMP_INTR,
+	NME_NL_ATTRSIZE,
+	NME_NL_BAD_SOCK,
+	NME_NL_NOADDR,
+	NME_NL_MSG_OVERFLOW,
+
+	/* platform errors. */
+	NME_PL_NOT_FOUND,
+	NME_PL_EXISTS,
+	NME_PL_WRONG_TYPE,
+	NME_PL_NOT_SLAVE,
+	NME_PL_NO_FIRMWARE,
+	NME_PL_OPNOTSUPP,
+	NME_PL_NETLINK,
+	NME_PL_CANT_SET_MTU,
+
+	_NM_ERRNO_RESERVED_LAST_PLUS_1,
+	_NM_ERRNO_RESERVED_LAST = _NM_ERRNO_RESERVED_LAST_PLUS_1 - 1,
+};
+
+/*****************************************************************************/
+
+static inline int
+nm_errno_native (int errsv)
+{
+	/* several API returns negative errno values as errors. Normalize
+	 * negative values to positive values.
+	 *
+	 * As a special case, map G_MININT to G_MAXINT. If you care about the
+	 * distinction, then check for G_MININT before.
+	 *
+	 * Basically, this normalizes a plain errno to be non-negative. */
+	return errsv >= 0
+	       ? errsv
+	       : ((errsv == G_MININT) ? G_MAXINT : -errsv);
+}
+
+static inline int
+nm_errno (int nmerr)
+{
+	/* Normalizes an nm-error to be positive. Various API returns negative
+	 * error codes, and this function converts the negative value to its
+	 * positive.
+	 *
+	 * It's very similar to nm_errno_native(), but not exactly. The difference is that
+	 * nm_errno_native() is for plain errno, while nm_errno() is for nm-error numbers.
+	 * Yes, nm-error number are ~almost~ the same as errno, except that a particular
+	 * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) is reserved. The difference
+	 * between the two functions is only how G_MININT is mapped.
+	 *
+	 * See also nm_errno_from_native() below. */
+	return nmerr >= 0
+	       ? nmerr
+	       : ((nmerr == G_MININT) ? NME_BUG : -nmerr);
+}
+
+static inline int
+nm_errno_from_native (int errsv)
+{
+	/* this maps a native errno to a (always non-negative) nm-error number.
+	 *
+	 * Note that nm-error numbers are embedded into the range of regular
+	 * errno. The only difference is, that nm-error numbers reserve a
+	 * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their
+	 * own purpose.
+	 *
+	 * That means, converting an errno to nm-error number means in
+	 * most cases just returning itself (negative values are normalized
+	 * to be positive). Only values G_MININT and [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST]
+	 * are coerced to the special value NME_NATIVE_ERRNO, as they cannot
+	 * otherwise be represented in nm-error number domain. */
+	if (errsv < 0) {
+		return   G_UNLIKELY (errsv == G_MININT)
+		       ? NME_NATIVE_ERRNO
+		       : -errsv;
+	}
+	return   G_UNLIKELY (   errsv >= _NM_ERRNO_RESERVED_FIRST
+	                     && errsv <= _NM_ERRNO_RESERVED_LAST)
+	       ? NME_NATIVE_ERRNO
+	       : errsv;
+}
+
+const char *nm_strerror (int nmerr);
+
+/*****************************************************************************/
+
+#endif /* __NM_ERRNO_H__ */
diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h
index 770cf0fe..b7534edd 100644
--- a/shared/nm-utils/nm-glib.h
+++ b/shared/nm-utils/nm-glib.h
@@ -538,4 +538,28 @@ _nm_g_variant_new_printf (const char *format_string, ...)
 
 /*****************************************************************************/
 
+#if !GLIB_CHECK_VERSION (2, 47, 1)
+/* Older versions of g_value_unset() only allowed to unset a GValue which
+ * was initialized previously. This was relaxed ([1], [2], [3]).
+ *
+ * Our nm_auto_unset_gvalue macro requires to be able to call g_value_unset().
+ * Also, it is our general practice to allow for that. Add a compat implementation.
+ *
+ * [1] https://gitlab.gnome.org/GNOME/glib/commit/4b2d92a864f1505f1b08eb639d74293fa32681da
+ * [2] commit "Allow passing unset GValues to g_value_unset()"
+ * [3] https://bugzilla.gnome.org/show_bug.cgi?id=755766
+ */
+static inline void
+_nm_g_value_unset (GValue *value)
+{
+	g_return_if_fail (value);
+
+	if (value->g_type != 0)
+		g_value_unset (value);
+}
+#define g_value_unset _nm_g_value_unset
+#endif
+
+/*****************************************************************************/
+
 #endif  /* __NM_GLIB_H__ */
diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c
index 4bc12b7c..80387c71 100644
--- a/shared/nm-utils/nm-hash-utils.c
+++ b/shared/nm-utils/nm-hash-utils.c
@@ -40,53 +40,66 @@ static const guint8 *volatile global_seed = NULL;
 static const guint8 *
 _get_hash_key_init (void)
 {
+	static gsize g_lock;
 	/* the returned hash is aligned to guin64, hence, it is safe
 	 * to use it as guint* or guint64* pointer. */
 	static union {
 		guint8 v8[HASH_KEY_SIZE];
 	} g_arr _nm_alignas (guint64);
-	static gsize g_lock;
 	const guint8 *g;
-	CSipHash siph_state;
-	uint64_t h;
-	guint *p;
+	union {
+		guint8 v8[HASH_KEY_SIZE];
+		guint vuint;
+	} t_arr;
 
-	g = global_seed;
+again:
+	g = g_atomic_pointer_get (&global_seed);
 	if (G_LIKELY (g != NULL)) {
 		nm_assert (g == g_arr.v8);
 		return g;
 	}
 
-	if (g_once_init_enter (&g_lock)) {
+	{
+		CSipHash siph_state;
+		uint64_t h;
 
-		nm_utils_random_bytes (g_arr.v8, sizeof (g_arr.v8));
+		/* initialize a random key in t_arr. */
+
+		nm_utils_random_bytes (&t_arr, sizeof (t_arr));
 
 		/* use siphash() of the key-size, to mangle the first guint. Otherwise,
 		 * the first guint has only the entropy that nm_utils_random_bytes()
-		 * generated for the first 4 bytes and relies on a good random generator. */
-		c_siphash_init (&siph_state, g_arr.v8);
-		c_siphash_append (&siph_state, g_arr.v8, sizeof (g_arr.v8));
+		 * generated for the first 4 bytes and relies on a good random generator.
+		 *
+		 * The first int is especially intersting for nm_hash_static() below, and we
+		 * want to have it all the entropy of t_arr. */
+		c_siphash_init (&siph_state, t_arr.v8);
+		c_siphash_append (&siph_state, (const guint8 *) &t_arr, sizeof (t_arr));
 		h = c_siphash_finalize (&siph_state);
-		p = (guint *) g_arr.v8;
 		if (sizeof (guint) < sizeof (h))
-			*p = *p ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32));
+			t_arr.vuint = t_arr.vuint ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32));
 		else
-			*p = *p ^ ((guint) (h & 0xFFFFFFFFu));
+			t_arr.vuint = t_arr.vuint ^ ((guint) (h & 0xFFFFFFFFu));
+	}
 
-		g_atomic_pointer_compare_and_exchange (&global_seed, NULL, g_arr.v8);
-		g_once_init_leave (&g_lock, 1);
+	if (!g_once_init_enter (&g_lock)) {
+		/* lost a race. The random key is already initialized. */
+		goto again;
 	}
 
-	nm_assert (global_seed == g_arr.v8);
-	return g_arr.v8;
+	memcpy (g_arr.v8, t_arr.v8, HASH_KEY_SIZE);
+	g = g_arr.v8;
+	g_atomic_pointer_set (&global_seed, g);
+	g_once_init_leave (&g_lock, 1);
+	return g;
 }
 
 #define _get_hash_key() \
 	({ \
 		const guint8 *_g; \
 		\
-		_g = global_seed; \
-		if (G_UNLIKELY (_g == NULL)) \
+		_g = g_atomic_pointer_get (&global_seed); \
+		if (G_UNLIKELY (!_g)) \
 			_g = _get_hash_key_init (); \
 		_g; \
 	})
@@ -109,17 +122,17 @@ nm_hash_static (guint static_seed)
 }
 
 void
-nm_hash_init (NMHashState *state, guint static_seed)
+nm_hash_siphash42_init (CSipHash *h, guint static_seed)
 {
 	const guint8 *g;
 	guint seed[HASH_KEY_SIZE_GUINT];
 
-	nm_assert (state);
+	nm_assert (h);
 
 	g = _get_hash_key ();
 	memcpy (seed, g, HASH_KEY_SIZE);
 	seed[0] ^= static_seed;
-	c_siphash_init (&state->_state, (const guint8 *) seed);
+	c_siphash_init (h, (const guint8 *) seed);
 }
 
 guint
diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h
index b797fb75..cf71a7e9 100644
--- a/shared/nm-utils/nm-hash-utils.h
+++ b/shared/nm-utils/nm-hash-utils.h
@@ -25,6 +25,39 @@
 #include "c-siphash/src/c-siphash.h"
 #include "nm-macros-internal.h"
 
+/*****************************************************************************/
+
+void nm_hash_siphash42_init (CSipHash *h, guint static_seed);
+
+/* Siphash24 of binary buffer @arr and @len, using the randomized seed from
+ * other NMHash functions.
+ *
+ * Note, that this is guaranteed to use siphash42 under the hood (contrary to
+ * all other NMHash API, which leave this undefined). That matters at the point,
+ * where the caller needs to be sure that a reasonably strong hasing algorithm
+ * is used.  (Yes, NMHash is all about siphash24, but otherwise that is not promised
+ * anywhere).
+ *
+ * Another difference is, that this returns guint64 (not guint like other NMHash functions).
+ *
+ * Another difference is, that this may also return zero (not like nm_hash_complete()).
+ *
+ * Then, why not use c_siphash_hash() directly? Because this also uses the randomized,
+ * per-run hash-seed like nm_hash_init(). So, you get siphash24 with a random
+ * seed (which is cached for the current run of the program).
+ */
+static inline guint64
+nm_hash_siphash42 (guint static_seed, const void *ptr, gsize n)
+{
+	CSipHash h;
+
+	nm_hash_siphash42_init (&h, static_seed);
+	c_siphash_append (&h, ptr, n);
+	return c_siphash_finalize (&h);
+}
+
+/*****************************************************************************/
+
 struct _NMHashState {
 	CSipHash _state;
 };
@@ -33,16 +66,33 @@ typedef struct _NMHashState NMHashState;
 
 guint nm_hash_static (guint static_seed);
 
-void nm_hash_init (NMHashState *state, guint static_seed);
+static inline void
+nm_hash_init (NMHashState *state, guint static_seed)
+{
+	nm_assert (state);
+
+	nm_hash_siphash42_init (&state->_state, static_seed);
+}
+
+static inline guint64
+nm_hash_complete_u64 (NMHashState *state)
+{
+	nm_assert (state);
+
+	/* this returns the native u64 hash value. Note that this differs
+	 * from nm_hash_complete() in two ways:
+	 *
+	 * - the type, guint64 vs. guint.
+	 * - nm_hash_complete() never returns zero. */
+	return c_siphash_finalize (&state->_state);
+}
 
 static inline guint
 nm_hash_complete (NMHashState *state)
 {
 	guint64 h;
 
-	nm_assert (state);
-
-	h = c_siphash_finalize (&state->_state);
+	h = nm_hash_complete_u64 (state);
 
 	/* we don't ever want to return a zero hash.
 	 *
@@ -218,8 +268,8 @@ guint nm_str_hash (gconstpointer str);
 	({ \
 		NMHashState _h; \
 		\
-		nm_hash_init (&_h, static_seed); \
-		nm_hash_update_val (&_h, val); \
+		nm_hash_init (&_h, (static_seed)); \
+		nm_hash_update_val (&_h, (val)); \
 		nm_hash_complete (&_h); \
 	})
 
diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-utils/nm-io-utils.c
index 88cb13ff..06f756c4 100644
--- a/shared/nm-utils/nm-io-utils.c
+++ b/shared/nm-utils/nm-io-utils.c
@@ -110,7 +110,7 @@ _mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len)
  * A reimplementation of g_file_get_contents() with a few differences:
  *   - accepts an open fd, instead of a path name. This allows you to
  *     use openat().
- *   - limits the maxium filesize to max_length.
+ *   - limits the maximum filesize to max_length.
  *
  * Returns: a negative error code on failure.
  */
@@ -268,7 +268,7 @@ nm_utils_fd_get_contents (int fd,
  *
  * A reimplementation of g_file_get_contents() with a few differences:
  *   - accepts an @dirfd to open @filename relative to that path via openat().
- *   - limits the maxium filesize to max_length.
+ *   - limits the maximum filesize to max_length.
  *   - uses O_CLOEXEC on internal file descriptor
  *
  * Returns: a negative error code on failure.
@@ -351,7 +351,7 @@ nm_utils_file_set_contents (const char *filename,
 		length = strlen (contents);
 
 	tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
-	fd = g_mkstemp_full (tmp_name, O_RDWR, mode);
+	fd = g_mkstemp_full (tmp_name, O_RDWR | O_CLOEXEC, mode);
 	if (fd < 0) {
 		errsv = errno;
 		g_set_error (error,
diff --git a/shared/nm-utils/nm-logging-fwd.h b/shared/nm-utils/nm-logging-fwd.h
new file mode 100644
index 00000000..303d5951
--- /dev/null
+++ b/shared/nm-utils/nm-logging-fwd.h
@@ -0,0 +1,111 @@
+/* NetworkManager -- Network link manager
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2006 - 2018 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef __NM_LOGGING_DEFINES_H__
+#define __NM_LOGGING_DEFINES_H__
+
+/* Log domains */
+
+typedef enum  { /*< skip >*/
+	LOGD_NONE       = 0LL,
+	LOGD_PLATFORM   = (1LL << 0), /* Platform services */
+	LOGD_RFKILL     = (1LL << 1),
+	LOGD_ETHER      = (1LL << 2),
+	LOGD_WIFI       = (1LL << 3),
+	LOGD_BT         = (1LL << 4),
+	LOGD_MB         = (1LL << 5), /* mobile broadband */
+	LOGD_DHCP4      = (1LL << 6),
+	LOGD_DHCP6      = (1LL << 7),
+	LOGD_PPP        = (1LL << 8),
+	LOGD_WIFI_SCAN  = (1LL << 9),
+	LOGD_IP4        = (1LL << 10),
+	LOGD_IP6        = (1LL << 11),
+	LOGD_AUTOIP4    = (1LL << 12),
+	LOGD_DNS        = (1LL << 13),
+	LOGD_VPN        = (1LL << 14),
+	LOGD_SHARING    = (1LL << 15), /* Connection sharing/dnsmasq */
+	LOGD_SUPPLICANT = (1LL << 16), /* Wi-Fi and 802.1x */
+	LOGD_AGENTS     = (1LL << 17), /* Secret agents */
+	LOGD_SETTINGS   = (1LL << 18), /* Settings */
+	LOGD_SUSPEND    = (1LL << 19), /* Suspend/Resume */
+	LOGD_CORE       = (1LL << 20), /* Core daemon and policy stuff */
+	LOGD_DEVICE     = (1LL << 21), /* Device state and activation */
+	LOGD_OLPC       = (1LL << 22),
+	LOGD_INFINIBAND = (1LL << 23),
+	LOGD_FIREWALL   = (1LL << 24),
+	LOGD_ADSL       = (1LL << 25),
+	LOGD_BOND       = (1LL << 26),
+	LOGD_VLAN       = (1LL << 27),
+	LOGD_BRIDGE     = (1LL << 28),
+	LOGD_DBUS_PROPS = (1LL << 29),
+	LOGD_TEAM       = (1LL << 30),
+	LOGD_CONCHECK   = (1LL << 31),
+	LOGD_DCB        = (1LL << 32), /* Data Center Bridging */
+	LOGD_DISPATCH   = (1LL << 33),
+	LOGD_AUDIT      = (1LL << 34),
+	LOGD_SYSTEMD    = (1LL << 35),
+	LOGD_VPN_PLUGIN = (1LL << 36),
+	LOGD_PROXY      = (1LL << 37),
+
+	__LOGD_MAX,
+	LOGD_ALL       = (((__LOGD_MAX - 1LL) << 1) - 1LL),
+	LOGD_DEFAULT   = LOGD_ALL & ~(
+	                              LOGD_DBUS_PROPS |
+	                              LOGD_WIFI_SCAN |
+	                              LOGD_VPN_PLUGIN |
+	                              0),
+
+	/* aliases: */
+	LOGD_DHCP       = LOGD_DHCP4 | LOGD_DHCP6,
+	LOGD_IP         = LOGD_IP4 | LOGD_IP6,
+} NMLogDomain;
+
+/* Log levels */
+typedef enum  { /*< skip >*/
+	LOGL_TRACE,
+	LOGL_DEBUG,
+	LOGL_INFO,
+	LOGL_WARN,
+	LOGL_ERR,
+
+	_LOGL_N_REAL, /* the number of actual logging levels */
+
+	_LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */
+	_LOGL_KEEP,               /* special logging level to indicate that the logging level should not be changed. */
+
+	_LOGL_N, /* the number of logging levels including "OFF" */
+} NMLogLevel;
+
+gboolean _nm_log_enabled (NMLogLevel level,
+                          NMLogDomain domain);
+
+void _nm_log_impl (const char *file,
+                   guint line,
+                   const char *func,
+                   NMLogLevel level,
+                   NMLogDomain domain,
+                   int error,
+                   const char *ifname,
+                   const char *con_uuid,
+                   const char *fmt,
+                   ...) _nm_printf (9, 10);
+
+#endif /* __NM_LOGGING_DEFINES_H__ */
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index 084219b4..1f3970fe 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -32,19 +32,21 @@
 
 /*****************************************************************************/
 
-#define _nm_packed           __attribute__ ((packed))
-#define _nm_unused           __attribute__ ((unused))
-#define _nm_pure             __attribute__ ((pure))
-#define _nm_const            __attribute__ ((const))
+#define _nm_packed           __attribute__ ((__packed__))
+#define _nm_unused           __attribute__ ((__unused__))
+#define _nm_used             __attribute__ ((__used__))
+#define _nm_pure             __attribute__ ((__pure__))
+#define _nm_const            __attribute__ ((__const__))
 #define _nm_printf(a,b)      __attribute__ ((__format__ (__printf__, a, b)))
-#define _nm_align(s)         __attribute__ ((aligned (s)))
+#define _nm_align(s)         __attribute__ ((__aligned__ (s)))
+#define _nm_section(s)       __attribute__ ((__section__ (s)))
 #define _nm_alignof(type)    __alignof (type)
 #define _nm_alignas(type)    _nm_align (_nm_alignof (type))
-#define nm_auto(fcn)         __attribute__ ((cleanup(fcn)))
+#define nm_auto(fcn)         __attribute__ ((__cleanup__(fcn)))
 
 
 #if __GNUC__ >= 7
-#define _nm_fallthrough      __attribute__ ((fallthrough))
+#define _nm_fallthrough      __attribute__ ((__fallthrough__))
 #else
 #define _nm_fallthrough
 #endif
@@ -99,7 +101,7 @@ static inline void name (Type *v) \
  * Call g_free() on a variable location when it goes out of scope.
  */
 #define gs_free nm_auto(gs_local_free)
-NM_AUTO_DEFINE_FCN_VOID (void *, gs_local_free, g_free)
+NM_AUTO_DEFINE_FCN_VOID0 (void *, gs_local_free, g_free)
 
 /**
  * gs_unref_object:
@@ -160,7 +162,7 @@ NM_AUTO_DEFINE_FCN0 (GHashTable *, gs_local_hashtable_unref, g_hash_table_unref)
  * of scope.
  */
 #define gs_free_slist nm_auto(gs_local_free_slist)
-NM_AUTO_DEFINE_FCN (GSList *, gs_local_free_slist, g_slist_free)
+NM_AUTO_DEFINE_FCN0 (GSList *, gs_local_free_slist, g_slist_free)
 
 /**
  * gs_unref_bytes:
@@ -178,7 +180,7 @@ NM_AUTO_DEFINE_FCN0 (GBytes *, gs_local_bytes_unref, g_bytes_unref)
  * Call g_strfreev() on a variable location when it goes out of scope.
  */
 #define gs_strfreev nm_auto(gs_local_strfreev)
-NM_AUTO_DEFINE_FCN (char **, gs_local_strfreev, g_strfreev)
+NM_AUTO_DEFINE_FCN0 (char **, gs_local_strfreev, g_strfreev)
 
 /**
  * gs_free_error:
@@ -222,7 +224,7 @@ static inline int nm_close (int fd);
  * However, let's never mix them. To free malloc'ed memory, always use
  * free() or nm_auto_free.
  */
-NM_AUTO_DEFINE_FCN_VOID (void *, _nm_auto_free_impl, free)
+NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_free_impl, free)
 #define nm_auto_free nm_auto(_nm_auto_free_impl)
 
 NM_AUTO_DEFINE_FCN0 (GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_free)
@@ -231,7 +233,7 @@ NM_AUTO_DEFINE_FCN0 (GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_
 NM_AUTO_DEFINE_FCN0 (GVariantBuilder *, _nm_auto_unref_variant_builder, g_variant_builder_unref)
 #define nm_auto_unref_variant_builder nm_auto(_nm_auto_unref_variant_builder)
 
-NM_AUTO_DEFINE_FCN (GList *, _nm_auto_free_list, g_list_free)
+NM_AUTO_DEFINE_FCN0 (GList *, _nm_auto_free_list, g_list_free)
 #define nm_auto_free_list nm_auto(_nm_auto_free_list)
 
 NM_AUTO_DEFINE_FCN0 (GChecksum *, _nm_auto_checksum_free, g_checksum_free)
@@ -458,6 +460,10 @@ NM_G_ERROR_MSG (GError *error)
 #endif
 
 #ifndef _NM_CC_SUPPORT_GENERIC
+/* In the meantime, NetworkManager requires C11 and _Generic() should always be available.
+ * However, shared/nm-utils may also be used in VPN/applet, which possibly did not yet
+ * bump the C standard requirement. Leave this for the moment, but eventually we can
+ * drop it. */
 #if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) || (defined (__clang__))
 #define _NM_CC_SUPPORT_GENERIC 1
 #else
@@ -605,7 +611,7 @@ NM_G_ERROR_MSG (GError *error)
  * argument is not modified (CC), but you want to make it work also
  * for "char **". C doesn't allow this form of casting (for good reasons),
  * so the function makes a choice like g_strdupv(char**). That means,
- * every time you want to call ith with a const argument, you need to
+ * every time you want to call it with a const argument, you need to
  * explicitly cast it.
  *
  * These macros do the cast, but they only accept a compatible input
@@ -639,6 +645,12 @@ NM_G_ERROR_MSG (GError *error)
 #define NM_PROPAGATE_CONST(test_expr, ptr) (ptr)
 #endif
 
+/* with the way it is implemented, the caller may or may not pass a trailing
+ * ',' and it will work. However, this makes the macro unsuitable for initializing
+ * an array. */
+#define NM_MAKE_STRV(...) \
+	((const char *const[(sizeof (((const char *const[]) { __VA_ARGS__ })) / sizeof (const char *)) + 1]) { __VA_ARGS__ })
+
 /*****************************************************************************/
 
 #define _NM_IN_SET_EVAL_1( op, _x, y)           (_x == (y))
@@ -673,7 +685,7 @@ NM_G_ERROR_MSG (GError *error)
 
 /* Beware that this does short-circuit evaluation (use "||" instead of "|")
  * which has a possibly unexpected non-function-like behavior.
- * Use NM_IN_SET_SE if you need all arguments to be evaluted. */
+ * Use NM_IN_SET_SE if you need all arguments to be evaluated. */
 #define NM_IN_SET(x, ...)                   _NM_IN_SET(||, typeof (x), x, __VA_ARGS__)
 
 /* "SE" stands for "side-effect". Contrary to NM_IN_SET(), this does not do
@@ -724,7 +736,7 @@ _NM_IN_STRSET_streq (const char *x, const char *s)
 
 /* Beware that this does short-circuit evaluation (use "||" instead of "|")
  * which has a possibly unexpected non-function-like behavior.
- * Use NM_IN_STRSET_SE if you need all arguments to be evaluted. */
+ * Use NM_IN_STRSET_SE if you need all arguments to be evaluated. */
 #define NM_IN_STRSET(x, ...)               _NM_IN_STRSET_EVAL_N(||, x, NM_NARG (__VA_ARGS__), __VA_ARGS__)
 
 /* "SE" stands for "side-effect". Contrary to NM_IN_STRSET(), this does not do
@@ -817,6 +829,9 @@ fcn (void) \
 #define nm_streq(s1, s2)  (strcmp (s1, s2) == 0)
 #define nm_streq0(s1, s2) (g_strcmp0 (s1, s2) == 0)
 
+#define NM_STR_HAS_PREFIX(str, prefix) \
+	(strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0)
+
 /*****************************************************************************/
 
 static inline GString *
@@ -829,6 +844,14 @@ nm_gstring_prepare (GString **l)
 	return *l;
 }
 
+static inline GString *
+nm_gstring_add_space_delimiter (GString *str)
+{
+	if (str->len > 0)
+		g_string_append_c (str, ' ');
+	return str;
+}
+
 static inline const char *
 nm_str_not_empty (const char *str)
 {
@@ -973,7 +996,7 @@ static inline void
 nm_g_object_unref (gpointer obj)
 {
 	/* g_object_unref() doesn't accept NULL. Usully, we workaround that
-	 * by using g_clear_object(), but sometimes that is not convinient
+	 * by using g_clear_object(), but sometimes that is not convenient
 	 * (for example as as destroy function for a hash table that can contain
 	 * NULL values). */
 	if (obj)
@@ -1106,6 +1129,23 @@ nm_clear_g_cancellable (GCancellable **cancellable)
 
 /*****************************************************************************/
 
+static inline GVariant *
+nm_g_variant_ref (GVariant *v)
+{
+	if (v)
+		g_variant_ref (v);
+	return v;
+}
+
+static inline void
+nm_g_variant_unref (GVariant *v)
+{
+	if (v)
+		g_variant_unref (v);
+}
+
+/*****************************************************************************/
+
 /* Determine whether @x is a power of two (@x being an integer type).
  * Basically, this returns TRUE, if @x has exactly one bit set.
  * For negative values and zero, this always returns FALSE. */
@@ -1154,7 +1194,7 @@ fcn_name (lookup_type val) \
 /* Call the string-lookup-table function @fcn_name. If the function returns
  * %NULL, the numeric index is converted to string using a alloca() buffer.
  * Beware: this macro uses alloca(). */
-#define NM_UTILS_LOOKUP_STR(fcn_name, idx) \
+#define NM_UTILS_LOOKUP_STR_A(fcn_name, idx) \
 	({ \
 		typeof (idx) _idx = (idx); \
 		const char *_s; \
@@ -1321,6 +1361,16 @@ nm_strcmp_p (gconstpointer a, gconstpointer b)
 		     : NM_UNIQ_T(X,xq)); \
 	})
 
+#define NM_MAX_WITH_CMP(cmp, a, b) \
+	({ \
+		typeof (a) _a = (a); \
+		typeof (b) _b = (b); \
+		\
+		(  ((cmp (_a, _b)) >= 0) \
+		 ? _a \
+		 : _b); \
+	})
+
 /*****************************************************************************/
 
 static inline guint
@@ -1359,7 +1409,8 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
  * If @str is longer then @trunc_at, the string is truncated and the closing
  * quote is instead '^' to indicate truncation.
  *
- * Thus, the maximum stack allocated buffer will be @trunc_at+3. */
+ * Thus, the maximum stack allocated buffer will be @trunc_at+3. The maximum
+ * buffer size must be a constant and not larger than 300. */
 #define nm_strquote_a(trunc_at, str) \
 	({ \
 		const char *const _str = (str); \
@@ -1370,6 +1421,8 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
 				const gsize _strlen_trunc = NM_MIN (strlen (_str), _trunc_at); \
 				char *_buf; \
 				\
+				G_STATIC_ASSERT_EXPR ((trunc_at) <= 300); \
+				\
 				_buf = g_alloca (_strlen_trunc + 3); \
 				_buf[0] = '"'; \
 				memcpy (&_buf[1], _str, _strlen_trunc); \
@@ -1394,19 +1447,30 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
 		_buf; \
 	})
 
-#define nm_sprintf_bufa(n_elements, format, ...) \
+/* it is "unsafe" because @bufsize must not be a constant expression and
+ * there is no check at compiletime. Regardless of that, the buffer size
+ * must not be larger than 300 bytes, as this gets stack allocated. */
+#define nm_sprintf_buf_unsafe_a(bufsize, format, ...) \
 	({ \
 		char *_buf; \
 		int _buf_len; \
-		typeof (n_elements) _n_elements = (n_elements); \
+		typeof (bufsize) _bufsize = (bufsize); \
+		\
+		nm_assert (_bufsize <= 300); \
 		\
-		_buf = g_alloca (_n_elements); \
-		_buf_len = g_snprintf (_buf, _n_elements, \
+		_buf = g_alloca (_bufsize); \
+		_buf_len = g_snprintf (_buf, _bufsize, \
 		                       ""format"", ##__VA_ARGS__); \
-		nm_assert (_buf_len < _n_elements); \
+		nm_assert (_buf_len >= 0 && _buf_len < _bufsize); \
 		_buf; \
 	})
 
+#define nm_sprintf_bufa(bufsize, format, ...) \
+	({ \
+		G_STATIC_ASSERT_EXPR ((bufsize) <= 300); \
+		nm_sprintf_buf_unsafe_a ((bufsize), format, ##__VA_ARGS__); \
+	})
+
 /* aims to alloca() a buffer and fill it with printf(format, name).
  * Note that format must not contain any format specifier except
  * "%s".
@@ -1420,8 +1484,9 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
 		char *_buf2; \
 		\
 		nm_assert (_p_val_to_free && !*_p_val_to_free); \
-		if (NM_STRLEN (format) + _name_len < 200) \
-			_buf2 = nm_sprintf_bufa (NM_STRLEN (format) + _name_len, format, _name); \
+		if (   NM_STRLEN (format) <= 290 \
+		    && _name_len < (gsize) (290 - NM_STRLEN (format))) \
+			_buf2 = nm_sprintf_buf_unsafe_a (NM_STRLEN (format) + _name_len, format, _name); \
 		else { \
 			_buf2 = g_strdup_printf (format, _name); \
 			*_p_val_to_free = _buf2; \
@@ -1433,7 +1498,7 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
 
 /**
  * The boolean type _Bool is C99 while we mostly stick to C89. However, _Bool is too
- * convinient to miss and is effectively available in gcc and clang. So, just use it.
+ * convenient to miss and is effectively available in gcc and clang. So, just use it.
  *
  * Usually, one would include "stdbool.h" to get the "bool" define which aliases
  * _Bool. We provide this define here, because we want to make use of it anywhere.
@@ -1445,7 +1510,7 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
  *   is a typedef for int). Especially when having boolean fields in a struct, we can
  *   thereby easily save some space.
  *
- * - _Bool type guarantees that two "true" expressions compare equal. E.g. the follwing
+ * - _Bool type guarantees that two "true" expressions compare equal. E.g. the following
  *   will not work:
  *        gboolean v1 = 1;
  *        gboolean v2 = 2;
diff --git a/shared/nm-utils/nm-random-utils.c b/shared/nm-utils/nm-random-utils.c
index 3e968a8e..d7c7da42 100644
--- a/shared/nm-utils/nm-random-utils.c
+++ b/shared/nm-utils/nm-random-utils.c
@@ -81,7 +81,7 @@ nm_utils_random_bytes (void *p, size_t n)
 
 				/* no or partial read. There is not enough entropy.
 				 * Fill the rest reading from urandom, and remember that
-				 * some bits are not hight quality. */
+				 * some bits are not high quality. */
 				nm_assert (r < n);
 				buf += r;
 				n -= r;
diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-utils/nm-secret-utils.h
index 21a3c1ba..9df31afe 100644
--- a/shared/nm-utils/nm-secret-utils.h
+++ b/shared/nm-utils/nm-secret-utils.h
@@ -43,7 +43,7 @@ nm_free_secret (char *secret)
 	}
 }
 
-NM_AUTO_DEFINE_FCN (char *, _nm_auto_free_secret, nm_free_secret)
+NM_AUTO_DEFINE_FCN0 (char *, _nm_auto_free_secret, nm_free_secret)
 /**
  * nm_auto_free_secret:
  *
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index ba38237b..c9ef89e9 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -59,6 +59,41 @@ nm_utils_strbuf_append_c (char **buf, gsize *len, char c)
 }
 
 void
+nm_utils_strbuf_append_bin (char **buf, gsize *len, gconstpointer str, gsize str_len)
+{
+	switch (*len) {
+	case 0:
+		return;
+	case 1:
+		if (str_len == 0) {
+			(*buf)[0] = '\0';
+			return;
+		}
+		(*buf)[0] = '\0';
+		*len = 0;
+		(*buf)++;
+		return;
+	default:
+		if (str_len == 0) {
+			(*buf)[0] = '\0';
+			return;
+		}
+		if (str_len >= *len) {
+			memcpy (*buf, str, *len - 1);
+			(*buf)[*len - 1] = '\0';
+			*buf = &(*buf)[*len];
+			*len = 0;
+		} else {
+			memcpy (*buf, str, str_len);
+			*buf = &(*buf)[str_len];
+			(*buf)[0] = '\0';
+			*len -= str_len;
+		}
+		return;
+	}
+}
+
+void
 nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str)
 {
 	gsize src_len;
@@ -118,7 +153,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
 /**
  * nm_utils_strbuf_seek_end:
  * @buf: the input/output buffer
- * @len: the input/output lenght of the buffer.
+ * @len: the input/output length of the buffer.
  *
  * Commonly, one uses nm_utils_strbuf_append*(), to incrementally
  * append strings to the buffer. However, sometimes we need to use
@@ -459,34 +494,25 @@ nm_utils_ip_is_site_local (int addr_family,
 gboolean
 nm_utils_parse_inaddr_bin (int addr_family,
                            const char *text,
+                           int *out_addr_family,
                            gpointer out_addr)
 {
 	NMIPAddr addrbin;
 
 	g_return_val_if_fail (text, FALSE);
 
-	if (addr_family == AF_UNSPEC)
+	if (addr_family == AF_UNSPEC) {
+		g_return_val_if_fail (!out_addr || out_addr_family, FALSE);
 		addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
-	else
+	} else
 		g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
 
-	/* use a temporary variable @addrbin, to guarantee that @out_addr
-	 * is only modified on success. */
 	if (inet_pton (addr_family, text, &addrbin) != 1)
 		return FALSE;
 
-	if (out_addr) {
-		switch (addr_family) {
-		case AF_INET:
-			*((in_addr_t *) out_addr) = addrbin.addr4;
-			break;
-		case AF_INET6:
-			*((struct in6_addr *) out_addr) = addrbin.addr6;
-			break;
-		default:
-			nm_assert_not_reached ();
-		}
-	}
+	NM_SET_OUT (out_addr_family, addr_family);
+	if (out_addr)
+		nm_ip_addr_set (addr_family, out_addr, &addrbin);
 	return TRUE;
 }
 
@@ -498,9 +524,7 @@ nm_utils_parse_inaddr (int addr_family,
 	NMIPAddr addrbin;
 	char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
 
-	nm_assert (!out_addr || !*out_addr);
-
-	if (!nm_utils_parse_inaddr_bin (addr_family, text, &addrbin))
+	if (!nm_utils_parse_inaddr_bin (addr_family, text, &addr_family, &addrbin))
 		return FALSE;
 	NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
 	return TRUE;
@@ -509,6 +533,7 @@ nm_utils_parse_inaddr (int addr_family,
 gboolean
 nm_utils_parse_inaddr_prefix_bin (int addr_family,
                                   const char *text,
+                                  int *out_addr_family,
                                   gpointer out_addr,
                                   int *out_prefix)
 {
@@ -517,19 +542,14 @@ nm_utils_parse_inaddr_prefix_bin (int addr_family,
 	const char *slash;
 	const char *addrstr;
 	NMIPAddr addrbin;
-	int addr_len;
 
 	g_return_val_if_fail (text, FALSE);
 
-	if (addr_family == AF_UNSPEC)
+	if (addr_family == AF_UNSPEC) {
+		g_return_val_if_fail (!out_addr || out_addr_family, FALSE);
 		addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
-
-	if (addr_family == AF_INET)
-		addr_len = sizeof (in_addr_t);
-	else if (addr_family == AF_INET6)
-		addr_len = sizeof (struct in6_addr);
-	else
-		g_return_val_if_reached (FALSE);
+	} else
+		g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
 
 	slash = strchr (text, '/');
 	if (slash)
@@ -549,8 +569,9 @@ nm_utils_parse_inaddr_prefix_bin (int addr_family,
 			return FALSE;
 	}
 
+	NM_SET_OUT (out_addr_family, addr_family);
 	if (out_addr)
-		memcpy (out_addr, &addrbin, addr_len);
+		nm_ip_addr_set (addr_family, out_addr, &addrbin);
 	NM_SET_OUT (out_prefix, prefix);
 	return TRUE;
 }
@@ -564,7 +585,7 @@ nm_utils_parse_inaddr_prefix (int addr_family,
 	NMIPAddr addrbin;
 	char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
 
-	if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addrbin, out_prefix))
+	if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addr_family, &addrbin, out_prefix))
 		return FALSE;
 	NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
 	return TRUE;
@@ -1081,11 +1102,24 @@ nm_utils_error_is_cancelled (GError *error,
                              gboolean consider_is_disposing)
 {
 	if (error) {
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			return TRUE;
-		if (   consider_is_disposing
-		    && g_error_matches (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING))
-			return TRUE;
+		if (error->domain == G_IO_ERROR)
+			return NM_IN_SET (error->code, G_IO_ERROR_CANCELLED);
+		if (consider_is_disposing) {
+			if (error->domain == NM_UTILS_ERROR)
+				return NM_IN_SET (error->code, NM_UTILS_ERROR_CANCELLED_DISPOSING);
+		}
+	}
+	return FALSE;
+}
+
+gboolean
+nm_utils_error_is_notfound (GError *error)
+{
+	if (error) {
+		if (error->domain == G_IO_ERROR)
+			return NM_IN_SET (error->code, G_IO_ERROR_NOT_FOUND);
+		if (error->domain == G_FILE_ERROR)
+			return NM_IN_SET (error->code, G_FILE_ERROR_NOENT);
 	}
 	return FALSE;
 }
@@ -1109,7 +1143,7 @@ nm_utils_error_is_cancelled (GError *error,
  */
 gboolean
 nm_g_object_set_property (GObject *object,
-                          const char   *property_name,
+                          const char *property_name,
                           const GValue *value,
                           GError **error)
 {
@@ -1182,30 +1216,136 @@ nm_g_object_set_property (GObject *object,
 	return TRUE;
 }
 
+#define _set_property(object, property_name, gtype, gtype_set, value, error) \
+	G_STMT_START { \
+		nm_auto_unset_gvalue GValue gvalue = { 0 }; \
+		\
+		g_value_init (&gvalue, gtype); \
+		gtype_set (&gvalue, (value)); \
+		return nm_g_object_set_property ((object), (property_name), &gvalue, (error)); \
+	} G_STMT_END
+
+gboolean
+nm_g_object_set_property_string (GObject *object,
+                                 const char *property_name,
+                                 const char *value,
+                                 GError **error)
+{
+	_set_property (object, property_name, G_TYPE_STRING, g_value_set_string, value, error);
+}
+
+gboolean
+nm_g_object_set_property_string_static (GObject *object,
+                                        const char *property_name,
+                                        const char *value,
+                                        GError **error)
+{
+	_set_property (object, property_name, G_TYPE_STRING, g_value_set_static_string, value, error);
+}
+
+gboolean
+nm_g_object_set_property_string_take (GObject *object,
+                                      const char *property_name,
+                                      char *value,
+                                      GError **error)
+{
+	_set_property (object, property_name, G_TYPE_STRING, g_value_take_string, value, error);
+}
+
 gboolean
 nm_g_object_set_property_boolean (GObject *object,
-                                  const char   *property_name,
+                                  const char *property_name,
                                   gboolean value,
                                   GError **error)
 {
-	nm_auto_unset_gvalue GValue gvalue = { 0 };
+	_set_property (object, property_name, G_TYPE_BOOLEAN, g_value_set_boolean, !!value, error);
+}
+
+gboolean
+nm_g_object_set_property_char (GObject *object,
+                               const char *property_name,
+                               gint8 value,
+                               GError **error)
+{
+	/* glib says about G_TYPE_CHAR:
+	 *
+	 * The type designated by G_TYPE_CHAR is unconditionally an 8-bit signed integer.
+	 *
+	 * This is always a (signed!) char. */
+	_set_property (object, property_name, G_TYPE_CHAR, g_value_set_schar, value, error);
+}
 
-	g_value_init (&gvalue, G_TYPE_BOOLEAN);
-	g_value_set_boolean (&gvalue, !!value);
-	return nm_g_object_set_property (object, property_name, &gvalue, error);
+gboolean
+nm_g_object_set_property_uchar (GObject *object,
+                                const char *property_name,
+                                guint8 value,
+                                GError **error)
+{
+	_set_property (object, property_name, G_TYPE_UCHAR, g_value_set_uchar, value, error);
+}
+
+gboolean
+nm_g_object_set_property_int (GObject *object,
+                              const char *property_name,
+                              int value,
+                              GError **error)
+{
+	_set_property (object, property_name, G_TYPE_INT, g_value_set_int, value, error);
+}
+
+gboolean
+nm_g_object_set_property_int64 (GObject *object,
+                                const char *property_name,
+                                gint64 value,
+                                GError **error)
+{
+	_set_property (object, property_name, G_TYPE_INT64, g_value_set_int64, value, error);
 }
 
 gboolean
 nm_g_object_set_property_uint (GObject *object,
-                               const char   *property_name,
+                               const char *property_name,
                                guint value,
                                GError **error)
 {
-	nm_auto_unset_gvalue GValue gvalue = { 0 };
+	_set_property (object, property_name, G_TYPE_UINT, g_value_set_uint, value, error);
+}
+
+gboolean
+nm_g_object_set_property_uint64 (GObject *object,
+                                 const char *property_name,
+                                 guint64 value,
+                                 GError **error)
+{
+	_set_property (object, property_name, G_TYPE_UINT64, g_value_set_uint64, value, error);
+}
 
-	g_value_init (&gvalue, G_TYPE_UINT);
-	g_value_set_uint (&gvalue, value);
-	return nm_g_object_set_property (object, property_name, &gvalue, error);
+gboolean
+nm_g_object_set_property_flags (GObject *object,
+                                const char *property_name,
+                                GType gtype,
+                                guint value,
+                                GError **error)
+{
+	nm_assert (({
+	                nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype);
+	                G_IS_FLAGS_CLASS (gtypeclass);
+	           }));
+	_set_property (object, property_name, gtype, g_value_set_flags, value, error);
+}
+
+gboolean
+nm_g_object_set_property_enum (GObject *object,
+                               const char *property_name,
+                               GType gtype,
+                               int value,
+                               GError **error)
+{
+	nm_assert (({
+	                nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype);
+	                G_IS_ENUM_CLASS (gtypeclass);
+	           }));
+	_set_property (object, property_name, gtype, g_value_set_enum, value, error);
 }
 
 GParamSpec *
@@ -2113,3 +2253,125 @@ _nm_utils_unescape_spaces (char *str)
 }
 
 #undef IS_SPACE
+
+/*****************************************************************************/
+
+typedef struct {
+	gpointer callback_user_data;
+	GCancellable *cancellable;
+	NMUtilsInvokeOnIdleCallback callback;
+	gulong cancelled_id;
+	guint idle_id;
+} InvokeOnIdleData;
+
+static gboolean
+_nm_utils_invoke_on_idle_cb_idle (gpointer user_data)
+{
+	InvokeOnIdleData *data = user_data;
+
+	data->idle_id = 0;
+	nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id);
+
+	data->callback (data->callback_user_data, data->cancellable);
+	nm_g_object_unref (data->cancellable);
+	g_slice_free (InvokeOnIdleData, data);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+_nm_utils_invoke_on_idle_cb_cancelled (GCancellable *cancellable,
+                                       InvokeOnIdleData *data)
+{
+	/* on cancellation, we invoke the callback synchronously. */
+	nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id);
+	nm_clear_g_source (&data->idle_id);
+	data->callback (data->callback_user_data, data->cancellable);
+	nm_g_object_unref (data->cancellable);
+	g_slice_free (InvokeOnIdleData, data);
+}
+
+void
+nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback,
+                         gpointer callback_user_data,
+                         GCancellable *cancellable)
+{
+	InvokeOnIdleData *data;
+
+	g_return_if_fail (callback);
+
+	data = g_slice_new (InvokeOnIdleData);
+	data->callback = callback;
+	data->callback_user_data = callback_user_data;
+	data->cancellable = nm_g_object_ref (cancellable);
+	if (   cancellable
+	    && !g_cancellable_is_cancelled (cancellable)) {
+		/* if we are passed a non-cancelled cancellable, we register to the "cancelled"
+		 * signal an invoke the callback synchronously (from the signal handler).
+		 *
+		 * We don't do that,
+		 *  - if the cancellable is already cancelled (because we don't want to invoke
+		 *    the callback synchronously from the caller).
+		 *  - if we have no cancellable at hand. */
+		data->cancelled_id = g_signal_connect (cancellable,
+		                                       "cancelled",
+		                                       G_CALLBACK (_nm_utils_invoke_on_idle_cb_cancelled),
+		                                       data);
+	} else
+		data->cancelled_id = 0;
+	data->idle_id = g_idle_add (_nm_utils_invoke_on_idle_cb_idle, data);
+}
+
+/*****************************************************************************/
+
+int
+nm_utils_getpagesize (void)
+{
+	static volatile int val = 0;
+	long l;
+	int v;
+
+	v = g_atomic_int_get (&val);
+
+	if (G_UNLIKELY (v == 0)) {
+		l = sysconf (_SC_PAGESIZE);
+
+		g_return_val_if_fail (l > 0 && l < G_MAXINT, 4*1024);
+
+		v = (int) l;
+		if (!g_atomic_int_compare_and_exchange (&val, 0, v)) {
+			v = g_atomic_int_get (&val);
+			g_return_val_if_fail (v > 0, 4*1024);
+		}
+	}
+
+	nm_assert (v > 0);
+#if NM_MORE_ASSERTS > 5
+	nm_assert (v == getpagesize ());
+	nm_assert (v == sysconf (_SC_PAGESIZE));
+#endif
+
+	return v;
+}
+
+gboolean
+nm_utils_memeqzero (gconstpointer data, gsize length)
+{
+	const unsigned char *p = data;
+	int len;
+
+	/* Taken from https://github.com/rustyrussell/ccan/blob/9d2d2c49f053018724bcc6e37029da10b7c3d60d/ccan/mem/mem.c#L92,
+	 * CC-0 licensed. */
+
+	/* Check first 16 bytes manually */
+	for (len = 0; len < 16; len++) {
+		if (!length)
+			return TRUE;
+		if (*p)
+			return FALSE;
+		p++;
+		length--;
+	}
+
+	/* Now we know that's zero, memcmp with self. */
+	return memcmp (data, p, length) == 0;
+}
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index 3ea887b4..e28a5fb2 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -76,8 +76,9 @@ static inline char
 nm_utils_addr_family_to_char (int addr_family)
 {
 	switch (addr_family) {
-	case AF_INET:  return '4';
-	case AF_INET6: return '6';
+	case AF_UNSPEC: return 'X';
+	case AF_INET:   return '4';
+	case AF_INET6:  return '6';
 	}
 	g_return_val_if_reached ('?');
 }
@@ -101,6 +102,7 @@ typedef struct {
 	union {
 		guint8 addr_ptr[1];
 		in_addr_t addr4;
+		struct in_addr addr4_struct;
 		struct in6_addr addr6;
 
 		/* NMIPAddr is really a union for IP addresses.
@@ -113,16 +115,17 @@ typedef struct {
 extern const NMIPAddr nm_ip_addr_zero;
 
 static inline void
-nm_ip_addr_set (int addr_family, gpointer dst, const NMIPAddr *src)
+nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src)
 {
 	nm_assert_addr_family (addr_family);
 	nm_assert (dst);
 	nm_assert (src);
 
-	if (addr_family != AF_INET6)
-		*((in_addr_t *) dst) = src->addr4;
-	else
-		*((struct in6_addr *) dst) = src->addr6;
+	memcpy (dst,
+	        src,
+	        (addr_family != AF_INET6)
+	          ? sizeof (in_addr_t)
+	          : sizeof (struct in6_addr));
 }
 
 /*****************************************************************************/
@@ -217,19 +220,7 @@ nm_ip_addr_set (int addr_family, gpointer dst, const NMIPAddr *src)
 
 /*****************************************************************************/
 
-static inline gboolean
-nm_utils_mem_all_zero (gconstpointer mem, gsize len)
-{
-	const guint8 *p;
-
-	for (p = mem; len-- > 0; p++) {
-		if (*p != 0)
-			return FALSE;
-	}
-
-	/* incidentally, a buffer with len==0, is also *all-zero*. */
-	return TRUE;
-}
+gboolean nm_utils_memeqzero (gconstpointer data, gsize length);
 
 /*****************************************************************************/
 
@@ -262,6 +253,17 @@ nm_memdup (gconstpointer data, gsize size)
 	return p;
 }
 
+static inline char *
+_nm_strndup_a_step (char *s, const char *str, gsize len)
+{
+	NM_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation");
+	if (len > 0)
+		strncpy (s, str, len);
+	s[len] = '\0';
+	return s;
+	NM_PRAGMA_WARNING_REENABLE;
+}
+
 /* Similar to g_strndup(), however, if the string (including the terminating
  * NUL char) fits into alloca_maxlen, this will alloca() the memory.
  *
@@ -270,7 +272,12 @@ nm_memdup (gconstpointer data, gsize size)
  *
  * In case malloc() is necessary, @out_str_free will be set (this string
  * must be freed afterwards). It is permissible to pass %NULL as @out_str_free,
- * if you ensure that len < alloca_maxlen. */
+ * if you ensure that len < alloca_maxlen.
+ *
+ * Note that just like g_strndup(), this always returns a buffer with @len + 1
+ * bytes, even if strlen(@str) is shorter than that (NUL terminated early). We fill
+ * the buffer with strncpy(), which means, that @str is copied up to the first
+ * NUL character and then filled with NUL characters. */
 #define nm_strndup_a(alloca_maxlen, str, len, out_str_free) \
 	({ \
 		const gsize _alloca_maxlen = (alloca_maxlen); \
@@ -279,6 +286,8 @@ nm_memdup (gconstpointer data, gsize size)
 		char **const _out_str_free = (out_str_free); \
 		char *_s; \
 		\
+		G_STATIC_ASSERT_EXPR ((alloca_maxlen) <= 300); \
+		\
 		if (   _out_str_free \
 		    && _len >= _alloca_maxlen) { \
 			_s = g_malloc (_len + 1); \
@@ -287,14 +296,46 @@ nm_memdup (gconstpointer data, gsize size)
 			g_assert (_len < _alloca_maxlen); \
 			_s = g_alloca (_len + 1); \
 		} \
-		if (_len > 0) \
-			strncpy (_s, _str, _len); \
-		_s[_len] = '\0'; \
-		_s; \
+		_nm_strndup_a_step (_s, _str, _len); \
 	})
 
 /*****************************************************************************/
 
+/* generic macro to convert an int to a (heap allocated) string.
+ *
+ * Usually, an inline function nm_strdup_int64() would be enough. However,
+ * that cannot be used for guint64. So, we would also need nm_strdup_uint64().
+ * This causes suble error potential, because the caller needs to ensure to
+ * use the right one (and compiler isn't going to help as it silently casts).
+ *
+ * Instead, this generic macro is supposed to handle all integers correctly. */
+#if _NM_CC_SUPPORT_GENERIC
+#define nm_strdup_int(val) \
+	_Generic ((val), \
+	          char:               g_strdup_printf ("%d",   (int)                (val)), \
+	          \
+	          signed char:        g_strdup_printf ("%d",   (signed)             (val)), \
+	          signed short:       g_strdup_printf ("%d",   (signed)             (val)), \
+	          signed:             g_strdup_printf ("%d",   (signed)             (val)), \
+	          signed long:        g_strdup_printf ("%ld",  (signed long)        (val)), \
+	          signed long long:   g_strdup_printf ("%lld", (signed long long)   (val)), \
+	          \
+	          unsigned char:      g_strdup_printf ("%u",   (unsigned)           (val)), \
+	          unsigned short:     g_strdup_printf ("%u",   (unsigned)           (val)), \
+	          unsigned:           g_strdup_printf ("%u",   (unsigned)           (val)), \
+	          unsigned long:      g_strdup_printf ("%lu",  (unsigned long)      (val)), \
+	          unsigned long long: g_strdup_printf ("%llu", (unsigned long long) (val))  \
+	)
+#else
+#define nm_strdup_int(val) \
+	(  (   sizeof (val) == sizeof (guint64) \
+	    && ((typeof (val)) -1) > 0) \
+	 ? g_strdup_printf ("%"G_GUINT64_FORMAT, (guint64) (val)) \
+	 : g_strdup_printf ("%"G_GINT64_FORMAT, (gint64) (val)))
+#endif
+
+/*****************************************************************************/
+
 extern const void *const _NM_PTRARRAY_EMPTY[1];
 
 #define NM_PTRARRAY_EMPTY(type) ((type const*) _NM_PTRARRAY_EMPTY)
@@ -315,6 +356,7 @@ _nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
 void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4);
 void nm_utils_strbuf_append_c (char **buf, gsize *len, char c);
 void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
+void nm_utils_strbuf_append_bin (char **buf, gsize *len, gconstpointer str, gsize str_len);
 void nm_utils_strbuf_seek_end (char **buf, gsize *len);
 
 const char *nm_strquote (char *buf, gsize buf_len, const char *str);
@@ -379,6 +421,46 @@ char **_nm_utils_strv_cleanup (char **strv,
 
 /*****************************************************************************/
 
+#define NM_UTILS_CHECKSUM_LENGTH_MD5          16
+#define NM_UTILS_CHECKSUM_LENGTH_SHA1         20
+#define NM_UTILS_CHECKSUM_LENGTH_SHA256       32
+
+#define nm_utils_checksum_get_digest(sum, arr) \
+	G_STMT_START { \
+		GChecksum *const _sum = (sum); \
+		gsize _len; \
+		\
+		G_STATIC_ASSERT_EXPR (   sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_MD5 \
+		                      || sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_SHA1 \
+		                      || sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_SHA256); \
+		G_STATIC_ASSERT_EXPR (sizeof (arr) == G_N_ELEMENTS (arr)); \
+		\
+		nm_assert (_sum); \
+		\
+		_len = G_N_ELEMENTS (arr); \
+		\
+		g_checksum_get_digest (_sum, (arr), &_len); \
+		nm_assert (_len == G_N_ELEMENTS (arr)); \
+	} G_STMT_END
+
+#define nm_utils_checksum_get_digest_len(sum, buf, len) \
+	G_STMT_START { \
+		GChecksum *const _sum = (sum); \
+		const gsize _len0 = (len); \
+		gsize _len; \
+		\
+		nm_assert (NM_IN_SET (_len0, NM_UTILS_CHECKSUM_LENGTH_MD5, \
+		                             NM_UTILS_CHECKSUM_LENGTH_SHA1, \
+		                             NM_UTILS_CHECKSUM_LENGTH_SHA256)); \
+		nm_assert (_sum); \
+		\
+		_len = _len0; \
+		g_checksum_get_digest (_sum, (buf), &_len); \
+		nm_assert (_len == _len0); \
+	} G_STMT_END
+
+/*****************************************************************************/
+
 guint32 _nm_utils_ip4_prefix_to_netmask (guint32 prefix);
 guint32 _nm_utils_ip4_get_default_prefix (guint32 ip);
 
@@ -389,6 +471,7 @@ gboolean nm_utils_ip_is_site_local (int addr_family,
 
 gboolean nm_utils_parse_inaddr_bin  (int addr_family,
                                      const char *text,
+                                     int *out_addr_family,
                                      gpointer out_addr);
 
 gboolean nm_utils_parse_inaddr (int addr_family,
@@ -397,6 +480,7 @@ gboolean nm_utils_parse_inaddr (int addr_family,
 
 gboolean nm_utils_parse_inaddr_prefix_bin (int addr_family,
                                            const char *text,
+                                           int *out_addr_family,
                                            gpointer out_addr,
                                            int *out_prefix);
 
@@ -539,19 +623,6 @@ _nm_g_slice_free_fcn_define (16)
 
 /*****************************************************************************/
 
-static inline int
-nm_errno (int errsv)
-{
-	/* several API returns negative errno values as errors. Normalize
-	 * negative values to positive values.
-	 *
-	 * As a special case, map G_MININT to G_MAXINT. If you care about the
-	 * distinction, then check for G_MININT before. */
-	return errsv >= 0
-	       ? errsv
-	       : ((errsv == G_MININT) ? G_MAXINT : -errsv);
-}
-
 /**
  * NMUtilsError:
  * @NM_UTILS_ERROR_UNKNOWN: unknown or unclassified error
@@ -605,6 +676,8 @@ void nm_utils_error_set_cancelled (GError **error,
 gboolean nm_utils_error_is_cancelled (GError *error,
                                       gboolean consider_is_disposing);
 
+gboolean nm_utils_error_is_notfound (GError *error);
+
 static inline void
 nm_utils_error_set_literal (GError **error, int error_code, const char *literal)
 {
@@ -620,25 +693,85 @@ nm_utils_error_set_literal (GError **error, int error_code, const char *literal)
 	             NM_UTILS_ERROR_UNKNOWN, \
 	             fmt, \
 	             ##__VA_ARGS__, \
-	             g_strerror (nm_errno (errsv)))
+	             g_strerror (({ \
+	                            const int _errsv = (errsv); \
+	                            \
+	                            (  _errsv >= 0 \
+	                             ? _errsv \
+	                             : (  (_errsv == G_MININT) \
+	                                ? G_MAXINT \
+	                                : -errsv)); \
+	                          })))
 
 /*****************************************************************************/
 
 gboolean nm_g_object_set_property (GObject *object,
-                                   const char   *property_name,
+                                   const char *property_name,
                                    const GValue *value,
                                    GError **error);
 
+gboolean nm_g_object_set_property_string (GObject *object,
+                                          const char *property_name,
+                                          const char *value,
+                                          GError **error);
+
+gboolean nm_g_object_set_property_string_static (GObject *object,
+                                                 const char *property_name,
+                                                 const char *value,
+                                                 GError **error);
+
+gboolean nm_g_object_set_property_string_take (GObject *object,
+                                               const char *property_name,
+                                               char *value,
+                                               GError **error);
+
 gboolean nm_g_object_set_property_boolean (GObject *object,
-                                           const char   *property_name,
+                                           const char *property_name,
                                            gboolean value,
                                            GError **error);
 
+gboolean nm_g_object_set_property_char (GObject *object,
+                                        const char *property_name,
+                                        gint8 value,
+                                        GError **error);
+
+gboolean nm_g_object_set_property_uchar (GObject *object,
+                                         const char *property_name,
+                                         guint8 value,
+                                         GError **error);
+
+gboolean nm_g_object_set_property_int (GObject *object,
+                                       const char *property_name,
+                                       int value,
+                                       GError **error);
+
+gboolean nm_g_object_set_property_int64 (GObject *object,
+                                         const char *property_name,
+                                         gint64 value,
+                                         GError **error);
+
 gboolean nm_g_object_set_property_uint (GObject *object,
-                                        const char   *property_name,
+                                        const char *property_name,
                                         guint value,
                                         GError **error);
 
+gboolean nm_g_object_set_property_uint64 (GObject *object,
+                                          const char *property_name,
+                                          guint64 value,
+                                          GError **error);
+
+gboolean nm_g_object_set_property_flags (GObject *object,
+                                         const char *property_name,
+                                         GType gtype,
+                                         guint value,
+                                         GError **error);
+
+gboolean nm_g_object_set_property_enum (GObject *object,
+                                        const char *property_name,
+                                        GType gtype,
+                                        int value,
+                                        GError **error);
+
 GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype,
                                                         const char *property_name);
 
@@ -907,4 +1040,57 @@ void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...);
 const char *_nm_utils_escape_spaces (const char *str, char **to_free);
 char *_nm_utils_unescape_spaces (char *str);
 
+/*****************************************************************************/
+
+typedef void (*NMUtilsInvokeOnIdleCallback) (gpointer callback_user_data,
+                                             GCancellable *cancellable);
+
+void nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback,
+                              gpointer callback_user_data,
+                              GCancellable *cancellable);
+
+/*****************************************************************************/
+
+static inline void
+nm_strv_ptrarray_add_string_take (GPtrArray *cmd,
+                                  char *str)
+{
+	nm_assert (cmd);
+	nm_assert (str);
+
+	g_ptr_array_add (cmd, str);
+}
+
+static inline void
+nm_strv_ptrarray_add_string_dup (GPtrArray *cmd,
+                                 const char *str)
+{
+	nm_strv_ptrarray_add_string_take (cmd,
+	                                  g_strdup (str));
+}
+
+#define nm_strv_ptrarray_add_string_concat(cmd, ...) \
+	nm_strv_ptrarray_add_string_take ((cmd), g_strconcat (__VA_ARGS__, NULL))
+
+#define nm_strv_ptrarray_add_string_printf(cmd, ...) \
+	nm_strv_ptrarray_add_string_take ((cmd), g_strdup_printf (__VA_ARGS__))
+
+#define nm_strv_ptrarray_add_int(cmd, val) \
+	nm_strv_ptrarray_add_string_take ((cmd), nm_strdup_int (val))
+
+static inline void
+nm_strv_ptrarray_take_gstring (GPtrArray *cmd,
+                               GString **gstr)
+{
+	nm_assert (gstr && *gstr);
+
+	nm_strv_ptrarray_add_string_take (cmd,
+	                                  g_string_free (g_steal_pointer (gstr),
+	                                                 FALSE));
+}
+
+/*****************************************************************************/
+
+int nm_utils_getpagesize (void);
+
 #endif /* __NM_SHARED_UTILS_H__ */
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index c8139862..c235d93d 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -30,6 +30,9 @@
  *
  * Our tests (make check) include this header-only file nm-test-utils.h.
  *
+ * You should always include this header *as last*. Reason is, that depending on
+ * previous includes, functionality will be enabled.
+ *
  * Logging:
  *   In tests, nm-logging redirects to glib logging. By default, glib suppresses all debug
  *   messages unless you set G_MESSAGES_DEBUG. To enable debug logging, you can explicitly set
@@ -110,7 +113,9 @@
 #include <string.h>
 #include <errno.h>
 
+#ifndef NM_TEST_UTILS_NO_LIBNM
 #include "nm-utils.h"
+#endif
 
 /*****************************************************************************/
 
@@ -191,6 +196,25 @@
 
 /*****************************************************************************/
 
+/* Our nm-error error numbers use negative values to signal failure.
+ * A non-negative value signals success. Hence, the correct way for checking
+ * is always (r < 0) vs. (r >= 0). Never (r == 0).
+ *
+ * For assertions in tests, we also want to assert that no positive values
+ * are returned. For a lot of functions, positive return values are unexpected
+ * and a bug. This macro evaluates @r to success or failure, while asserting
+ * that @r is not positive. */
+#define NMTST_NM_ERR_SUCCESS(r) \
+	({ \
+		const int _r = (r); \
+		\
+		if (_r >= 0) \
+			g_assert_cmpint (_r, ==, 0); \
+		(_r >= 0); \
+	})
+
+/*****************************************************************************/
+
 struct __nmtst_internal
 {
 	GRand *rand0;
@@ -695,6 +719,7 @@ nmtst_test_quick (void)
 #else
 #define NMTST_EXPECT_LIBNM(level, msg)          NMTST_EXPECT ("libnm", level, msg)
 
+#define NMTST_EXPECT_LIBNM_WARNING(msg)         NMTST_EXPECT_LIBNM (G_LOG_LEVEL_WARNING, msg)
 #define NMTST_EXPECT_LIBNM_CRITICAL(msg)        NMTST_EXPECT_LIBNM (G_LOG_LEVEL_CRITICAL, msg)
 #endif
 
@@ -880,6 +905,16 @@ nmtst_rand_buf (GRand *rand, gpointer buffer, gsize buffer_length)
 	return buffer;
 }
 
+#define _nmtst_rand_select(uniq, v0, ...) \
+	({ \
+		typeof (v0) NM_UNIQ_T (UNIQ, uniq)[1 + NM_NARG (__VA_ARGS__)] = { (v0), __VA_ARGS__ }; \
+		\
+		NM_UNIQ_T (UNIQ, uniq)[nmtst_get_rand_int () % G_N_ELEMENTS (NM_UNIQ_T (UNIQ, uniq))]; \
+	})
+
+#define nmtst_rand_select(...) \
+	_nmtst_rand_select (NM_UNIQ, __VA_ARGS__)
+
 static inline void *
 nmtst_rand_perm (GRand *rand, void *dst, const void *src, gsize elmt_size, gsize n_elmt)
 {
@@ -1089,6 +1124,8 @@ __define_nmtst_static(02, 1024)
 __define_nmtst_static(03, 1024)
 #undef __define_nmtst_static
 
+#if defined (__NM_UTILS_H__) || defined (NM_UTILS_H)
+
 #define NMTST_UUID_INIT(uuid) \
 	gs_free char *_nmtst_hidden_##uuid = nm_utils_uuid_generate (); \
 	const char *const uuid = _nmtst_hidden_##uuid
@@ -1105,6 +1142,8 @@ nmtst_uuid_generate (void)
 	return u;
 }
 
+#endif
+
 #define NMTST_SWAP(x,y) \
 	G_STMT_START { \
 		char __nmtst_swap_temp[sizeof(x) == sizeof(y) ? (signed) sizeof(x) : -1]; \
@@ -1156,6 +1195,48 @@ nmtst_inet6_from_string (const char *str)
 	return &addr;
 }
 
+static inline gconstpointer
+nmtst_inet_from_string (int addr_family, const char *str)
+{
+	if (addr_family == AF_INET) {
+		static in_addr_t a;
+
+		a = nmtst_inet4_from_string (str);
+		return &a;
+	}
+	if (addr_family == AF_INET6)
+		return nmtst_inet6_from_string (str);
+
+	g_assert_not_reached ();
+	return NULL;
+}
+
+static inline const char *
+nmtst_inet_to_string (int addr_family, gconstpointer addr)
+{
+	static char buf[MAX (INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
+
+	g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
+	g_assert (addr);
+
+	if (inet_ntop (addr_family, addr, buf, sizeof (buf)) != buf)
+		g_assert_not_reached ();
+
+	return buf;
+}
+
+static inline const char *
+nmtst_inet4_to_string (in_addr_t addr)
+{
+	return nmtst_inet_to_string (AF_INET, &addr);
+}
+
+static inline const char *
+nmtst_inet6_to_string (const struct in6_addr *addr)
+{
+	return nmtst_inet_to_string (AF_INET6, addr);
+}
+
 static inline void
 _nmtst_assert_ip4_address (const char *file, int line, in_addr_t addr, const char *str_expected)
 {
@@ -1337,6 +1418,42 @@ _nmtst_assert_resolve_relative_path_equals (const char *f1, const char *f2, cons
 
 /*****************************************************************************/
 
+#ifdef __NETWORKMANAGER_LOGGING_H__
+static inline gpointer
+nmtst_logging_disable (gboolean always)
+{
+	gpointer p;
+
+	g_assert (nmtst_initialized ());
+	if (!always && __nmtst_internal.no_expect_message) {
+		/* The caller does not want to @always suppress logging. Instead,
+		 * the caller wants to suppress unexpected log messages that would
+		 * fail assertions (since we possibly assert against all unexpected
+		 * log messages).
+		 *
+		 * If the test is run with no-expect-message, then don't suppress
+		 * the loggings, because they also wouldn't fail assertions. */
+		return NULL;
+	}
+
+	p = g_memdup (_nm_logging_enabled_state, sizeof (_nm_logging_enabled_state));
+	memset (_nm_logging_enabled_state, 0, sizeof (_nm_logging_enabled_state));
+	return p;
+}
+
+static inline void
+nmtst_logging_reenable (gpointer old_state)
+{
+	g_assert (nmtst_initialized ());
+	if (old_state) {
+		memcpy (_nm_logging_enabled_state, old_state, sizeof (_nm_logging_enabled_state));
+		g_free (old_state);
+	}
+}
+#endif
+
+/*****************************************************************************/
+
 #ifdef NM_SETTING_IP_CONFIG_H
 static inline void
 nmtst_setting_ip_config_add_address (NMSettingIPConfig *s_ip,
@@ -1859,8 +1976,8 @@ nmtst_assert_hwaddr_equals (gconstpointer hwaddr1, gssize hwaddr1_len, const cha
 static inline NMConnection *
 nmtst_create_connection_from_keyfile (const char *keyfile_str, const char *full_filename)
 {
-	GKeyFile *keyfile;
-	GError *error = NULL;
+	gs_unref_keyfile GKeyFile *keyfile = NULL;
+	gs_free_error GError *error = NULL;
 	gboolean success;
 	NMConnection *con;
 	gs_free char *filename = g_path_get_basename (full_filename);
@@ -1871,14 +1988,10 @@ nmtst_create_connection_from_keyfile (const char *keyfile_str, const char *full_
 
 	keyfile =  g_key_file_new ();
 	success = g_key_file_load_from_data (keyfile, keyfile_str, strlen (keyfile_str), G_KEY_FILE_NONE, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	nmtst_assert_success (success, error);
 
 	con = nm_keyfile_read (keyfile, base_dir, NULL, NULL, &error);
-	g_assert_no_error (error);
-	g_assert (NM_IS_CONNECTION (con));
-
-	g_key_file_unref (keyfile);
+	nmtst_assert_success (NM_IS_CONNECTION (con), error);
 
 	nm_keyfile_read_ensure_id (con, filename);
 	nm_keyfile_read_ensure_uuid (con, full_filename);
@@ -2034,4 +2147,50 @@ typedef enum {
 
 #endif /* __NM_CONNECTION_H__ */
 
+/*****************************************************************************/
+
+static inline void
+nmtst_keyfile_assert_data (GKeyFile *kf, const char *data, gssize data_len)
+{
+	gs_unref_keyfile GKeyFile *kf2 = NULL;
+	gs_free_error GError *error = NULL;
+	gs_free char *d1 = NULL;
+	gs_free char *d2 = NULL;
+	gboolean success;
+	gsize d1_len;
+	gsize d2_len;
+
+	g_assert (kf);
+	g_assert (data || data_len == 0);
+	g_assert (data_len >= -1);
+
+	d1 = g_key_file_to_data (kf, &d1_len, &error);
+	nmtst_assert_success (d1, error);
+
+	if (data_len == -1) {
+		g_assert_cmpint (strlen (d1), ==, d1_len);
+		data_len = strlen (data);
+		g_assert_cmpstr (d1, ==, data);
+	}
+
+	g_assert_cmpmem (d1, d1_len, data, (gsize) data_len);
+
+	/* also check that we can re-generate the same keyfile from the data. */
+
+	kf2 = g_key_file_new ();
+	success = g_key_file_load_from_data (kf2,
+	                                     d1,
+	                                     d1_len,
+	                                     G_KEY_FILE_NONE,
+	                                     &error);
+	nmtst_assert_success (success, error);
+
+	d2 = g_key_file_to_data (kf2, &d2_len, &error);
+	nmtst_assert_success (d2, error);
+
+	g_assert_cmpmem (d2, d2_len, d1, d1_len);
+}
+
+/*****************************************************************************/
+
 #endif /* __NM_TEST_UTILS_H__ */
diff --git a/shared/nm-utils/nm-time-utils.c b/shared/nm-utils/nm-time-utils.c
new file mode 100644
index 00000000..ae526c34
--- /dev/null
+++ b/shared/nm-utils/nm-time-utils.c
@@ -0,0 +1,273 @@
+/* NetworkManager -- Network link manager
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-time-utils.h"
+
+/*****************************************************************************/
+
+typedef struct {
+	/* the offset to the native clock, in seconds. */
+	gint64 offset_sec;
+	clockid_t clk_id;
+} GlobalState;
+
+static const GlobalState *volatile p_global_state;
+
+static const GlobalState *
+_t_init_global_state (void)
+{
+	static GlobalState global_state = { };
+	static gsize init_once = 0;
+	const GlobalState *p;
+	clockid_t clk_id;
+	struct timespec tp;
+	gint64 offset_sec;
+	int r;
+
+	clk_id = CLOCK_BOOTTIME;
+	r = clock_gettime (clk_id, &tp);
+	if (r == -1 && errno == EINVAL) {
+		clk_id = CLOCK_MONOTONIC;
+		r = clock_gettime (clk_id, &tp);
+	}
+
+	/* The only failure we tolerate is that CLOCK_BOOTTIME is not supported.
+	 * Other than that, we rely on kernel to not fail on this. */
+	g_assert (r == 0);
+	g_assert (tp.tv_nsec >= 0 && tp.tv_nsec < NM_UTILS_NS_PER_SECOND);
+
+	/* Calculate an offset for the time stamp.
+	 *
+	 * We always want positive values, because then we can initialize
+	 * a timestamp with 0 and be sure, that it will be less then any
+	 * value nm_utils_get_monotonic_timestamp_*() might return.
+	 * For this to be true also for nm_utils_get_monotonic_timestamp_s() at
+	 * early boot, we have to shift the timestamp to start counting at
+	 * least from 1 second onward.
+	 *
+	 * Another advantage of shifting is, that this way we make use of the whole 31 bit
+	 * range of signed int, before the time stamp for nm_utils_get_monotonic_timestamp_s()
+	 * wraps (~68 years).
+	 **/
+	offset_sec = (- ((gint64) tp.tv_sec)) + 1;
+
+	if (!g_once_init_enter (&init_once)) {
+		/* there was a race. We expect the pointer to be fully initialized now. */
+		p = g_atomic_pointer_get (&p_global_state);
+		g_assert (p);
+		return p;
+	}
+
+	global_state.offset_sec = offset_sec;
+	global_state.clk_id = clk_id;
+	p = &global_state;
+	g_atomic_pointer_set (&p_global_state, p);
+	g_once_init_leave (&init_once, 1);
+
+	_nm_utils_monotonic_timestamp_initialized (&tp,
+	                                           p->offset_sec,
+	                                           p->clk_id == CLOCK_BOOTTIME);
+
+	return p;
+}
+
+#define _t_get_global_state() \
+	({ \
+		const GlobalState *_p; \
+		\
+		_p = g_atomic_pointer_get (&p_global_state); \
+		(G_LIKELY (_p) ? _p : _t_init_global_state ()); \
+	})
+
+#define _t_clock_gettime_eval(p, tp) \
+	({ \
+		struct timespec *const _tp = (tp); \
+		const GlobalState *const _p2 = (p); \
+		int _r; \
+		\
+		nm_assert (_tp); \
+		\
+		_r = clock_gettime (_p2->clk_id, _tp); \
+		\
+		nm_assert (_r == 0); \
+		nm_assert (_tp->tv_nsec >= 0 && _tp->tv_nsec < NM_UTILS_NS_PER_SECOND); \
+		\
+		_p2; \
+	})
+
+#define _t_clock_gettime(tp) \
+	_t_clock_gettime_eval (_t_get_global_state (), 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)
+{
+	const GlobalState *p;
+	struct timespec tp;
+
+	p = _t_clock_gettime (&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) + p->offset_sec) * NM_UTILS_NS_PER_SECOND +
+	       tp.tv_nsec;
+}
+
+/**
+ * nm_utils_get_monotonic_timestamp_us:
+ *
+ * Returns: a monotonically increasing time stamp in microseconds,
+ * 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_us (void)
+{
+	const GlobalState *p;
+	struct timespec tp;
+
+	p = _t_clock_gettime (&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) + p->offset_sec) * ((gint64) G_USEC_PER_SEC) +
+	       (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/G_USEC_PER_SEC));
+}
+
+/**
+ * nm_utils_get_monotonic_timestamp_ms:
+ *
+ * Returns: a monotonically increasing time stamp in milliseconds,
+ * 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_ms (void)
+{
+	const GlobalState *p;
+	struct timespec tp;
+
+	p = _t_clock_gettime (&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) + p->offset_sec) * ((gint64) 1000) +
+	       (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000));
+}
+
+/**
+ * nm_utils_get_monotonic_timestamp_s:
+ *
+ * Returns: nm_utils_get_monotonic_timestamp_ms() in seconds (throwing
+ * away sub second parts). The returned value will always be positive.
+ *
+ * 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)
+{
+	const GlobalState *p;
+	struct timespec tp;
+
+	p = _t_clock_gettime (&tp);
+
+	return (((gint64) tp.tv_sec) + p->offset_sec);
+}
+
+/**
+ * nm_utils_monotonic_timestamp_as_boottime:
+ * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME.
+ * @timestamp_ns_per_tick: How many nano seconds make one unit of @timestamp? E.g. if
+ * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; @timestamp in nano
+ * seconds, pass 1; @timestamp in milli seconds, pass %NM_UTILS_NS_PER_SECOND/1000; etc.
+ *
+ * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime().
+ * The unit is the same as the passed in @timestamp basd on @timestamp_ns_per_tick.
+ * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds.
+ * If @timestamp is a non-positive, it returns -1. Note that a (valid) monotonic-timestamp
+ * is always positive.
+ *
+ * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC.
+ **/
+gint64
+nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_per_tick)
+{
+	const GlobalState *p;
+	gint64 offset;
+
+	/* only support ns-per-tick being a multiple of 10. */
+	g_return_val_if_fail (timestamp_ns_per_tick == 1
+	                      || (timestamp_ns_per_tick > 0 &&
+	                          timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND &&
+	                          timestamp_ns_per_tick % 10 == 0),
+	                      -1);
+
+	/* Check that the timestamp is in a valid range. */
+	g_return_val_if_fail (timestamp >= 0, -1);
+
+	/* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful
+	 * value (because he has no idea what these timestamps would be). That would be a bug. */
+	nm_assert (g_atomic_pointer_get (&p_global_state));
+
+	p = _t_get_global_state ();
+
+	/* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */
+	offset = p->offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick);
+
+	/* check for overflow. */
+	g_return_val_if_fail (offset > 0 || timestamp < G_MAXINT64 + offset, G_MAXINT64);
+
+	return timestamp - offset;
+}
diff --git a/shared/nm-utils/nm-time-utils.h b/shared/nm-utils/nm-time-utils.h
new file mode 100644
index 00000000..7e4f4f25
--- /dev/null
+++ b/shared/nm-utils/nm-time-utils.h
@@ -0,0 +1,45 @@
+/* NetworkManager -- Network link manager
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2018 Red Hat, Inc.
+ */
+
+#ifndef __NM_TIME_UTILS_H__
+#define __NM_TIME_UTILS_H__
+
+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);
+gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns);
+
+static inline gint64
+nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now)
+{
+	return    (*cache_now)
+	       ?: (*cache_now = nm_utils_get_monotonic_timestamp_ns ());
+}
+
+struct timespec;
+
+/* this function must be implemented to handle the notification when
+ * the first monotonic-timestamp is fetched. */
+extern void _nm_utils_monotonic_timestamp_initialized (const struct timespec *tp,
+                                                       gint64 offset_sec,
+                                                       gboolean is_boottime);
+
+#endif /* __NM_TIME_UTILS_H__ */
diff --git a/shared/nm-utils/nm-udev-utils.c b/shared/nm-utils/nm-udev-utils.c
index 709f7590..5d0919b3 100644
--- a/shared/nm-utils/nm-udev-utils.c
+++ b/shared/nm-utils/nm-udev-utils.c
@@ -241,26 +241,23 @@ nm_udev_client_new (const char *const*subsystems,
 		if (self->subsystems) {
 			/* install subsystem filters to only wake up for certain events */
 			for (n = 0; self->subsystems[n]; n++) {
-				if (self->monitor) {
-					gs_free char *to_free = NULL;
-					const char *subsystem;
-					const char *devtype;
-
-					_subsystem_split (self->subsystems[n], &subsystem, &devtype, &to_free);
-					udev_monitor_filter_add_match_subsystem_devtype (self->monitor, subsystem, devtype);
-				}
+				gs_free char *to_free = NULL;
+				const char *subsystem;
+				const char *devtype;
+
+				_subsystem_split (self->subsystems[n], &subsystem, &devtype, &to_free);
+				udev_monitor_filter_add_match_subsystem_devtype (self->monitor, subsystem, devtype);
 			}
 
 			/* listen to events, and buffer them */
-			if (self->monitor) {
-				udev_monitor_enable_receiving (self->monitor);
-				channel = g_io_channel_unix_new (udev_monitor_get_fd (self->monitor));
-				self->watch_source = g_io_create_watch (channel, G_IO_IN);
-				g_io_channel_unref (channel);
-				g_source_set_callback (self->watch_source, (GSourceFunc)(void (*) (void)) monitor_event, self, NULL);
-				g_source_attach (self->watch_source, g_main_context_get_thread_default ());
-				g_source_unref (self->watch_source);
-			}
+			udev_monitor_set_receive_buffer_size (self->monitor, 4*1024*1024);
+			udev_monitor_enable_receiving (self->monitor);
+			channel = g_io_channel_unix_new (udev_monitor_get_fd (self->monitor));
+			self->watch_source = g_io_create_watch (channel, G_IO_IN);
+			g_io_channel_unref (channel);
+			g_source_set_callback (self->watch_source, (GSourceFunc)(void (*) (void)) monitor_event, self, NULL);
+			g_source_attach (self->watch_source, g_main_context_get_thread_default ());
+			g_source_unref (self->watch_source);
 		}
 	}
 
diff --git a/shared/nm-utils/nm-vpn-plugin-utils.c b/shared/nm-utils/nm-vpn-plugin-utils.c
index 772aa39a..353a2817 100644
--- a/shared/nm-utils/nm-vpn-plugin-utils.c
+++ b/shared/nm-utils/nm-vpn-plugin-utils.c
@@ -16,7 +16,7 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301 USA.
  *
- * Copyright 2016 Red Hat, Inc.
+ * Copyright 2016,2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -44,14 +44,37 @@ nm_vpn_plugin_utils_load_editor (const char *module_name,
 		char *factory_name;
 	} cached = { 0 };
 	NMVpnEditor *editor;
+	gs_free char *module_path = NULL;
+	gs_free char *dirname = NULL;
+	Dl_info plugin_info;
 
-	g_return_val_if_fail (module_name && g_path_is_absolute (module_name), NULL);
+	g_return_val_if_fail (module_name, NULL);
 	g_return_val_if_fail (factory_name && factory_name[0], NULL);
 	g_return_val_if_fail (editor_factory, NULL);
 	g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (editor_plugin), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
 
+	if (!g_path_is_absolute (module_name)) {
+		/*
+		 * Load an editor from the same directory this plugin is in.
+		 * Ideally, we'd get our .so name from the NMVpnEditorPlugin if it
+		 * would just have a property with it...
+		 */
+		if (!dladdr(nm_vpn_plugin_utils_load_editor, &plugin_info)) {
+			/* Really a "can not happen" scenario. */
+			g_set_error (error,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_FAILED,
+			             _("unable to get editor plugin name: %s"), dlerror ());
+		}
+
+		dirname = g_path_get_dirname (plugin_info.dli_fname);
+		module_path = g_build_filename (dirname, module_name, NULL);
+	} else {
+		module_path = g_strdup (module_name);
+	}
+
 	/* we really expect this function to be called with unchanging @module_name
 	 * and @factory_name. And we only want to load the module once, hence it would
 	 * be more complicated to accept changing @module_name/@factory_name arguments.
@@ -71,18 +94,18 @@ nm_vpn_plugin_utils_load_editor (const char *module_name,
 		gpointer factory;
 		void *dl_module;
 
-		dl_module = dlopen (module_name, RTLD_LAZY | RTLD_LOCAL);
+		dl_module = dlopen (module_path, RTLD_LAZY | RTLD_LOCAL);
 		if (!dl_module) {
-			if (!g_file_test (module_name, G_FILE_TEST_EXISTS)) {
+			if (!g_file_test (module_path, G_FILE_TEST_EXISTS)) {
 				g_set_error (error,
 				             G_FILE_ERROR,
 				             G_FILE_ERROR_NOENT,
-				             _("missing plugin file \"%s\""), module_name);
+				             _("missing plugin file \"%s\""), module_path);
 				return NULL;
 			}
 			g_set_error (error,
-			             NM_CONNECTION_ERROR,
-			             NM_CONNECTION_ERROR_FAILED,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_FAILED,
 			             _("cannot load editor plugin: %s"), dlerror ());
 			return NULL;
 		}
@@ -90,8 +113,8 @@ nm_vpn_plugin_utils_load_editor (const char *module_name,
 		factory = dlsym (dl_module, factory_name);
 		if (!factory) {
 			g_set_error (error,
-			             NM_CONNECTION_ERROR,
-			             NM_CONNECTION_ERROR_FAILED,
+			             NM_VPN_PLUGIN_ERROR,
+			             NM_VPN_PLUGIN_ERROR_FAILED,
 			             _("cannot load factory %s from plugin: %s"),
 			             factory_name, dlerror ());
 			dlclose (dl_module);
@@ -116,8 +139,8 @@ nm_vpn_plugin_utils_load_editor (const char *module_name,
 	if (!editor) {
 		if (error && !*error ) {
 			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_FAILED,
+			                     NM_VPN_PLUGIN_ERROR,
+			                     NM_VPN_PLUGIN_ERROR_FAILED,
 			                     _("unknown error creating editor instance"));
 			g_return_val_if_reached (NULL);
 		}
@@ -127,4 +150,3 @@ nm_vpn_plugin_utils_load_editor (const char *module_name,
 	g_return_val_if_fail (NM_IS_VPN_EDITOR (editor), NULL);
 	return editor;
 }
-
diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c
new file mode 100644
index 00000000..7d22e56d
--- /dev/null
+++ b/shared/nm-utils/tests/test-shared-general.c
@@ -0,0 +1,221 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#define NM_TEST_UTILS_NO_LIBNM 1
+
+#include "nm-default.h"
+
+#include "nm-utils/nm-time-utils.h"
+#include "nm-utils/nm-random-utils.h"
+
+#include "nm-utils/nm-test-utils.h"
+
+/*****************************************************************************/
+
+static int _monotonic_timestamp_initialized;
+
+void
+_nm_utils_monotonic_timestamp_initialized (const struct timespec *tp,
+                                           gint64 offset_sec,
+                                           gboolean is_boottime)
+{
+	g_assert (!_monotonic_timestamp_initialized);
+	_monotonic_timestamp_initialized = 1;
+}
+
+/*****************************************************************************/
+
+static void
+test_monotonic_timestamp (void)
+{
+	g_assert (nm_utils_get_monotonic_timestamp_s () > 0);
+	g_assert (_monotonic_timestamp_initialized);
+}
+
+/*****************************************************************************/
+
+static void
+test_nmhash (void)
+{
+	int rnd;
+
+	nm_utils_random_bytes (&rnd, sizeof (rnd));
+
+	g_assert (nm_hash_val (555, 4) != 0);
+}
+
+/*****************************************************************************/
+
+static const char *
+_make_strv_foo (void)
+{
+	return "foo";
+}
+
+static const char *const*const _tst_make_strv_1 = NM_MAKE_STRV ("1", "2");
+
+static void
+test_make_strv (void)
+{
+	const char *const*v1a = NM_MAKE_STRV ("a");
+	const char *const*v1b = NM_MAKE_STRV ("a", );
+	const char *const*v2a = NM_MAKE_STRV ("a", "b");
+	const char *const*v2b = NM_MAKE_STRV ("a", "b", );
+	const char *const v3[] = { "a", "b", };
+	const char *const*v4b = NM_MAKE_STRV ("a", _make_strv_foo (), );
+
+	g_assert (NM_PTRARRAY_LEN (v1a) == 1);
+	g_assert (NM_PTRARRAY_LEN (v1b) == 1);
+	g_assert (NM_PTRARRAY_LEN (v2a) == 2);
+	g_assert (NM_PTRARRAY_LEN (v2b) == 2);
+
+	g_assert (NM_PTRARRAY_LEN (_tst_make_strv_1) == 2);
+	g_assert_cmpstr (_tst_make_strv_1[0], ==, "1");
+	g_assert_cmpstr (_tst_make_strv_1[1], ==, "2");
+	/* writing the static read-only variable leads to crash .*/
+	//((char **) _tst_make_strv_1)[0] = NULL;
+	//((char **) _tst_make_strv_1)[2] = "c";
+
+	G_STATIC_ASSERT_EXPR (G_N_ELEMENTS (v3) == 2);
+
+	g_assert (NM_PTRARRAY_LEN (v4b) == 2);
+
+	G_STATIC_ASSERT_EXPR (G_N_ELEMENTS (NM_MAKE_STRV ("a", "b"  )) == 3);
+	G_STATIC_ASSERT_EXPR (G_N_ELEMENTS (NM_MAKE_STRV ("a", "b", )) == 3);
+
+	nm_strquote_a (300, "");
+}
+
+/*****************************************************************************/
+
+typedef enum {
+	TEST_NM_STRDUP_ENUM_m1 = -1,
+	TEST_NM_STRDUP_ENUM_3  = 3,
+} TestNMStrdupIntEnum;
+
+static void
+test_nm_strdup_int (void)
+{
+#define _NM_STRDUP_INT_TEST(num, str) \
+	G_STMT_START { \
+		gs_free char *_s1 = NULL; \
+		\
+		_s1 = nm_strdup_int ((num)); \
+		\
+		g_assert (_s1); \
+		g_assert_cmpstr (_s1, ==, str); \
+	} G_STMT_END
+
+#define _NM_STRDUP_INT_TEST_TYPED(type, num) \
+	G_STMT_START { \
+		type _num = ((type) num); \
+		\
+		_NM_STRDUP_INT_TEST (_num, G_STRINGIFY (num)); \
+	} G_STMT_END
+
+	_NM_STRDUP_INT_TEST_TYPED (char, 0);
+	_NM_STRDUP_INT_TEST_TYPED (char, 1);
+	_NM_STRDUP_INT_TEST_TYPED (guint8, 0);
+	_NM_STRDUP_INT_TEST_TYPED (gint8, 25);
+	_NM_STRDUP_INT_TEST_TYPED (char, 47);
+	_NM_STRDUP_INT_TEST_TYPED (short, 47);
+	_NM_STRDUP_INT_TEST_TYPED (int, 47);
+	_NM_STRDUP_INT_TEST_TYPED (long, 47);
+	_NM_STRDUP_INT_TEST_TYPED (unsigned char, 47);
+	_NM_STRDUP_INT_TEST_TYPED (unsigned short, 47);
+	_NM_STRDUP_INT_TEST_TYPED (unsigned, 47);
+	_NM_STRDUP_INT_TEST_TYPED (unsigned long, 47);
+	_NM_STRDUP_INT_TEST_TYPED (gint64, 9223372036854775807);
+	_NM_STRDUP_INT_TEST_TYPED (gint64, -9223372036854775807);
+	_NM_STRDUP_INT_TEST_TYPED (guint64, 0);
+	_NM_STRDUP_INT_TEST_TYPED (guint64, 9223372036854775807);
+
+	_NM_STRDUP_INT_TEST (TEST_NM_STRDUP_ENUM_m1, "-1");
+	_NM_STRDUP_INT_TEST (TEST_NM_STRDUP_ENUM_3,  "3");
+}
+
+/*****************************************************************************/
+
+static void
+test_nm_strndup_a (void)
+{
+	int run;
+
+	for (run = 0; run < 20; run++) {
+		gs_free char *input = NULL;
+		char ch;
+		gsize i, l;
+
+		input = g_strnfill (nmtst_get_rand_int () % 20, 'x');
+
+		for (i = 0; input[i]; i++) {
+			while ((ch = ((char) nmtst_get_rand_int ())) == '\0') {
+				/* repeat. */
+			}
+			input[i] = ch;
+		}
+
+		{
+			gs_free char *dup_free = NULL;
+			const char *dup;
+
+			l = strlen (input) + 1;
+			dup = nm_strndup_a (10, input, l - 1, &dup_free);
+			g_assert_cmpstr (dup, ==, input);
+			if (strlen (dup) < 10)
+				g_assert (!dup_free);
+			else
+				g_assert (dup == dup_free);
+		}
+
+		{
+			gs_free char *dup_free = NULL;
+			const char *dup;
+
+			l = nmtst_get_rand_int () % 23;
+			dup = nm_strndup_a (10, input, l, &dup_free);
+			g_assert (strncmp (dup, input, l) == 0);
+			g_assert (strlen (dup) <= l);
+			if (l < 10)
+				g_assert (!dup_free);
+			else
+				g_assert (dup == dup_free);
+			if (strlen (input) < l)
+				g_assert (nm_utils_memeqzero (&dup[strlen (input)], l - strlen (input)));
+		}
+	}
+}
+
+/*****************************************************************************/
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+	nmtst_init (&argc, &argv, TRUE);
+
+	g_test_add_func ("/general/test_monotonic_timestamp", test_monotonic_timestamp);
+	g_test_add_func ("/general/test_nmhash", test_nmhash);
+	g_test_add_func ("/general/test_nm_make_strv", test_make_strv);
+	g_test_add_func ("/general/test_nm_strdup_int", test_nm_strdup_int);
+	g_test_add_func ("/general/test_nm_strndup_a", test_nm_strndup_a);
+
+	return g_test_run ();
+}
+
diff --git a/shared/nm-utils/unaligned.h b/shared/nm-utils/unaligned.h
index e62188d1..00c17f87 100644
--- a/shared/nm-utils/unaligned.h
+++ b/shared/nm-utils/unaligned.h
@@ -7,37 +7,37 @@
 /* BE */
 
 static inline uint16_t unaligned_read_be16(const void *_u) {
-        const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
+        const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
 
         return be16toh(u->x);
 }
 
 static inline uint32_t unaligned_read_be32(const void *_u) {
-        const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
+        const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
 
         return be32toh(u->x);
 }
 
 static inline uint64_t unaligned_read_be64(const void *_u) {
-        const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
+        const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
 
         return be64toh(u->x);
 }
 
 static inline void unaligned_write_be16(void *_u, uint16_t a) {
-        struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
+        struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
 
         u->x = be16toh(a);
 }
 
 static inline void unaligned_write_be32(void *_u, uint32_t a) {
-        struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
+        struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
 
         u->x = be32toh(a);
 }
 
 static inline void unaligned_write_be64(void *_u, uint64_t a) {
-        struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
+        struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
 
         u->x = be64toh(a);
 }
@@ -45,37 +45,37 @@ static inline void unaligned_write_be64(void *_u, uint64_t a) {
 /* LE */
 
 static inline uint16_t unaligned_read_le16(const void *_u) {
-        const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
+        const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
 
         return le16toh(u->x);
 }
 
 static inline uint32_t unaligned_read_le32(const void *_u) {
-        const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
+        const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
 
         return le32toh(u->x);
 }
 
 static inline uint64_t unaligned_read_le64(const void *_u) {
-        const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
+        const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
 
         return le64toh(u->x);
 }
 
 static inline void unaligned_write_le16(void *_u, uint16_t a) {
-        struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
+        struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
 
         u->x = le16toh(a);
 }
 
 static inline void unaligned_write_le32(void *_u, uint32_t a) {
-        struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
+        struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
 
         u->x = le32toh(a);
 }
 
 static inline void unaligned_write_le64(void *_u, uint64_t a) {
-        struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
+        struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
 
         u->x = le64toh(a);
 }