summary refs log tree commit diff
path: root/shared/nm-utils
diff options
context:
space:
mode:
Diffstat (limited to 'shared/nm-utils')
-rw-r--r--shared/nm-utils/c-list-util.c88
-rw-r--r--shared/nm-utils/c-list-util.h2
-rw-r--r--shared/nm-utils/c-list.h397
-rw-r--r--shared/nm-utils/nm-c-list.h81
-rw-r--r--shared/nm-utils/nm-compat.c95
-rw-r--r--shared/nm-utils/nm-compat.h53
-rw-r--r--shared/nm-utils/nm-dedup-multi.c10
-rw-r--r--shared/nm-utils/nm-dedup-multi.h4
-rw-r--r--shared/nm-utils/nm-enum-utils.c47
-rw-r--r--shared/nm-utils/nm-enum-utils.h5
-rw-r--r--shared/nm-utils/nm-glib.h382
-rw-r--r--shared/nm-utils/nm-hash-utils.c134
-rw-r--r--shared/nm-utils/nm-hash-utils.h13
-rw-r--r--shared/nm-utils/nm-jansson.h46
-rw-r--r--shared/nm-utils/nm-macros-internal.h245
-rw-r--r--shared/nm-utils/nm-obj.h2
-rw-r--r--shared/nm-utils/nm-shared-utils.c244
-rw-r--r--shared/nm-utils/nm-shared-utils.h166
-rw-r--r--shared/nm-utils/nm-test-utils.h70
-rw-r--r--shared/nm-utils/nm-udev-utils.c2
-rw-r--r--shared/nm-utils/siphash24.c17
-rw-r--r--shared/nm-utils/unaligned.h61
22 files changed, 1180 insertions, 984 deletions
diff --git a/shared/nm-utils/c-list-util.c b/shared/nm-utils/c-list-util.c
index 070323c6..44ca26a5 100644
--- a/shared/nm-utils/c-list-util.c
+++ b/shared/nm-utils/c-list-util.c
@@ -58,39 +58,35 @@ c_list_relink (CList *lst)
 /*****************************************************************************/
 
 static CList *
-_c_list_sort (CList *ls,
-              CListSortCmp cmp,
-              const void *user_data)
+_c_list_srt_split (CList *ls)
 {
-	CList *ls1, *ls2;
-	CList head;
+	CList *ls2;
 
-	if (!ls->next)
-		return ls;
-
-	/* split list in two halfs @ls1 and @ls2. */
-	ls1 = ls;
 	ls2 = ls;
 	ls = ls->next;
-	while (ls) {
+	if (!ls)
+		return NULL;
+	do {
 		ls = ls->next;
 		if (!ls)
 			break;
 		ls = ls->next;
 		ls2 = ls2->next;
-	}
-	ls = ls2;
-	ls2 = ls->next;
-	ls->next = NULL;
-
-	/* recurse */
-	ls1 = _c_list_sort (ls1, cmp, user_data);
-	if (!ls2)
-		return ls1;
+	} while (ls);
+	ls = ls2->next;
+	ls2->next = NULL;
+	return ls;
+}
 
-	ls2 = _c_list_sort (ls2, cmp, user_data);
+static CList *
+_c_list_srt_merge (CList *ls1,
+                   CList *ls2,
+                   CListSortCmp cmp,
+                   const void *user_data)
+{
+	CList *ls;
+	CList head;
 
-	/* merge */
 	ls = &head;
 	for (;;) {
 		/* while invoking the @cmp function, the list
@@ -115,6 +111,54 @@ _c_list_sort (CList *ls,
 	return head.next;
 }
 
+typedef struct {
+	CList *ls1;
+	CList *ls2;
+	char ls1_sorted;
+} SortStack;
+
+static CList *
+_c_list_sort (CList *ls,
+              CListSortCmp cmp,
+              const void *user_data)
+{
+	/* reserve a huge stack-size. We need roughly log2(n) entries, hence this
+	 * is much more we will ever need. We don't guard for stack-overflow either. */
+	SortStack stack_arr[70];
+	SortStack *stack_head = stack_arr;
+
+	stack_arr[0].ls1 = ls;
+
+	/* A simple top-down, non-recursive, stable merge-sort.
+	 *
+	 * Maybe natural merge-sort would be better, to do better for
+	 * partially sorted lists. */
+_split:
+	stack_head[0].ls2 = _c_list_srt_split (stack_head[0].ls1);
+	if (stack_head[0].ls2) {
+		stack_head[0].ls1_sorted = 0;
+		stack_head[1].ls1 = stack_head[0].ls1;
+		stack_head++;
+		goto _split;
+	}
+
+_backtrack:
+	if (stack_head == stack_arr)
+		return stack_arr[0].ls1;
+
+	stack_head--;
+	if (!stack_head[0].ls1_sorted) {
+		stack_head[0].ls1 = stack_head[1].ls1;
+		stack_head[0].ls1_sorted = 1;
+		stack_head[1].ls1 = stack_head[0].ls2;
+		stack_head++;
+		goto _split;
+	}
+
+	stack_head[0].ls1 = _c_list_srt_merge (stack_head[0].ls1, stack_head[1].ls1, cmp, user_data);
+	goto _backtrack;
+}
+
 /**
  * c_list_sort_headless:
  * @lst: the list.
diff --git a/shared/nm-utils/c-list-util.h b/shared/nm-utils/c-list-util.h
index 199583cf..e87f1c19 100644
--- a/shared/nm-utils/c-list-util.h
+++ b/shared/nm-utils/c-list-util.h
@@ -22,7 +22,7 @@
 #ifndef __C_LIST_UTIL_H__
 #define __C_LIST_UTIL_H__
 
-#include "c-list.h"
+#include "c-list/src/c-list.h"
 
 /*****************************************************************************/
 
diff --git a/shared/nm-utils/c-list.h b/shared/nm-utils/c-list.h
deleted file mode 100644
index a3c4053b..00000000
--- a/shared/nm-utils/c-list.h
+++ /dev/null
@@ -1,397 +0,0 @@
-#pragma once
-
-/*
- * Circular Double Linked List Implementation in Standard ISO-C11
- *
- * This implements a generic circular double linked list. List entries must
- * embed the CList object, which provides pointers to the next and previous
- * element. Insertion and removal can be done in O(1) due to the double links.
- * Furthermore, the list is circular, thus allows access to front/tail in O(1)
- * as well, even if you only have a single head pointer (which is not how the
- * list is usually operated, though).
- *
- * Note that you are free to use the list implementation without a head
- * pointer. However, usual operation uses a single CList object as head, which
- * is itself linked in the list and as such must be identified as list head.
- * This allows very simply list operations and avoids a lot of special cases.
- * Most importantly, you can unlink entries without requiring a head pointer.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stddef.h>
-
-typedef struct CList CList;
-
-/**
- * struct CList - Entry of a circular double linked list
- * @next:               next entry
- * @prev:               previous entry
- *
- * Each entry in a list must embed a CList object. This object contains
- * pointers to its next and previous elements, which can be freely accessed by
- * the API user at any time. Note that the list is circular, and the list head
- * is linked in the list as well.
- *
- * The list head must be initialized via C_LIST_INIT before use. There is no
- * reason to initialize entry objects before linking them. However, if you need
- * a boolean state that tells you whether the entry is linked or not, you should
- * initialize the entry via C_LIST_INIT as well.
- */
-struct CList {
-        CList *next;
-        CList *prev;
-};
-
-#define C_LIST_INIT(_var) { .next = &(_var), .prev = &(_var) }
-
-/**
- * c_list_init() - initialize list entry
- * @what:               list entry to initialize
- */
-static inline void c_list_init(CList *what) {
-        *what = (CList)C_LIST_INIT(*what);
-}
-
-/**
- * c_list_entry() - get parent container of list entry
- * @_what:              list entry, or NULL
- * @_t:                 type of parent container
- * @_m:                 member name of list entry in @_t
- *
- * If the list entry @_what is embedded into a surrounding structure, this will
- * turn the list entry pointer @_what into a pointer to the parent container
- * (using offsetof(3), or sometimes called container_of(3)).
- *
- * If @_what is NULL, this will also return NULL.
- *
- * Return: Pointer to parent container, or NULL.
- */
-#define c_list_entry(_what, _t, _m) \
-        ((_t *)(void *)(((unsigned long)(void *)(_what) ?: \
-                         offsetof(_t, _m)) - offsetof(_t, _m)))
-
-/**
- * c_list_is_linked() - check whether an entry is linked
- * @what:               entry to check, or NULL
- *
- * Return: True if @what is linked in a list, false if not.
- */
-static inline _Bool c_list_is_linked(const CList *what) {
-        return what && what->next != what;
-}
-
-/**
- * c_list_is_empty() - check whether a list is empty
- * @list:               list to check, or NULL
- *
- * Return: True if @list is empty, false if not.
- */
-static inline _Bool c_list_is_empty(const CList *list) {
-        return !list || !c_list_is_linked(list);
-}
-
-/**
- * c_list_link_before() - link entry into list
- * @where:              linked list entry used as anchor
- * @what:               entry to link
- *
- * This links @what directly in front of @where. @where can either be a list
- * head or any entry in the list.
- *
- * If @where points to the list head, this effectively links @what as new tail
- * element. Hence, the macro c_list_link_tail() is an alias to this.
- *
- * @what is not inspected prior to being linked. Hence, it better not be linked
- * into another list, or the other list will be corrupted.
- */
-static inline void c_list_link_before(CList *where, CList *what) {
-        CList *prev = where->prev, *next = where;
-
-        next->prev = what;
-        what->next = next;
-        what->prev = prev;
-        prev->next = what;
-}
-#define c_list_link_tail(_list, _what) c_list_link_before((_list), (_what))
-
-/**
- * c_list_link_after() - link entry into list
- * @where:              linked list entry used as anchor
- * @what:               entry to link
- *
- * This links @what directly after @where. @where can either be a list head or
- * any entry in the list.
- *
- * If @where points to the list head, this effectively links @what as new front
- * element. Hence, the macro c_list_link_front() is an alias to this.
- *
- * @what is not inspected prior to being linked. Hence, it better not be linked
- * into another list, or the other list will be corrupted.
- */
-static inline void c_list_link_after(CList *where, CList *what) {
-        CList *prev = where, *next = where->next;
-
-        next->prev = what;
-        what->next = next;
-        what->prev = prev;
-        prev->next = what;
-}
-#define c_list_link_front(_list, _what) c_list_link_after((_list), (_what))
-
-/**
- * c_list_unlink_stale() - unlink element from list
- * @what:               element to unlink
- *
- * This unlinks @what. If @what was initialized via C_LIST_INIT(), it has no
- * effect. If @what was never linked, nor initialized, behavior is undefined.
- *
- * Note that this does not modify @what. It just modifies the previous and next
- * elements in the list to no longer reference @what. If you want to make sure
- * @what is re-initialized after removal, use c_list_unlink().
- */
-static inline void c_list_unlink_stale(CList *what) {
-        CList *prev = what->prev, *next = what->next;
-
-        next->prev = prev;
-        prev->next = next;
-}
-
-/**
- * c_list_unlink() - unlink element from list and re-initialize
- * @what:               element to unlink
- *
- * This is like c_list_unlink_stale() but re-initializes @what after removal.
- */
-static inline void c_list_unlink(CList *what) {
-        /* condition is not needed, but avoids STOREs in fast-path */
-        if (c_list_is_linked(what)) {
-                c_list_unlink_stale(what);
-                *what = (CList)C_LIST_INIT(*what);
-        }
-}
-
-/**
- * c_list_swap() - exchange the contents of two lists
- * @list1:      the list to operate on
- * @list2:      the list to operate on
- *
- * This replaces the contents of the list @list1 with the contents
- * of @list2, and vice versa.
- */
-static inline void c_list_swap(CList *list1, CList *list2) {
-        CList t;
-
-        /* make neighbors of list1 point to list2, and vice versa */
-        t = *list1;
-        t.next->prev = list2;
-        t.prev->next = list2;
-        t = *list2;
-        t.next->prev = list1;
-        t.prev->next = list1;
-
-        /* swap list1 and list2 now that their neighbors were fixed up */
-        t = *list1;
-        *list1 = *list2;
-        *list2 = t;
-}
-
-/**
- * c_list_splice() - splice one list into another
- * @target:     the list to splice into
- * @source:     the list to splice
- *
- * This removes all the entries from @source and splice them into @target.
- * The order of the two lists is preserved and the source is appended
- * to the end of target.
- *
- * On return, the source list will be empty.
- */
-static inline void c_list_splice(CList *target, CList *source) {
-        if (!c_list_is_empty(source)) {
-                /* attach the front of @source to the tail of @target */
-                source->next->prev = target->prev;
-                target->prev->next = source->next;
-
-                /* attach the tail of @source to the front of @target */
-                source->prev->next = target;
-                target->prev = source->prev;
-
-                /* clear source */
-                *source = (CList)C_LIST_INIT(*source);
-        }
-}
-
-/**
- * c_list_for_each() - loop over all list entries
- * @_iter:              iterator to use
- * @_list:              list to loop over
- *
- * This is a macro to use as for-loop to iterate an entire list. It is meant as
- * convenience macro. Feel free to code your own loop iterator.
- */
-#define c_list_for_each(_iter, _list)                                           \
-        for (_iter = (_list)->next;                                             \
-             (_iter) != (_list);                                                \
-             _iter = (_iter)->next)
-
-
-/**
- * c_list_for_each_safe() - loop over all list entries, safe for removal
- * @_iter:              iterator to use
- * @_safe:              used to store pointer to next element
- * @_list:              list to loop over
- *
- * This is a macro to use as for-loop to iterate an entire list, safe against
- * removal of the current element. It is meant as convenience macro. Feel free
- * to code your own loop iterator.
- *
- * Note that this fetches the next element prior to executing the loop body.
- * This makes it safe against removal of the current entry, but it will go
- * havoc if you remove other list entries. You better not modify anything but
- * the current list entry.
- */
-#define c_list_for_each_safe(_iter, _safe, _list)                               \
-        for (_iter = (_list)->next, _safe = (_iter)->next;                      \
-             (_iter) != (_list);                                                \
-             _iter = (_safe), _safe = (_safe)->next)
-
-/**
- * c_list_for_each_entry() - loop over all list entries
- * @_iter:              iterator to use
- * @_list:              list to loop over
- * @_m:                 member name of CList object in list type
- *
- * This combines c_list_for_each() with c_list_entry(), making it easy to
- * iterate over a list of a specific type.
- */
-#define c_list_for_each_entry(_iter, _list, _m)                                 \
-        for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m);       \
-             &(_iter)->_m != (_list);                                           \
-             _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m))
-
-/**
- * c_list_for_each_entry_safe() - loop over all list entries, safe for removal
- * @_iter:              iterator to use
- * @_safe:              used to store pointer to next element
- * @_list:              list to loop over
- * @_m:                 member name of CList object in list type
- *
- * This combines c_list_for_each_safe() with c_list_entry(), making it easy to
- * iterate over a list of a specific type.
- */
-#define c_list_for_each_entry_safe(_iter, _safe, _list, _m)                     \
-        for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m),       \
-             _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m);    \
-             &(_iter)->_m != (_list);                                           \
-             _iter = (_safe),                                                   \
-             _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m))    \
-
-/**
- * c_list_first() - return pointer to first element, or NULL if empty
- * @list:               list to operate on, or NULL
- *
- * This returns a pointer to the first element, or NULL if empty. This never
- * returns a pointer to the list head.
- *
- * Return: Pointer to first list element, or NULL if empty.
- */
-static inline CList *c_list_first(CList *list) {
-        return c_list_is_empty(list) ? NULL : list->next;
-}
-
-/**
- * c_list_last() - return pointer to last element, or NULL if empty
- * @list:               list to operate on, or NULL
- *
- * This returns a pointer to the last element, or NULL if empty. This never
- * returns a pointer to the list head.
- *
- * Return: Pointer to last list element, or NULL if empty.
- */
-static inline CList *c_list_last(CList *list) {
-        return c_list_is_empty(list) ? NULL : list->prev;
-}
-
-/**
- * c_list_first_entry() - return pointer to first entry, or NULL if empty
- * @_list:              list to operate on, or NULL
- * @_t:                 type of list entries
- * @_m:                 name of CList member in @_t
- *
- * This is like c_list_first(), but also applies c_list_entry() on the result.
- *
- * Return: Pointer to first list entry, or NULL if empty.
- */
-#define c_list_first_entry(_list, _t, _m) \
-        c_list_entry(c_list_first(_list), _t, _m)
-
-/**
- * c_list_last_entry() - return pointer to last entry, or NULL if empty
- * @_list:              list to operate on, or NULL
- * @_t:                 type of list entries
- * @_m:                 name of CList member in @_t
- *
- * This is like c_list_last(), but also applies c_list_entry() on the result.
- *
- * Return: Pointer to last list entry, or NULL if empty.
- */
-#define c_list_last_entry(_list, _t, _m) \
-        c_list_entry(c_list_last(_list), _t, _m)
-
-/**
- * c_list_length() - return number of linked entries, excluding the head
- * @list:               list to operate on
- *
- * Returns the number of entries in the list, excluding the list head @list.
- * That is, for a list that is empty according to c_list_is_empty(), the
- * returned length is 0. This requires to iterate the list and has thus O(n)
- * runtime.
- *
- * Note that this function is meant for debugging purposes only. If you need
- * the list size during normal operation, you should maintain a counter
- * separately.
- *
- * Return: Number of items in @list.
- */
-static inline unsigned long c_list_length(const CList *list) {
-        unsigned long n = 0;
-        const CList *iter;
-
-        c_list_for_each(iter, list)
-                ++n;
-
-        return n;
-}
-
-/**
- * c_list_contains() - check whether an entry is linked in a certain list
- * @list:               list to operate on
- * @what:               entry to look for
- *
- * This checks whether @what is linked into @list. This requires a linear
- * search through the list, as such runs in O(n). Note that the list-head is
- * considered part of the list, and hence this returns true if @what equals
- * @list.
- *
- * Note that this function is meant for debugging purposes, and consistency
- * checks. You should always be aware whether your objects are linked in a
- * specific list.
- *
- * Return: True if @what is in @list, false otherwise.
- */
-static inline _Bool c_list_contains(const CList *list, const CList *what) {
-        const CList *iter;
-
-        c_list_for_each(iter, list)
-                if (what == iter)
-                        return 1;
-
-        return what == list;
-}
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/shared/nm-utils/nm-c-list.h b/shared/nm-utils/nm-c-list.h
new file mode 100644
index 00000000..b43d1441
--- /dev/null
+++ b/shared/nm-utils/nm-c-list.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 2014 Red Hat, Inc.
+ */
+
+#ifndef __NM_C_LIST_H__
+#define __NM_C_LIST_H__
+
+#include "c-list/src/c-list.h"
+
+/*****************************************************************************/
+
+#define nm_c_list_contains_entry(list, what, member) \
+	({ \
+		typeof (what) _what = (what); \
+		\
+		_what && c_list_contains (list, &_what->member); \
+	})
+
+typedef struct {
+	CList lst;
+	void *data;
+} NMCListElem;
+
+static inline NMCListElem *
+nm_c_list_elem_new_stale (void *data)
+{
+	NMCListElem *elem;
+
+	elem = g_slice_new (NMCListElem);
+	elem->data = data;
+	return elem;
+}
+
+static inline void *
+nm_c_list_elem_get (CList *lst)
+{
+	if (!lst)
+		return NULL;
+	return c_list_entry (lst, NMCListElem, lst)->data;
+}
+
+static inline void
+nm_c_list_elem_free (NMCListElem *elem)
+{
+	if (elem) {
+		c_list_unlink_stale (&elem->lst);
+		g_slice_free (NMCListElem, elem);
+	}
+}
+
+static inline void
+nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn)
+{
+	NMCListElem *elem;
+
+	while ((elem = c_list_first_entry (head, NMCListElem, lst))) {
+		if (free_fcn)
+			free_fcn (elem->data);
+		c_list_unlink_stale (&elem->lst);
+		g_slice_free (NMCListElem, elem);
+	}
+}
+
+#endif /* __NM_C_LIST_H__ */
diff --git a/shared/nm-utils/nm-compat.c b/shared/nm-utils/nm-compat.c
new file mode 100644
index 00000000..90328c06
--- /dev/null
+++ b/shared/nm-utils/nm-compat.c
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-compat.h"
+
+/*****************************************************************************/
+
+static void
+_get_keys_cb (const char *key, const char *val, gpointer user_data)
+{
+	GPtrArray *a = user_data;
+
+	g_ptr_array_add (a, g_strdup (key));
+}
+
+static const char **
+_get_keys (NMSettingVpn *setting,
+           gboolean is_secrets,
+           guint *out_length)
+{
+	guint len;
+	const char **keys = NULL;
+	GPtrArray *a;
+
+	nm_assert (NM_IS_SETTING_VPN (setting));
+
+	if (is_secrets)
+		len = nm_setting_vpn_get_num_secrets (setting);
+	else
+		len = nm_setting_vpn_get_num_data_items (setting);
+
+	a = g_ptr_array_sized_new (len + 1);
+
+	if (is_secrets)
+		nm_setting_vpn_foreach_secret (setting, _get_keys_cb, a);
+	else
+		nm_setting_vpn_foreach_data_item (setting, _get_keys_cb, a);
+
+	len = a->len;
+	if (len) {
+		g_ptr_array_sort (a, nm_strcmp_p);
+		g_ptr_array_add (a, NULL);
+		keys = g_memdup (a->pdata, a->len * sizeof (gpointer));
+
+		/* we need to cache the keys *somewhere*. */
+		g_object_set_qdata_full (G_OBJECT (setting),
+		                         is_secrets
+		                         ? NM_CACHED_QUARK ("libnm._nm_setting_vpn_get_secret_keys")
+		                         : NM_CACHED_QUARK ("libnm._nm_setting_vpn_get_data_keys"),
+		                         g_ptr_array_free (a, FALSE),
+		                         (GDestroyNotify) g_strfreev);
+	} else
+		g_ptr_array_free (a, TRUE);
+
+	NM_SET_OUT (out_length, len);
+	return keys;
+}
+
+const char **
+_nm_setting_vpn_get_data_keys (NMSettingVpn *setting,
+                               guint *out_length)
+{
+	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+	return _get_keys (setting, FALSE, out_length);
+}
+
+const char **
+_nm_setting_vpn_get_secret_keys (NMSettingVpn *setting,
+                                 guint *out_length)
+{
+	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+	return _get_keys (setting, TRUE, out_length);
+}
diff --git a/shared/nm-utils/nm-compat.h b/shared/nm-utils/nm-compat.h
new file mode 100644
index 00000000..52341690
--- /dev/null
+++ b/shared/nm-utils/nm-compat.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_COMPAT_H__
+#define __NM_COMPAT_H__
+
+#include "nm-setting-vpn.h"
+
+const char **_nm_setting_vpn_get_data_keys (NMSettingVpn *setting,
+                                            guint *out_length);
+
+const char **_nm_setting_vpn_get_secret_keys (NMSettingVpn *setting,
+                                              guint *out_length);
+
+#if NM_CHECK_VERSION (1, 11, 0)
+#define nm_setting_vpn_get_data_keys(setting, out_length) \
+	({ \
+		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+		nm_setting_vpn_get_data_keys (setting, out_length); \
+		G_GNUC_END_IGNORE_DEPRECATIONS \
+	})
+#define nm_setting_vpn_get_secret_keys(setting, out_length) \
+	({ \
+		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+		nm_setting_vpn_get_secret_keys (setting, out_length); \
+		G_GNUC_END_IGNORE_DEPRECATIONS \
+	})
+#else
+#define nm_setting_vpn_get_data_keys(setting, out_length) \
+	_nm_setting_vpn_get_data_keys (setting, out_length)
+#define nm_setting_vpn_get_secret_keys(setting, out_length) \
+	_nm_setting_vpn_get_secret_keys (setting, out_length)
+#endif
+
+#endif /* __NM_COMPAT_H__ */
diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c
index ee310a7b..fc134e25 100644
--- a/shared/nm-utils/nm-dedup-multi.c
+++ b/shared/nm-utils/nm-dedup-multi.c
@@ -386,10 +386,10 @@ _add (NMDedupMultiIndex *self,
 	head_entry->len++;
 
 	if (   add_head_entry
-	    && !nm_g_hash_table_add (self->idx_entries, head_entry))
+	    && !g_hash_table_add (self->idx_entries, head_entry))
 		nm_assert_not_reached ();
 
-	if (!nm_g_hash_table_add (self->idx_entries, entry))
+	if (!g_hash_table_add (self->idx_entries, entry))
 		nm_assert_not_reached ();
 
 	NM_SET_OUT (out_entry, entry);
@@ -870,14 +870,14 @@ nm_dedup_multi_index_obj_intern (NMDedupMultiIndex *self,
 	nm_assert (obj_new);
 	nm_assert (!obj_new->_multi_idx);
 
-	if (!nm_g_hash_table_add (self->idx_objs, (gpointer) obj_new))
+	if (!g_hash_table_add (self->idx_objs, (gpointer) obj_new))
 		nm_assert_not_reached ();
 
 	((NMDedupMultiObj *) obj_new)->_multi_idx = self;
 	return obj_new;
 }
 
-const NMDedupMultiObj *
+void
 nm_dedup_multi_obj_unref (const NMDedupMultiObj *obj)
 {
 	if (obj) {
@@ -899,8 +899,6 @@ again:
 			obj->klass->obj_destroy ((NMDedupMultiObj *) obj);
 		}
 	}
-
-	return NULL;
 }
 
 gboolean
diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-utils/nm-dedup-multi.h
index bebfe43d..8d482de9 100644
--- a/shared/nm-utils/nm-dedup-multi.h
+++ b/shared/nm-utils/nm-dedup-multi.h
@@ -97,7 +97,7 @@ nm_dedup_multi_obj_ref (const NMDedupMultiObj *obj)
 	return obj;
 }
 
-const NMDedupMultiObj *nm_dedup_multi_obj_unref       (const NMDedupMultiObj *obj);
+void                   nm_dedup_multi_obj_unref       (const NMDedupMultiObj *obj);
 const NMDedupMultiObj *nm_dedup_multi_obj_clone       (const NMDedupMultiObj *obj);
 gboolean               nm_dedup_multi_obj_needs_clone (const NMDedupMultiObj *obj);
 
@@ -115,7 +115,7 @@ void nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self,
 /* the NMDedupMultiIdxType is an access handle under which you can store and
  * retrieve NMDedupMultiObj instances in NMDedupMultiIndex.
  *
- * The NMDedupMultiIdxTypeClass determines it's behavior, but you can have
+ * The NMDedupMultiIdxTypeClass determines its behavior, but you can have
  * multiple instances (of the same class).
  *
  * For example, NMIP4Config can have idx-type to put there all IPv4 Routes.
diff --git a/shared/nm-utils/nm-enum-utils.c b/shared/nm-utils/nm-enum-utils.c
index 70a8b415..b9bc6e88 100644
--- a/shared/nm-utils/nm-enum-utils.c
+++ b/shared/nm-utils/nm-enum-utils.c
@@ -64,10 +64,10 @@ _enum_is_valid_flags_nick (const char *str)
 char *
 _nm_utils_enum_to_str_full (GType type,
                             int value,
-                            const char *flags_separator)
+                            const char *flags_separator,
+                            const NMUtilsEnumValueInfo *value_infos)
 {
-	GTypeClass *class;
-	char *ret;
+	nm_auto_unref_gtypeclass GTypeClass *class = NULL;
 
 	if (   flags_separator
 	    && (   !flags_separator[0]
@@ -79,12 +79,17 @@ _nm_utils_enum_to_str_full (GType type,
 	if (G_IS_ENUM_CLASS (class)) {
 		GEnumValue *enum_value;
 
+		for ( ; value_infos && value_infos->nick; value_infos++) {
+			if (value_infos->value == value)
+				return g_strdup (value_infos->nick);
+		}
+
 		enum_value = g_enum_get_value (G_ENUM_CLASS (class), value);
 		if (   !enum_value
 		    || !_enum_is_valid_enum_nick (enum_value->value_nick))
-			ret = g_strdup_printf ("%d", value);
+			return g_strdup_printf ("%d", value);
 		else
-			ret = strdup (enum_value->value_nick);
+			return g_strdup (enum_value->value_nick);
 	} else if (G_IS_FLAGS_CLASS (class)) {
 		GFlagsValue *flags_value;
 		GString *str = g_string_new ("");
@@ -92,6 +97,28 @@ _nm_utils_enum_to_str_full (GType type,
 
 		flags_separator = flags_separator ?: " ";
 
+		for ( ; value_infos && value_infos->nick; value_infos++) {
+
+			nm_assert (_enum_is_valid_flags_nick (value_infos->nick));
+
+			if (uvalue == 0) {
+				if (value_infos->value != 0)
+					continue;
+			} else {
+				if (!NM_FLAGS_ALL (uvalue, (unsigned) value_infos->value))
+					continue;
+			}
+
+			if (str->len)
+				g_string_append (str, flags_separator);
+			g_string_append (str, value_infos->nick);
+			uvalue &= ~((unsigned) value_infos->value);
+			if (uvalue == 0) {
+				/* we printed all flags. Done. */
+				goto flags_done;
+			}
+		}
+
 		do {
 			flags_value = g_flags_get_first_value (G_FLAGS_CLASS (class), uvalue);
 			if (str->len)
@@ -105,12 +132,12 @@ _nm_utils_enum_to_str_full (GType type,
 			g_string_append (str, flags_value->value_nick);
 			uvalue &= ~flags_value->value;
 		} while (uvalue);
-		ret = g_string_free (str, FALSE);
-	} else
-		g_return_val_if_reached (NULL);
 
-	g_type_class_unref (class);
-	return ret;
+flags_done:
+		return g_string_free (str, FALSE);
+	}
+
+	g_return_val_if_reached (NULL);
 }
 
 static const NMUtilsEnumValueInfo *
diff --git a/shared/nm-utils/nm-enum-utils.h b/shared/nm-utils/nm-enum-utils.h
index b78d9191..d6dae859 100644
--- a/shared/nm-utils/nm-enum-utils.h
+++ b/shared/nm-utils/nm-enum-utils.h
@@ -31,7 +31,10 @@ typedef struct _NMUtilsEnumValueInfo {
 	int value;
 } NMUtilsEnumValueInfo;
 
-char *_nm_utils_enum_to_str_full (GType type, int value, const char *sep);
+char *_nm_utils_enum_to_str_full (GType type,
+                                  int value,
+                                  const char *sep,
+                                  const NMUtilsEnumValueInfo *value_infos);
 gboolean _nm_utils_enum_from_str_full (GType type,
                                        const char *str,
                                        int *out_value,
diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h
index 599890e0..f1498dc4 100644
--- a/shared/nm-utils/nm-glib.h
+++ b/shared/nm-utils/nm-glib.h
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2008 - 2011 Red Hat, Inc.
+ * Copyright 2008 - 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_GLIB_H__
@@ -40,84 +40,6 @@
 
 #endif
 
-static inline void
-__g_type_ensure (GType type)
-{
-#if !GLIB_CHECK_VERSION(2,34,0)
-	if (G_UNLIKELY (type == (GType)-1))
-		g_error ("can't happen");
-#else
-	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-	g_type_ensure (type);
-	G_GNUC_END_IGNORE_DEPRECATIONS;
-#endif
-}
-#define g_type_ensure __g_type_ensure
-
-#if !GLIB_CHECK_VERSION(2,34,0)
-
-#define g_clear_pointer(pp, destroy) \
-    G_STMT_START {                                                                 \
-        G_STATIC_ASSERT (sizeof *(pp) == sizeof (gpointer));                       \
-        /* Only one access, please */                                              \
-        gpointer *_pp = (gpointer *) (pp);                                         \
-        gpointer _p;                                                               \
-        /* This assignment is needed to avoid a gcc warning */                     \
-        GDestroyNotify _destroy = (GDestroyNotify) (destroy);                      \
-                                                                                   \
-        _p = *_pp;                                                                 \
-        if (_p)                                                                    \
-        {                                                                          \
-            *_pp = NULL;                                                           \
-            _destroy (_p);                                                         \
-        }                                                                          \
-    } G_STMT_END
-
-/* These are used to clean up the output of test programs; we can just let
- * them no-op in older glib.
- */
-#define g_test_expect_message(log_domain, log_level, pattern)
-#define g_test_assert_expected_messages()
-
-#else
-
-/* We build with -DGLIB_MAX_ALLOWED_VERSION set to 2.32 to make sure we don't
- * accidentally use new API that we shouldn't. But we don't want warnings for
- * the APIs that we emulate above.
- */
-
-#define g_test_expect_message(domain, level, format...) \
-	G_STMT_START { \
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
-		g_test_expect_message (domain, level, format); \
-		G_GNUC_END_IGNORE_DEPRECATIONS \
-	} G_STMT_END
-
-#define g_test_assert_expected_messages_internal(domain, file, line, func) \
-	G_STMT_START { \
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
-		g_test_assert_expected_messages_internal (domain, file, line, func); \
-		G_GNUC_END_IGNORE_DEPRECATIONS \
-	} G_STMT_END
-
-#endif
-
-
-#if GLIB_CHECK_VERSION (2, 35, 0)
-/* For glib >= 2.36, g_type_init() is deprecated.
- * But since 2.35.1 (7c42ab23b55c43ab96d0ac2124b550bf1f49c1ec) this function
- * does nothing. Replace the call with empty statement. */
-#define nm_g_type_init()     G_STMT_START { (void) 0; } G_STMT_END
-#else
-#define nm_g_type_init()     G_STMT_START { g_type_init (); } G_STMT_END
-#endif
-
-
-/* g_test_initialized() is only available since glib 2.36. */
-#if !GLIB_CHECK_VERSION (2, 36, 0)
-#define g_test_initialized() (g_test_config_vars->test_initialized)
-#endif
-
 /* g_assert_cmpmem() is only available since glib 2.46. */
 #if !GLIB_CHECK_VERSION (2, 45, 7)
 #define g_assert_cmpmem(m1, l1, m2, l2) G_STMT_START {\
@@ -146,239 +68,6 @@ nm_glib_check_version (guint major, guint minor, guint micro)
 	               && glib_micro_version < micro));
 }
 
-/* g_test_skip() is only available since glib 2.38. Add a compatibility wrapper. */
-static inline void
-__nmtst_g_test_skip (const gchar *msg)
-{
-#if GLIB_CHECK_VERSION (2, 38, 0)
-	G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-	g_test_skip (msg);
-	G_GNUC_END_IGNORE_DEPRECATIONS
-#else
-	g_debug ("%s", msg);
-#endif
-}
-#define g_test_skip __nmtst_g_test_skip
-
-
-/* g_test_add_data_func_full() is only available since glib 2.34. Add a compatibility wrapper. */
-static inline void
-__g_test_add_data_func_full (const char     *testpath,
-                             gpointer        test_data,
-                             GTestDataFunc   test_func,
-                             GDestroyNotify  data_free_func)
-{
-#if GLIB_CHECK_VERSION (2, 34, 0)
-	G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-	g_test_add_data_func_full (testpath, test_data, test_func, data_free_func);
-	G_GNUC_END_IGNORE_DEPRECATIONS
-#else
-	g_return_if_fail (testpath != NULL);
-	g_return_if_fail (testpath[0] == '/');
-	g_return_if_fail (test_func != NULL);
-
-	g_test_add_vtable (testpath, 0, test_data, NULL,
-	                   (GTestFixtureFunc) test_func,
-	                   (GTestFixtureFunc) data_free_func);
-#endif
-}
-#define g_test_add_data_func_full __g_test_add_data_func_full
-
-
-#if !GLIB_CHECK_VERSION (2, 34, 0)
-#define G_DEFINE_QUARK(QN, q_n)               \
-GQuark                                        \
-q_n##_quark (void)                            \
-{                                             \
-	static GQuark q;                          \
-                                              \
-	if G_UNLIKELY (q == 0)                    \
-		q = g_quark_from_static_string (#QN); \
-                                              \
-	return q;                                 \
-}
-#endif
-
-
-static inline gboolean
-nm_g_hash_table_replace (GHashTable *hash, gpointer key, gpointer value)
-{
-	/* glib 2.40 added a return value indicating whether the key already existed
-	 * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */
-#if GLIB_CHECK_VERSION(2, 40, 0)
-	return g_hash_table_replace (hash, key, value);
-#else
-	gboolean contained = g_hash_table_contains (hash, key);
-
-	g_hash_table_replace (hash, key, value);
-	return !contained;
-#endif
-}
-
-static inline gboolean
-nm_g_hash_table_insert (GHashTable *hash, gpointer key, gpointer value)
-{
-	/* glib 2.40 added a return value indicating whether the key already existed
-	 * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */
-#if GLIB_CHECK_VERSION(2, 40, 0)
-	return g_hash_table_insert (hash, key, value);
-#else
-	gboolean contained = g_hash_table_contains (hash, key);
-
-	g_hash_table_insert (hash, key, value);
-	return !contained;
-#endif
-}
-
-static inline gboolean
-nm_g_hash_table_add (GHashTable *hash, gpointer key)
-{
-	/* glib 2.40 added a return value indicating whether the key already existed
-	 * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */
-#if GLIB_CHECK_VERSION(2, 40, 0)
-	return g_hash_table_add (hash, key);
-#else
-	gboolean contained = g_hash_table_contains (hash, key);
-
-	g_hash_table_add (hash, key);
-	return !contained;
-#endif
-}
-
-#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST)
-static inline void
-_nm_g_ptr_array_insert (GPtrArray *array,
-                        gint       index_,
-                        gpointer   data)
-{
-	g_return_if_fail (array);
-	g_return_if_fail (index_ >= -1);
-	g_return_if_fail (index_ <= (gint) array->len);
-
-	g_ptr_array_add (array, data);
-
-	if (index_ != -1 && index_ != (gint) (array->len - 1)) {
-		memmove (&(array->pdata[index_ + 1]),
-		         &(array->pdata[index_]),
-		         (array->len - index_ - 1) * sizeof (gpointer));
-		array->pdata[index_] = data;
-	}
-}
-#endif
-#if !GLIB_CHECK_VERSION(2, 40, 0)
-#define g_ptr_array_insert(array, index, data) G_STMT_START { _nm_g_ptr_array_insert (array, index, data); } G_STMT_END
-#else
-#define g_ptr_array_insert(array, index, data) \
-	G_STMT_START { \
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
-		g_ptr_array_insert (array, index, data); \
-		G_GNUC_END_IGNORE_DEPRECATIONS \
-	} G_STMT_END
-#endif
-
-
-#if !GLIB_CHECK_VERSION (2, 40, 0)
-static inline gboolean
-_g_key_file_save_to_file (GKeyFile     *key_file,
-                          const gchar  *filename,
-                          GError      **error)
-{
-	gchar *contents;
-	gboolean success;
-	gsize length;
-
-	g_return_val_if_fail (key_file != NULL, FALSE);
-	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-	contents = g_key_file_to_data (key_file, &length, NULL);
-	g_assert (contents != NULL);
-
-	success = g_file_set_contents (filename, contents, length, error);
-	g_free (contents);
-
-	return success;
-}
-#define g_key_file_save_to_file(key_file, filename, error) \
-	_g_key_file_save_to_file (key_file, filename, error)
-#else
-#define g_key_file_save_to_file(key_file, filename, error) \
-	({ \
-		gboolean _success; \
-		\
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
-		_success = g_key_file_save_to_file (key_file, filename, error); \
-		G_GNUC_END_IGNORE_DEPRECATIONS \
-		_success; \
-	})
-#endif
-
-
-#if GLIB_CHECK_VERSION (2, 36, 0)
-#define g_credentials_get_unix_pid(creds, error) \
-	({ \
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
-			(g_credentials_get_unix_pid) ((creds), (error)); \
-		G_GNUC_END_IGNORE_DEPRECATIONS \
-	})
-#else
-#define g_credentials_get_unix_pid(creds, error) \
-	({ \
-		struct ucred *native_creds; \
-		 \
-		native_creds = g_credentials_get_native ((creds), G_CREDENTIALS_TYPE_LINUX_UCRED); \
-		g_assert (native_creds); \
-		native_creds->pid; \
-	})
-#endif
-
-
-#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST)
-static inline gpointer *
-_nm_g_hash_table_get_keys_as_array (GHashTable *hash_table,
-                                    guint      *length)
-{
-	GHashTableIter iter;
-	gpointer key, *ret;
-	guint i = 0;
-
-	g_return_val_if_fail (hash_table, NULL);
-
-	ret = g_new0 (gpointer, g_hash_table_size (hash_table) + 1);
-	g_hash_table_iter_init (&iter, hash_table);
-
-	while (g_hash_table_iter_next (&iter, &key, NULL))
-		ret[i++] = key;
-
-	ret[i] = NULL;
-
-	if (length)
-		*length = i;
-
-	return ret;
-}
-#endif
-#if !GLIB_CHECK_VERSION(2, 40, 0)
-#define g_hash_table_get_keys_as_array(hash_table, length) \
-	({ \
-		_nm_g_hash_table_get_keys_as_array (hash_table, length); \
-	})
-#else
-#define g_hash_table_get_keys_as_array(hash_table, length) \
-	({ \
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
-			(g_hash_table_get_keys_as_array) ((hash_table), (length)); \
-		G_GNUC_END_IGNORE_DEPRECATIONS \
-	})
-#endif
-
-#ifndef g_info
-/* g_info was only added with 2.39.2 */
-#define g_info(...)     g_log (G_LOG_DOMAIN,         \
-                               G_LOG_LEVEL_INFO,     \
-                               __VA_ARGS__)
-#endif
-
 #if !GLIB_CHECK_VERSION(2, 44, 0)
 static inline gpointer
 g_steal_pointer (gpointer pp)
@@ -420,70 +109,17 @@ _nm_g_strv_contains (const gchar * const *strv,
 }
 #define g_strv_contains _nm_g_strv_contains
 
-static inline GVariant *
-_nm_g_variant_new_take_string (gchar *string)
-{
-#if !GLIB_CHECK_VERSION(2, 36, 0)
-	GVariant *value;
-
-	g_return_val_if_fail (string != NULL, NULL);
-	g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL);
-
-	value = g_variant_new_string (string);
-	g_free (string);
-	return value;
-#elif !GLIB_CHECK_VERSION(2, 38, 0)
-	GVariant *value;
-	GBytes *bytes;
-
-	g_return_val_if_fail (string != NULL, NULL);
-	g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL);
-
-	bytes = g_bytes_new_take (string, strlen (string) + 1);
-	value = g_variant_new_from_bytes (G_VARIANT_TYPE_STRING, bytes, TRUE);
-	g_bytes_unref (bytes);
-
-	return value;
-#else
-	G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-	return g_variant_new_take_string (string);
-	G_GNUC_END_IGNORE_DEPRECATIONS
-#endif
-}
-#define g_variant_new_take_string _nm_g_variant_new_take_string
-
-#if !GLIB_CHECK_VERSION(2, 38, 0)
-_nm_printf (1, 2)
-static inline GVariant *
-_nm_g_variant_new_printf (const char *format_string, ...)
-{
-	char *string;
-	va_list ap;
-
-	g_return_val_if_fail (format_string, NULL);
-
-	va_start (ap, format_string);
-	string = g_strdup_vprintf (format_string, ap);
-	va_end (ap);
-
-	return g_variant_new_take_string (string);
-}
-#define g_variant_new_printf(...) _nm_g_variant_new_printf(__VA_ARGS__)
-#else
-#define g_variant_new_printf(...) \
-	({ \
-		GVariant *_v; \
-		\
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
-		_v = g_variant_new_printf (__VA_ARGS__); \
-		G_GNUC_END_IGNORE_DEPRECATIONS \
-		_v; \
-	})
-#endif
-
 #if !GLIB_CHECK_VERSION (2, 56, 0)
 #define g_object_ref(Obj)      ((typeof(Obj)) g_object_ref (Obj))
 #define g_object_ref_sink(Obj) ((typeof(Obj)) g_object_ref_sink (Obj))
 #endif
 
+#ifndef g_autofree
+/* we still don't rely on recent glib to provide g_autofree. Hence, we continue
+ * to use our gs_* free macros that we took from libgsystem.
+ *
+ * To ease migration towards g_auto*, add a compat define for g_autofree. */
+#define g_autofree gs_free
+#endif
+
 #endif  /* __NM_GLIB_H__ */
diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c
index c563140e..8d8c21ce 100644
--- a/shared/nm-utils/nm-hash-utils.c
+++ b/shared/nm-utils/nm-hash-utils.c
@@ -28,6 +28,8 @@
 #include "nm-shared-utils.h"
 #include "nm-random-utils.h"
 
+#include "siphash24.c"
+
 /*****************************************************************************/
 
 #define HASH_KEY_SIZE 16u
@@ -35,33 +37,77 @@
 
 G_STATIC_ASSERT (sizeof (guint) * HASH_KEY_SIZE_GUINT >= HASH_KEY_SIZE);
 
+static const guint8 *volatile global_seed = NULL;
+
 static const guint8 *
-_get_hash_key (void)
+_get_hash_key_init (void)
 {
-	static const guint8 *volatile global_seed = NULL;
+	/* 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;
+	struct siphash siph_state;
+	uint64_t h;
+	guint *p;
 
 	g = global_seed;
-	if (G_UNLIKELY (g == NULL)) {
-		/* the returned hash is aligned to guin64, hence, it is save
-		 * to use it as guint* or guint64* pointer. */
-		static union {
-			guint8 v8[HASH_KEY_SIZE];
-		} g_arr _nm_alignas (guint64);
-		static gsize g_lock;
-
-		if (g_once_init_enter (&g_lock)) {
-			nm_utils_random_bytes (g_arr.v8, sizeof (g_arr.v8));
-			g_atomic_pointer_compare_and_exchange (&global_seed, NULL, g_arr.v8);
-			g = g_arr.v8;
-			g_once_init_leave (&g_lock, 1);
-		} else {
-			g = global_seed;
-			nm_assert (g);
-		}
+	if (G_LIKELY (g != NULL)) {
+		nm_assert (g == g_arr.v8);
+		return g;
+	}
+
+	if (g_once_init_enter (&g_lock)) {
+
+		nm_utils_random_bytes (g_arr.v8, sizeof (g_arr.v8));
+
+		/* 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. */
+		siphash24_init (&siph_state, g_arr.v8);
+		siphash24_compress (g_arr.v8, sizeof (g_arr.v8), &siph_state);
+		h = siphash24_finalize (&siph_state);
+		p = (guint *) g_arr.v8;
+		if (sizeof (guint) < sizeof (h))
+			*p = *p ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32));
+		else
+			*p = *p ^ ((guint) (h & 0xFFFFFFFFu));
+
+		g_atomic_pointer_compare_and_exchange (&global_seed, NULL, g_arr.v8);
+		g_once_init_leave (&g_lock, 1);
 	}
 
-	return g;
+	nm_assert (global_seed == g_arr.v8);
+	return g_arr.v8;
+}
+
+#define _get_hash_key() \
+	({ \
+		const guint8 *_g; \
+		\
+		_g = global_seed; \
+		if (G_UNLIKELY (_g == NULL)) \
+			_g = _get_hash_key_init (); \
+		_g; \
+	})
+
+guint
+nm_hash_static (guint static_seed)
+{
+	/* note that we only xor the static_seed with the key.
+	 * We don't use siphash24(), which would mix the bits better.
+	 * Note that this doesn't matter, because static_seed is not
+	 * supposed to be a value that you are hashing (for that, use
+	 * full siphash24()).
+	 * Instead, different callers may set a different static_seed
+	 * so that nm_hash_str(NULL) != nm_hash_ptr(NULL).
+	 *
+	 * Also, ensure that we don't return zero.
+	 */
+	return ((*((const guint *) _get_hash_key ())) ^ static_seed)
+	       ?: static_seed ?: 3679500967u;
 }
 
 void
@@ -83,11 +129,10 @@ nm_hash_str (const char *str)
 {
 	NMHashState h;
 
-	if (str) {
-		nm_hash_init (&h, 1867854211u);
-		nm_hash_update_str (&h, str);
-	} else
-		nm_hash_init (&h, 842995561u);
+	if (!str)
+		return nm_hash_static (1867854211u);
+	nm_hash_init (&h, 1867854211u);
+	nm_hash_update_str (&h, str);
 	return nm_hash_complete (&h);
 }
 
@@ -100,16 +145,13 @@ nm_str_hash (gconstpointer str)
 guint
 nm_hash_ptr (gconstpointer ptr)
 {
-	guint h;
-
-	h = ((const guint *) _get_hash_key ())[0];
-
-	if (sizeof (ptr) <= sizeof (guint))
-		h = h ^ ((guint) ((uintptr_t) ptr));
-	else
-		h = h ^ ((guint) (((guint64) (uintptr_t) ptr) >> 32)) ^ ((guint) ((uintptr_t) ptr));
+	NMHashState h;
 
-	return h ?: 2907677551u;
+	if (!ptr)
+		return nm_hash_static (2907677551u);
+	nm_hash_init (&h, 2907677551u);
+	nm_hash_update (&h, &ptr, sizeof (ptr));
+	return nm_hash_complete (&h);
 }
 
 guint
@@ -117,3 +159,27 @@ nm_direct_hash (gconstpointer ptr)
 {
 	return nm_hash_ptr (ptr);
 }
+
+/*****************************************************************************/
+
+guint
+nm_pstr_hash (gconstpointer p)
+{
+	const char *const*s = p;
+
+	if (!s)
+		return nm_hash_static (101061439u);
+	return nm_hash_str (*s);
+}
+
+gboolean
+nm_pstr_equal (gconstpointer a, gconstpointer b)
+{
+	const char *const*s1 = a;
+	const char *const*s2 = b;
+
+	return    (s1 == s2)
+	       || (   s1
+	           && s2
+	           && nm_streq0 (*s1, *s2));
+}
diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h
index 276e1ebe..3bd3f652 100644
--- a/shared/nm-utils/nm-hash-utils.h
+++ b/shared/nm-utils/nm-hash-utils.h
@@ -31,6 +31,8 @@ struct _NMHashState {
 
 typedef struct _NMHashState NMHashState;
 
+guint nm_hash_static (guint static_seed);
+
 void nm_hash_init (NMHashState *state, guint static_seed);
 
 static inline guint
@@ -207,4 +209,15 @@ guint nm_direct_hash (gconstpointer str);
 guint nm_hash_str (const char *str);
 guint nm_str_hash (gconstpointer str);
 
+/*****************************************************************************/
+
+/* nm_pstr_*() are for hashing keys that are pointers to strings,
+ * that is, "const char *const*" types, using strcmp(). */
+
+guint nm_pstr_hash (gconstpointer p);
+
+gboolean nm_pstr_equal (gconstpointer a, gconstpointer b);
+
+/*****************************************************************************/
+
 #endif /* __NM_HASH_UTILS_H__ */
diff --git a/shared/nm-utils/nm-jansson.h b/shared/nm-utils/nm-jansson.h
new file mode 100644
index 00000000..b00c75c6
--- /dev/null
+++ b/shared/nm-utils/nm-jansson.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_JANSSON_H__
+#define __NM_JANSSON_H__
+
+/* you need to include at least "config.h" first, possibly "nm-default.h". */
+
+#if WITH_JANSSON
+
+#include <jansson.h>
+
+/* Added in Jansson v2.7 */
+#ifndef json_boolean_value
+#define json_boolean_value json_is_true
+#endif
+
+/* Added in Jansson v2.8 */
+#ifndef json_object_foreach_safe
+#define json_object_foreach_safe(object, n, key, value)     \
+    for(key = json_object_iter_key(json_object_iter(object)), \
+            n = json_object_iter_next(object, json_object_key_to_iter(key)); \
+        key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
+        key = json_object_iter_key(n), \
+            n = json_object_iter_next(object, json_object_key_to_iter(key)))
+#endif
+
+#endif /* WITH_JANSON */
+
+#endif  /* __NM_JANSSON_H__ */
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index 29678bb6..cc7205a4 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -35,6 +35,12 @@
 #define _nm_alignof(type)    __alignof (type)
 #define _nm_alignas(type)    _nm_align (_nm_alignof (type))
 
+#if __GNUC__ >= 7
+#define _nm_fallthrough      __attribute__ ((fallthrough))
+#else
+#define _nm_fallthrough
+#endif
+
 /*****************************************************************************/
 
 #ifdef thread_local
@@ -70,6 +76,30 @@ static inline int nm_close (int fd);
 GS_DEFINE_CLEANUP_FUNCTION(void*, _nm_auto_free_impl, free)
 
 static inline void
+nm_free_secret (char *secret)
+{
+	if (secret) {
+		memset (secret, 0, strlen (secret));
+		g_free (secret);
+	}
+}
+
+static inline void
+_nm_auto_free_secret_impl (char **v)
+{
+	nm_free_secret (*v);
+}
+
+/**
+ * nm_auto_free_secret:
+ *
+ * Call g_free() on a variable location when it goes out of scope.
+ * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out
+ * the secret.
+ */
+#define nm_auto_free_secret nm_auto(_nm_auto_free_secret_impl)
+
+static inline void
 _nm_auto_unset_gvalue_impl (GValue *v)
 {
 	g_value_unset (v);
@@ -241,7 +271,8 @@ NM_G_ERROR_MSG (GError *error)
 		gsize _n = 0; \
 		\
 		if (_array) { \
-			_nm_unused typeof (*(_array[0])) *_array_check = _array[0]; \
+			_nm_unused gconstpointer _type_check_is_pointer = _array[0]; \
+			\
 			while (_array[_n]) \
 				_n++; \
 		} \
@@ -359,6 +390,28 @@ NM_G_ERROR_MSG (GError *error)
 #define NM_CONSTCAST(type, obj, ...) \
 	NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__)
 
+#if _NM_CC_SUPPORT_GENERIC
+#define NM_UNCONST_PTR(type, arg) \
+	_Generic ((arg), \
+	          const type *: ((type *) (arg)), \
+	                type *: ((type *) (arg)))
+#else
+#define NM_UNCONST_PTR(type, arg) \
+	((type *) (arg))
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+#define NM_UNCONST_PPTR(type, arg) \
+	_Generic ((arg), \
+	          const type *     *: ((type **) (arg)), \
+	                type *     *: ((type **) (arg)), \
+	          const type *const*: ((type **) (arg)), \
+	                type *const*: ((type **) (arg)))
+#else
+#define NM_UNCONST_PPTR(type, arg) \
+	((type **) (arg))
+#endif
+
 #define NM_GOBJECT_CAST(type, obj, is_check, ...) \
 	({ \
 		const void *_obj = (obj); \
@@ -388,6 +441,41 @@ NM_G_ERROR_MSG (GError *error)
 #endif
 
 #if _NM_CC_SUPPORT_GENERIC
+/* these macros cast (value) to
+ *  - "const char **"      (for "MC", mutable-const)
+ *  - "const char *const*" (for "CC", const-const)
+ * The point is to do this cast, but only accepting pointers
+ * that are compatible already.
+ *
+ * The problem is, if you add a function like g_strdupv(), the input
+ * 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
+ * explicitly cast it.
+ *
+ * These macros do the cast, but they only accept a compatible input
+ * type, otherwise they will fail compilation.
+ */
+#define NM_CAST_STRV_MC(value) \
+	(_Generic ((value), \
+	           const char *     *: (const char *     *) (value), \
+	                 char *     *: (const char *     *) (value), \
+	                       void *: (const char *     *) (value)))
+#define NM_CAST_STRV_CC(value) \
+	(_Generic ((value), \
+	           const char *const*: (const char *const*) (value), \
+	           const char *     *: (const char *const*) (value), \
+	                 char *const*: (const char *const*) (value), \
+	                 char *     *: (const char *const*) (value), \
+	                 const void *: (const char *const*) (value), \
+	                       void *: (const char *const*) (value)))
+#else
+#define NM_CAST_STRV_MC(value) ((const char *     *) (value))
+#define NM_CAST_STRV_CC(value) ((const char *const*) (value))
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
 #define NM_PROPAGATE_CONST(test_expr, ptr) \
 	(_Generic ((test_expr), \
 	           const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \
@@ -741,6 +829,32 @@ nm_g_object_unref (gpointer obj)
 		_changed; \
 	})
 
+#define nm_clear_pointer(pp, destroy) \
+	({ \
+		typeof (*(pp)) *_pp = (pp); \
+		typeof (*_pp) _p; \
+		gboolean _changed = FALSE; \
+		\
+		if (   _pp \
+		    && (_p = *_pp)) { \
+			_nm_unused gconstpointer _p_check_is_pointer = _p; \
+			\
+			*_pp = NULL; \
+			/* g_clear_pointer() assigns @destroy first to a local variable, so that
+			 * you can call "g_clear_pointer (pp, (GDestroyNotify) destroy);" without
+			 * gcc emitting a warning. We don't do that, hence, you cannot cast
+			 * "destroy" first.
+			 *
+			 * On the upside: you are not supposed to cast fcn, because the pointer
+			 * types are preserved. If you really need a cast, you should cast @pp.
+			 * But that is hardly ever necessary. */ \
+			(destroy) (_p); \
+			\
+			_changed = TRUE; \
+		} \
+		_changed; \
+	})
+
 /* basically, replaces
  *   g_clear_pointer (&location, g_free)
  * with
@@ -751,42 +865,20 @@ nm_g_object_unref (gpointer obj)
  * pointer or points to a const-pointer.
  */
 #define nm_clear_g_free(pp) \
-	({  \
-		typeof (*(pp)) *_pp = (pp); \
-		typeof (**_pp) *_p; \
-		gboolean _changed = FALSE; \
-		\
-		if (  _pp \
-		    && (_p = *_pp)) { \
-			*_pp = NULL; \
-			g_free (_p); \
-			_changed = TRUE; \
-		} \
-		_changed; \
-	})
+	nm_clear_pointer (pp, g_free)
 
 #define nm_clear_g_object(pp) \
-	({ \
-		typeof (*(pp)) *_pp = (pp); \
-		typeof (**_pp) *_p; \
-		gboolean _changed = FALSE; \
-		\
-		if (   _pp \
-		    && (_p = *_pp)) { \
-			nm_assert (G_IS_OBJECT (_p)); \
-			*_pp = NULL; \
-			g_object_unref (_p); \
-			_changed = TRUE; \
-		} \
-		_changed; \
-	})
+	nm_clear_pointer (pp, g_object_unref)
 
 static inline gboolean
 nm_clear_g_source (guint *id)
 {
-	if (id && *id) {
-		g_source_remove (*id);
+	guint v;
+
+	if (   id
+	    && (v = *id)) {
 		*id = 0;
+		g_source_remove (v);
 		return TRUE;
 	}
 	return FALSE;
@@ -795,9 +887,12 @@ nm_clear_g_source (guint *id)
 static inline gboolean
 nm_clear_g_signal_handler (gpointer self, gulong *id)
 {
-	if (id && *id) {
-		g_signal_handler_disconnect (self, *id);
+	gulong v;
+
+	if (   id
+	    && (v = *id)) {
 		*id = 0;
+		g_signal_handler_disconnect (self, v);
 		return TRUE;
 	}
 	return FALSE;
@@ -806,9 +901,12 @@ nm_clear_g_signal_handler (gpointer self, gulong *id)
 static inline gboolean
 nm_clear_g_variant (GVariant **variant)
 {
-	if (variant && *variant) {
-		g_variant_unref (*variant);
+	GVariant *v;
+
+	if (   variant
+	    && (v = *variant)) {
 		*variant = NULL;
+		g_variant_unref (v);
 		return TRUE;
 	}
 	return FALSE;
@@ -817,10 +915,13 @@ nm_clear_g_variant (GVariant **variant)
 static inline gboolean
 nm_clear_g_cancellable (GCancellable **cancellable)
 {
-	if (cancellable && *cancellable) {
-		g_cancellable_cancel (*cancellable);
-		g_object_unref (*cancellable);
+	GCancellable *v;
+
+	if (   cancellable
+	    && (v = *cancellable)) {
 		*cancellable = NULL;
+		g_cancellable_cancel (v);
+		g_object_unref (v);
 		return TRUE;
 	}
 	return FALSE;
@@ -991,35 +1092,6 @@ nm_strcmp_p (gconstpointer a, gconstpointer b)
 	return strcmp (s1, s2);
 }
 
-/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data().
- * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different
- * signature. I guess, in glib there are knowledgeable people that ensure
- * that this additional argument doesn't cause problems due to different ABI
- * for every architecture that glib supports.
- * For NetworkManager, we'd rather avoid such stunts.
- **/
-static inline int
-nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
-{
-	const char *s1 = *((const char **) a);
-	const char *s2 = *((const char **) b);
-
-	return strcmp (s1, s2);
-}
-
-static inline int
-nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
-{
-	const guint32 a = *((const guint32 *) p_a);
-	const guint32 b = *((const guint32 *) p_b);
-
-	if (a < b)
-		return -1;
-	if (a > b)
-		return 1;
-	return 0;
-}
-
 /*****************************************************************************/
 
 /* Taken from systemd's UNIQ_T and UNIQ macros. */
@@ -1150,6 +1222,28 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
 		_buf; \
 	})
 
+/* aims to alloca() a buffer and fill it with printf(format, name).
+ * Note that format must not contain any format specifier except
+ * "%s".
+ * If the resulting string would be too large for stack allocation,
+ * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */
+#define nm_construct_name_a(format, name, p_val_to_free) \
+	({ \
+		const char *const _name = (name); \
+		char **const _p_val_to_free = (p_val_to_free); \
+		const gsize _name_len = strlen (_name); \
+		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); \
+		else { \
+			_buf2 = g_strdup_printf (format, _name); \
+			*_p_val_to_free = _buf2; \
+		} \
+		(const char *) _buf2; \
+	})
+
 /*****************************************************************************/
 
 /**
@@ -1225,6 +1319,25 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
 
 /*****************************************************************************/
 
+/**
+ * nm_steal_int:
+ * @p_val: pointer to an int type.
+ *
+ * Returns: *p_val and sets *p_val to zero the same time.
+ *   Accepts %NULL, in which case also numeric 0 will be returned.
+ */
+#define nm_steal_int(p_val) \
+	({ \
+		typeof (p_val) const _p_val = (p_val); \
+		typeof (*_p_val) _val = 0; \
+		\
+		if (   _p_val \
+		    && (_val = *_p_val)) { \
+			*_p_val = 0; \
+		} \
+		_val; \
+	})
+
 static inline int
 nm_steal_fd (int *p_fd)
 {
diff --git a/shared/nm-utils/nm-obj.h b/shared/nm-utils/nm-obj.h
index 1a9d4868..4edd1f3e 100644
--- a/shared/nm-utils/nm-obj.h
+++ b/shared/nm-utils/nm-obj.h
@@ -56,7 +56,7 @@ struct _NMObjBaseClass {
 	 * Note that it is also an abstract super class of GTypeInstance, that means
 	 * you may implement a NMObjBaseClass as a subtype of GTypeClass.
 	 *
-	 * For that to work, you must properly set the GTypeClass instance (and it's
+	 * For that to work, you must properly set the GTypeClass instance (and its
 	 * GType).
 	 *
 	 * Note that to implement a NMObjBaseClass that is *not* a GTypeClass, you wouldn't
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 0b343afd..6937065c 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -499,6 +499,56 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma
 
 /*****************************************************************************/
 
+/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data().
+ * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different
+ * signature. I guess, in glib there are knowledgeable people that ensure
+ * that this additional argument doesn't cause problems due to different ABI
+ * for every architecture that glib supports.
+ * For NetworkManager, we'd rather avoid such stunts.
+ **/
+int
+nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	const char *s1 = *((const char **) a);
+	const char *s2 = *((const char **) b);
+
+	return strcmp (s1, s2);
+}
+
+int
+nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+	const guint32 a = *((const guint32 *) p_a);
+	const guint32 b = *((const guint32 *) p_b);
+
+	if (a < b)
+		return -1;
+	if (a > b)
+		return 1;
+	return 0;
+}
+
+int
+nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+	/* p_a and p_b are two pointers to a pointer, where the pointer is
+	 * interpreted as a integer using GPOINTER_TO_INT().
+	 *
+	 * That is the case of a hash-table that uses GINT_TO_POINTER() to
+	 * convert integers as pointers, and the resulting keys-as-array
+	 * array. */
+	const int a = GPOINTER_TO_INT (*((gconstpointer *) p_a));
+	const int b = GPOINTER_TO_INT (*((gconstpointer *) p_b));
+
+	if (a < b)
+		return -1;
+	if (a > b)
+		return 1;
+	return 0;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_utils_strsplit_set:
  * @str: the string to split.
@@ -1115,3 +1165,197 @@ nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
 
 	return 0;
 }
+
+NMUtilsNamedValue *
+nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len)
+{
+	GHashTableIter iter;
+	NMUtilsNamedValue *values;
+	guint i, len;
+
+	if (   !hash
+	    || !(len = g_hash_table_size (hash))) {
+		NM_SET_OUT (out_len, 0);
+		return NULL;
+	}
+
+	i = 0;
+	values = g_new (NMUtilsNamedValue, len + 1);
+	g_hash_table_iter_init (&iter, hash);
+	while (g_hash_table_iter_next (&iter,
+	                               (gpointer *) &values[i].name,
+	                               (gpointer *) &values[i].value_ptr))
+		i++;
+	nm_assert (i == len);
+	values[i].name = NULL;
+	values[i].value_ptr = NULL;
+
+	if (len > 1) {
+		g_qsort_with_data (values, len, sizeof (values[0]),
+		                   nm_utils_named_entry_cmp_with_data, NULL);
+	}
+
+	NM_SET_OUT (out_len, len);
+	return values;
+}
+
+gpointer *
+nm_utils_hash_keys_to_array (GHashTable *hash,
+                             GCompareDataFunc compare_func,
+                             gpointer user_data,
+                             guint *out_len)
+{
+	guint len;
+	gpointer *keys;
+
+	/* by convention, we never return an empty array. In that
+	 * case, always %NULL. */
+	if (   !hash
+	    || g_hash_table_size (hash) == 0) {
+		NM_SET_OUT (out_len, 0);
+		return NULL;
+	}
+
+	keys = g_hash_table_get_keys_as_array (hash, &len);
+	if (   len > 1
+	    && compare_func) {
+		g_qsort_with_data (keys,
+		                   len,
+		                   sizeof (gpointer),
+		                   compare_func,
+		                   user_data);
+	}
+	NM_SET_OUT (out_len, len);
+	return keys;
+}
+
+char **
+nm_utils_strv_make_deep_copied (const char **strv)
+{
+	gsize i;
+
+	/* it takes a strv dictionary, and copies each
+	 * strings. Note that this updates @strv *in-place*
+	 * and returns it. */
+
+	if (!strv)
+		return NULL;
+	for (i = 0; strv[i]; i++)
+		strv[i] = g_strdup (strv[i]);
+
+	return (char **) strv;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_get_start_time_for_pid:
+ * @pid: the process identifier
+ * @out_state: return the state character, like R, S, Z. See `man 5 proc`.
+ * @out_ppid: parent process id
+ *
+ * Originally copied from polkit source (src/polkit/polkitunixprocess.c)
+ * and adjusted.
+ *
+ * Returns: the timestamp when the process started (by parsing /proc/$PID/stat).
+ * If an error occurs (e.g. the process does not exist), 0 is returned.
+ *
+ * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds)
+ **/
+guint64
+nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
+{
+	guint64 start_time;
+	char filename[256];
+	gs_free gchar *contents = NULL;
+	size_t length;
+	gs_strfreev gchar **tokens = NULL;
+	guint num_tokens;
+	gchar *p;
+	char state = ' ';
+	gint64 ppid = 0;
+
+	start_time = 0;
+	contents = NULL;
+
+	g_return_val_if_fail (pid > 0, 0);
+
+	nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
+
+	if (!g_file_get_contents (filename, &contents, &length, NULL))
+		goto fail;
+
+	/* start time is the token at index 19 after the '(process name)' entry - since only this
+	 * field can contain the ')' character, search backwards for this to avoid malicious
+	 * processes trying to fool us
+	 */
+	p = strrchr (contents, ')');
+	if (p == NULL)
+		goto fail;
+	p += 2; /* skip ') ' */
+	if (p - contents >= (int) length)
+		goto fail;
+
+	state = p[0];
+
+	tokens = g_strsplit (p, " ", 0);
+
+	num_tokens = g_strv_length (tokens);
+
+	if (num_tokens < 20)
+		goto fail;
+
+	if (out_ppid) {
+		ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0);
+		if (ppid == 0)
+			goto fail;
+	}
+
+	start_time = _nm_utils_ascii_str_to_int64 (tokens[19], 10, 1, G_MAXINT64, 0);
+	if (start_time == 0)
+		goto fail;
+
+	NM_SET_OUT (out_state, state);
+	NM_SET_OUT (out_ppid, ppid);
+	return start_time;
+
+fail:
+	NM_SET_OUT (out_state, ' ');
+	NM_SET_OUT (out_ppid, 0);
+	return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ * _nm_utils_strv_sort:
+ * @strv: pointer containing strings that will be sorted
+ *   in-place, %NULL is allowed, unless @len indicates
+ *   that there are more elements.
+ * @len: the number of elements in strv. If negative,
+ *   strv must be a NULL terminated array and the length
+ *   will be calculated first. If @len is a positive
+ *   number, all first @len elements in @strv must be
+ *   non-NULL, valid strings.
+ *
+ * Ascending sort of the array @strv inplace, using plain strcmp() string
+ * comparison.
+ */
+void
+_nm_utils_strv_sort (const char **strv, gssize len)
+{
+	gsize l;
+
+	l = len < 0 ? (gsize) NM_PTRARRAY_LEN (strv) : (gsize) len;
+
+	if (l <= 1)
+		return;
+
+	nm_assert (l <= (gsize) G_MAXINT);
+
+	g_qsort_with_data (strv,
+	                   l,
+	                   sizeof (const char *),
+	                   nm_strcmp_p_with_data,
+	                   NULL);
+}
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index d6d829cd..84325bb7 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -326,12 +326,18 @@ _nm_g_slice_free_fcn_define (16)
 		/* If mem_size is a compile time constant, the compiler
 		 * will be able to optimize this. Hence, you don't want
 		 * to call this with a non-constant size argument. */ \
-		switch (mem_size) { \
+		G_STATIC_ASSERT_EXPR (   ((mem_size) ==  1) \
+		                      || ((mem_size) ==  2) \
+		                      || ((mem_size) ==  4) \
+		                      || ((mem_size) ==  8) \
+		                      || ((mem_size) == 12) \
+		                      || ((mem_size) == 16)); \
+		switch ((mem_size)) { \
 		case  1: _fcn = _nm_g_slice_free_fcn_1;  break; \
 		case  2: _fcn = _nm_g_slice_free_fcn_2;  break; \
 		case  4: _fcn = _nm_g_slice_free_fcn_4;  break; \
 		case  8: _fcn = _nm_g_slice_free_fcn_8;  break; \
-		case 12: _fcn = _nm_g_slice_free_fcn_12;  break; \
+		case 12: _fcn = _nm_g_slice_free_fcn_12; break; \
 		case 16: _fcn = _nm_g_slice_free_fcn_16; break; \
 		default: g_assert_not_reached (); _fcn = NULL; break; \
 		} \
@@ -415,6 +421,29 @@ char *nm_utils_str_utf8safe_unescape_cp (const char *str);
 
 char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags);
 
+static inline void
+nm_g_variant_unref_floating (GVariant *var)
+{
+	/* often a function wants to keep a reference to an input variant.
+	 * It uses g_variant_ref_sink() to either increase the ref-count,
+	 * or take ownership of a possibly floating reference.
+	 *
+	 * If the function doesn't actually want to do anything with the
+	 * input variant, it still must make sure that a passed in floating
+	 * reference is consumed. Hence, this helper which:
+	 *
+	 *   - does nothing if @var is not floating
+	 *   - unrefs (consumes) @var if it is floating. */
+	if (g_variant_is_floating (var))
+		g_variant_unref (var);
+}
+
+/*****************************************************************************/
+
+int nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data);
+int nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data);
+int nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data);
+
 /*****************************************************************************/
 
 typedef struct {
@@ -435,6 +464,35 @@ typedef struct {
 #define nm_utils_named_entry_cmp           nm_strcmp_p
 #define nm_utils_named_entry_cmp_with_data nm_strcmp_p_with_data
 
+NMUtilsNamedValue *nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len);
+
+gpointer *nm_utils_hash_keys_to_array (GHashTable *hash,
+                                       GCompareDataFunc compare_func,
+                                       gpointer user_data,
+                                       guint *out_len);
+
+static inline const char **
+nm_utils_strdict_get_keys (const GHashTable *hash,
+                           gboolean sorted,
+                           guint *out_length)
+{
+	return (const char **) nm_utils_hash_keys_to_array ((GHashTable *) hash,
+	                                                    sorted ? nm_strcmp_p_with_data : NULL,
+	                                                    NULL,
+	                                                    out_length);
+}
+
+char **nm_utils_strv_make_deep_copied (const char **strv);
+
+static inline char **
+nm_utils_strv_make_deep_copied_nonnull (const char **strv)
+{
+	return nm_utils_strv_make_deep_copied (strv) ?: g_new0 (char *, 1);
+}
+
+void _nm_utils_strv_sort (const char **strv, gssize len);
+#define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort (NM_CAST_STRV_MC (strv), len)
+
 /*****************************************************************************/
 
 #define NM_UTILS_NS_PER_SECOND  ((gint64) 1000000000)
@@ -449,4 +507,108 @@ int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
 
 /*****************************************************************************/
 
+static inline const char *
+nm_utils_dbus_normalize_object_path (const char *path)
+{
+	/* D-Bus does not allow an empty object path. Hence, whenever we mean NULL / no-object
+	 * on D-Bus, it's path is actually "/".
+	 *
+	 * Normalize that away, and return %NULL in that case. */
+	if (path && path[0] == '/' && path[1] == '\0')
+		return NULL;
+	return path;
+}
+
+#define NM_DEFINE_GDBUS_ARG_INFO_FULL(name_, ...) \
+	((GDBusArgInfo *) (&((const GDBusArgInfo) { \
+		.ref_count = -1, \
+		.name = name_, \
+		__VA_ARGS__ \
+	})))
+
+#define NM_DEFINE_GDBUS_ARG_INFO(name_, a_signature) \
+	NM_DEFINE_GDBUS_ARG_INFO_FULL ( \
+		name_, \
+		.signature = a_signature, \
+	)
+
+#define NM_DEFINE_GDBUS_ARG_INFOS(...) \
+	((GDBusArgInfo **) ((const GDBusArgInfo *[]) { \
+		__VA_ARGS__ \
+		NULL, \
+	}))
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFO(name_, ...) \
+	((GDBusPropertyInfo *) (&((const GDBusPropertyInfo) { \
+		.ref_count = -1, \
+		.name = name_, \
+		__VA_ARGS__ \
+	})))
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFO_READABLE(name_, m_signature) \
+	NM_DEFINE_GDBUS_PROPERTY_INFO ( \
+		name_, \
+		.signature = m_signature, \
+		.flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE, \
+	)
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFOS(...) \
+	((GDBusPropertyInfo **) ((const GDBusPropertyInfo *[]) { \
+		__VA_ARGS__ \
+		NULL, \
+	}))
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFO_INIT(name_, ...) \
+	{ \
+		.ref_count = -1, \
+		.name = name_, \
+		__VA_ARGS__ \
+	}
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFO(name_, ...) \
+	((GDBusSignalInfo *) (&((const GDBusSignalInfo) NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (name_, __VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFOS(...) \
+	((GDBusSignalInfo **) ((const GDBusSignalInfo *[]) { \
+		__VA_ARGS__ \
+		NULL, \
+	}))
+
+#define NM_DEFINE_GDBUS_METHOD_INFO_INIT(name_, ...) \
+	{ \
+		.ref_count = -1, \
+		.name = name_, \
+		__VA_ARGS__ \
+	}
+
+#define NM_DEFINE_GDBUS_METHOD_INFO(name_, ...) \
+	((GDBusMethodInfo *) (&((const GDBusMethodInfo) NM_DEFINE_GDBUS_METHOD_INFO_INIT (name_, __VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_METHOD_INFOS(...) \
+	((GDBusMethodInfo **) ((const GDBusMethodInfo *[]) { \
+		__VA_ARGS__ \
+		NULL, \
+	}))
+
+#define NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(name_, ...) \
+	{ \
+		.ref_count = -1, \
+		.name = name_, \
+		__VA_ARGS__ \
+	}
+
+#define NM_DEFINE_GDBUS_INTERFACE_INFO(name_, ...) \
+	((GDBusInterfaceInfo *) (&((const GDBusInterfaceInfo) NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (name_, __VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_INTERFACE_VTABLE(...) \
+	((GDBusInterfaceVTable *) (&((const GDBusInterfaceVTable) { \
+		__VA_ARGS__ \
+	})))
+
+/*****************************************************************************/
+
+guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
+
+/*****************************************************************************/
+
 #endif /* __NM_SHARED_UTILS_H__ */
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index 126546ec..cc33a1ae 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -21,6 +21,10 @@
 #ifndef __NM_TEST_UTILS_H__
 #define __NM_TEST_UTILS_H__
 
+#if defined(NETWORKMANAGER_COMPILATION) && !defined(NETWORKMANAGER_COMPILATION_TEST)
+#error Need to mark the compilation with NETWORKMANAGER_COMPILATION_TEST.
+#endif
+
 /*******************************************************************************
  * HOWTO run tests.
  *
@@ -158,6 +162,14 @@
 			g_assert_not_reached (); \
 	} G_STMT_END
 
+#define nmtst_assert_nonnull(command) \
+	({ \
+		typeof (*(command)) *_ptr = (command); \
+		\
+		g_assert (_ptr && (TRUE || (command))); \
+		_ptr; \
+	 })
+
 #define nmtst_assert_success(success, error) \
 	G_STMT_START { \
 		g_assert_no_error (error); \
@@ -328,8 +340,6 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_
 
 	__nmtst_internal.assert_logging = !!assert_logging;
 
-	nm_g_type_init ();
-
 	is_debug = g_test_verbose ();
 
 	nmtst_debug = g_getenv ("NMTST_DEBUG");
@@ -424,6 +434,11 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_
 			g_array_append_val (debug_messages, msg);
 		}
 	} else {
+		/* We're intentionally assigning a value to static variables
+		 * s_tests_x and p_tests_x without using it afterwards, just
+		 * so that valgrind doesn't complain about the leak. */
+		NM_PRAGMA_WARNING_DISABLE("-Wunused-but-set-variable")
+
 		/* g_test_init() is a variadic function, so we cannot pass it
 		 * (variadic) arguments. If you need to pass additional parameters,
 		 * call nmtst_init() with argc==NULL and call g_test_init() yourself. */
@@ -497,6 +512,8 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_
 				s_tests = NULL;
 			}
 		}
+
+		NM_PRAGMA_WARNING_REENABLE
 	}
 
 	if (test_quick_set)
@@ -529,13 +546,8 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_
 		*out_set_logging = TRUE;
 #endif
 		g_assert (success);
-#if GLIB_CHECK_VERSION(2,34,0)
 		if (__nmtst_internal.no_expect_message)
 			g_log_set_always_fatal (G_LOG_FATAL_MASK);
-#else
-		/* g_test_expect_message() is a NOP, so allow any messages */
-		g_log_set_always_fatal (G_LOG_FATAL_MASK);
-#endif
 	} else if (__nmtst_internal.no_expect_message) {
 		/* We have a test that would be assert_logging, but the user specified no_expect_message.
 		 * This transforms g_test_expect_message() into a NOP, but we also have to relax
@@ -555,14 +567,9 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_
 		}
 #endif
 	} else {
-#if GLIB_CHECK_VERSION(2,34,0)
 		/* We were called not to set logging levels. This means, that the user
 		 * expects to assert against (all) messages. Any uncought message is fatal. */
 		g_log_set_always_fatal (G_LOG_LEVEL_MASK);
-#else
-		/* g_test_expect_message() is a NOP, so allow any messages */
-		g_log_set_always_fatal (G_LOG_FATAL_MASK);
-#endif
 	}
 
 	if ((!__nmtst_internal.assert_logging || (__nmtst_internal.assert_logging && __nmtst_internal.no_expect_message)) &&
@@ -629,7 +636,6 @@ nmtst_test_quick (void)
 	return __nmtst_internal.test_quick;
 }
 
-#if GLIB_CHECK_VERSION(2,34,0)
 #undef g_test_expect_message
 #define g_test_expect_message(...) \
 	G_STMT_START { \
@@ -637,9 +643,7 @@ nmtst_test_quick (void)
 		if (__nmtst_internal.assert_logging && __nmtst_internal.no_expect_message) { \
 			g_debug ("nmtst: assert-logging: g_test_expect_message %s", G_STRINGIFY ((__VA_ARGS__))); \
 		} else { \
-			G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
 			g_test_expect_message (__VA_ARGS__); \
-			G_GNUC_END_IGNORE_DEPRECATIONS \
 		} \
 	} G_STMT_END
 #undef g_test_assert_expected_messages_internal
@@ -653,10 +657,21 @@ nmtst_test_quick (void)
 		if (__nmtst_internal.assert_logging && __nmtst_internal.no_expect_message) \
 			g_debug ("nmtst: assert-logging: g_test_assert_expected_messages(%s, %s:%d, %s)", _domain?:"", _file?:"", _line, _func?:""); \
 		\
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
 		g_test_assert_expected_messages_internal (_domain, _file, _line, _func); \
-		G_GNUC_END_IGNORE_DEPRECATIONS \
 	} G_STMT_END
+
+#define NMTST_EXPECT(domain, level, msg)        g_test_expect_message (domain, level, msg)
+
+#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_UTIL
+#define NMTST_EXPECT_LIBNM_U(level, msg)        NMTST_EXPECT ("libnm-util", level, msg)
+#define NMTST_EXPECT_LIBNM_G(level, msg)        NMTST_EXPECT ("libnm-glib", level, msg)
+
+#define NMTST_EXPECT_LIBNM_U_CRITICAL(msg)      NMTST_EXPECT_LIBNM_U (G_LOG_LEVEL_CRITICAL, msg)
+#define NMTST_EXPECT_LIBNM_G_CRITICAL(msg)      NMTST_EXPECT_LIBNM_G (G_LOG_LEVEL_CRITICAL, msg)
+#else
+#define NMTST_EXPECT_LIBNM(level, msg)          NMTST_EXPECT ("libnm", level, msg)
+
+#define NMTST_EXPECT_LIBNM_CRITICAL(msg)        NMTST_EXPECT_LIBNM (G_LOG_LEVEL_CRITICAL, msg)
 #endif
 
 /*****************************************************************************/
@@ -801,6 +816,12 @@ nmtst_get_rand_int (void)
 	return g_rand_int (nmtst_get_rand ());
 }
 
+static inline gboolean
+nmtst_get_rand_bool (void)
+{
+	return nmtst_get_rand_int () % 2;
+}
+
 static inline gpointer
 nmtst_rand_buf (GRand *rand, gpointer buffer, gsize buffer_length)
 {
@@ -912,7 +933,7 @@ _nmtst_main_loop_run_timeout (gpointer user_data)
 }
 
 static inline gboolean
-nmtst_main_loop_run (GMainLoop *loop, int timeout_ms)
+nmtst_main_loop_run (GMainLoop *loop, guint timeout_ms)
 {
 	GSource *source = NULL;
 	guint id = 0;
@@ -928,6 +949,9 @@ nmtst_main_loop_run (GMainLoop *loop, int timeout_ms)
 
 	g_main_loop_run (loop);
 
+	if (source && loopx)
+		g_source_destroy (source);
+
 	/* if the timeout was reached, return FALSE. */
 	return loopx != NULL;
 }
@@ -1499,13 +1523,12 @@ _nmtst_connection_normalize (NMConnection *connection, ...)
 static inline NMConnection *
 _nmtst_connection_duplicate_and_normalize (NMConnection *connection, ...)
 {
-	gboolean was_modified;
 	va_list args;
 
 	connection = nmtst_clone_connection (connection);
 
 	va_start (args, connection);
-	was_modified = _nmtst_connection_normalize_v (connection, args);
+	_nmtst_connection_normalize_v (connection, args);
 	va_end (args);
 
 	return connection;
@@ -1697,7 +1720,7 @@ nmtst_assert_setting_verifies (NMSetting *setting)
 	g_assert (success);
 }
 
-#if defined(__NM_SIMPLE_CONNECTION_H__)
+#if defined(__NM_SIMPLE_CONNECTION_H__) && NM_CHECK_VERSION (1, 10, 0) && (!defined (NM_VERSION_MAX_ALLOWED) || NM_VERSION_MAX_ALLOWED >= NM_VERSION_1_10)
 static inline void
 _nmtst_assert_connection_has_settings (NMConnection *connection, gboolean has_at_least, gboolean has_at_most, ...)
 {
@@ -1715,7 +1738,7 @@ _nmtst_assert_connection_has_settings (NMConnection *connection, gboolean has_at
 
 	va_start (ap, has_at_most);
 	while ((name = va_arg (ap, const char *))) {
-		if (!nm_g_hash_table_add (names, (gpointer) name))
+		if (!g_hash_table_add (names, (gpointer) name))
 			g_assert_not_reached ();
 		g_ptr_array_add (names_arr, (gpointer) name);
 	}
@@ -1751,8 +1774,7 @@ _nmtst_assert_connection_has_settings (NMConnection *connection, gboolean has_at
 #define nmtst_assert_connection_has_settings(connection, ...)          _nmtst_assert_connection_has_settings ((connection), TRUE,  TRUE,  __VA_ARGS__, NULL)
 #define nmtst_assert_connection_has_settings_at_least(connection, ...) _nmtst_assert_connection_has_settings ((connection), TRUE,  FALSE, __VA_ARGS__, NULL)
 #define nmtst_assert_connection_has_settings_at_most(connection, ...)  _nmtst_assert_connection_has_settings ((connection), FALSE, TRUE,  __VA_ARGS__, NULL)
-
-#endif /* __NM_SIMPLE_CONNECTION_H__ */
+#endif
 
 static inline void
 nmtst_assert_setting_verify_fails (NMSetting *setting,
diff --git a/shared/nm-utils/nm-udev-utils.c b/shared/nm-utils/nm-udev-utils.c
index 79d4426d..709f7590 100644
--- a/shared/nm-utils/nm-udev-utils.c
+++ b/shared/nm-utils/nm-udev-utils.c
@@ -257,7 +257,7 @@ nm_udev_client_new (const char *const*subsystems,
 				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) monitor_event, self, NULL);
+				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/siphash24.c b/shared/nm-utils/siphash24.c
index 3a5a635d..8e59afb2 100644
--- a/shared/nm-utils/siphash24.c
+++ b/shared/nm-utils/siphash24.c
@@ -19,7 +19,8 @@
 
 #include "nm-default.h"
 
-#define assert(cond) nm_assert (cond)
+#define assert(cond)   nm_assert (cond)
+#define _fallthrough_  _nm_fallthrough
 
 #include <stdio.h>
 
@@ -130,25 +131,25 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
         switch (left) {
                 case 7:
                         state->padding |= ((uint64_t) in[6]) << 48;
-                        /* fall through */
+                        _fallthrough_;
                 case 6:
                         state->padding |= ((uint64_t) in[5]) << 40;
-                        /* fall through */
+                        _fallthrough_;
                 case 5:
                         state->padding |= ((uint64_t) in[4]) << 32;
-                        /* fall through */
+                        _fallthrough_;
                 case 4:
                         state->padding |= ((uint64_t) in[3]) << 24;
-                        /* fall through */
+                        _fallthrough_;
                 case 3:
                         state->padding |= ((uint64_t) in[2]) << 16;
-                        /* fall through */
+                        _fallthrough_;
                 case 2:
                         state->padding |= ((uint64_t) in[1]) <<  8;
-                        /* fall through */
+                        _fallthrough_;
                 case 1:
                         state->padding |= ((uint64_t) in[0]);
-                        /* fall through */
+                        _fallthrough_;
                 case 0:
                         break;
         }
diff --git a/shared/nm-utils/unaligned.h b/shared/nm-utils/unaligned.h
index 7c847a3c..73302b42 100644
--- a/shared/nm-utils/unaligned.h
+++ b/shared/nm-utils/unaligned.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -25,89 +26,77 @@
 /* BE */
 
 static inline uint16_t unaligned_read_be16(const void *_u) {
-        const uint8_t *u = _u;
+        const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
 
-        return (((uint16_t) u[0]) << 8) |
-                ((uint16_t) u[1]);
+        return be16toh(u->x);
 }
 
 static inline uint32_t unaligned_read_be32(const void *_u) {
-        const uint8_t *u = _u;
+        const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
 
-        return (((uint32_t) unaligned_read_be16(u)) << 16) |
-                ((uint32_t) unaligned_read_be16(u + 2));
+        return be32toh(u->x);
 }
 
 static inline uint64_t unaligned_read_be64(const void *_u) {
-        const uint8_t *u = _u;
+        const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
 
-        return (((uint64_t) unaligned_read_be32(u)) << 32) |
-                ((uint64_t) unaligned_read_be32(u + 4));
+        return be64toh(u->x);
 }
 
 static inline void unaligned_write_be16(void *_u, uint16_t a) {
-        uint8_t *u = _u;
+        struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
 
-        u[0] = (uint8_t) (a >> 8);
-        u[1] = (uint8_t) a;
+        u->x = be16toh(a);
 }
 
 static inline void unaligned_write_be32(void *_u, uint32_t a) {
-        uint8_t *u = _u;
+        struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
 
-        unaligned_write_be16(u, (uint16_t) (a >> 16));
-        unaligned_write_be16(u + 2, (uint16_t) a);
+        u->x = be32toh(a);
 }
 
 static inline void unaligned_write_be64(void *_u, uint64_t a) {
-        uint8_t *u = _u;
+        struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
 
-        unaligned_write_be32(u, (uint32_t) (a >> 32));
-        unaligned_write_be32(u + 4, (uint32_t) a);
+        u->x = be64toh(a);
 }
 
 /* LE */
 
 static inline uint16_t unaligned_read_le16(const void *_u) {
-        const uint8_t *u = _u;
+        const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
 
-        return (((uint16_t) u[1]) << 8) |
-                ((uint16_t) u[0]);
+        return le16toh(u->x);
 }
 
 static inline uint32_t unaligned_read_le32(const void *_u) {
-        const uint8_t *u = _u;
+        const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
 
-        return (((uint32_t) unaligned_read_le16(u + 2)) << 16) |
-                ((uint32_t) unaligned_read_le16(u));
+        return le32toh(u->x);
 }
 
 static inline uint64_t unaligned_read_le64(const void *_u) {
-        const uint8_t *u = _u;
+        const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
 
-        return (((uint64_t) unaligned_read_le32(u + 4)) << 32) |
-                ((uint64_t) unaligned_read_le32(u));
+        return le64toh(u->x);
 }
 
 static inline void unaligned_write_le16(void *_u, uint16_t a) {
-        uint8_t *u = _u;
+        struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
 
-        u[0] = (uint8_t) a;
-        u[1] = (uint8_t) (a >> 8);
+        u->x = le16toh(a);
 }
 
 static inline void unaligned_write_le32(void *_u, uint32_t a) {
-        uint8_t *u = _u;
+        struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
 
-        unaligned_write_le16(u, (uint16_t) a);
-        unaligned_write_le16(u + 2, (uint16_t) (a >> 16));
+        u->x = le32toh(a);
 }
 
 static inline void unaligned_write_le64(void *_u, uint64_t a) {
-        uint8_t *u = _u;
+        struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
 
-        unaligned_write_le32(u, (uint32_t) a);
-        unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
+        u->x = le64toh(a);
 }
 
 #if __BYTE_ORDER == __BIG_ENDIAN