about summary refs log tree commit diff
path: root/shared
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-06-04 00:08:31 +0200
committerMichael Biebl <biebl@debian.org>2018-06-04 00:08:31 +0200
commit0dd9df69fdbd475c48a0c8d5b0a1882550fe7321 (patch)
tree249cf25643b1fe408e10679bb61613bc6540e894 /shared
parent2e94a3b93171ab3fb95bf689aab1664d23988809 (diff)
parent04bc9e1cd3544445d883ad29ea108c1645c8e7b7 (diff)
Update upstream source from tag 'upstream/1.11.4'
Update to upstream version '1.11.4'
with Debian dir d0638aa2e32d5bae4e8daa021b9a66b7c4d6647e
Diffstat (limited to 'shared')
-rw-r--r--shared/meson.build82
-rw-r--r--shared/n-acd/src/n-acd.c41
-rw-r--r--shared/n-acd/src/n-acd.h2
-rw-r--r--shared/nm-test-libnm-utils.h16
-rw-r--r--shared/nm-test-utils-impl.c148
-rw-r--r--shared/nm-utils/nm-enum-utils.c136
-rw-r--r--shared/nm-utils/nm-glib.h2
-rw-r--r--shared/nm-utils/nm-hash-utils.c16
-rw-r--r--shared/nm-utils/nm-hash-utils.h10
-rw-r--r--shared/nm-utils/nm-macros-internal.h11
-rw-r--r--shared/nm-utils/nm-random-utils.c2
-rw-r--r--shared/nm-utils/nm-shared-utils.c154
-rw-r--r--shared/nm-utils/nm-shared-utils.h37
-rw-r--r--shared/nm-utils/nm-test-utils.h24
-rw-r--r--shared/nm-utils/siphash24.c204
-rw-r--r--shared/nm-utils/siphash24.h23
-rw-r--r--shared/nm-utils/unaligned.h13
-rw-r--r--shared/nm-version-macros.h2
18 files changed, 520 insertions, 403 deletions
diff --git a/shared/meson.build b/shared/meson.build
index a812b588..db6b8a40 100644
--- a/shared/meson.build
+++ b/shared/meson.build
@@ -1,70 +1,78 @@
-shared_c_list_dep = declare_dependency(
-    include_directories: include_directories('c-list/src')
-)
+shared_inc = include_directories('.')
 
 shared_c_siphash = static_library(
     'c-siphash',
-    sources: ['c-siphash/src/c-siphash.c']
+    sources: 'c-siphash/src/c-siphash.c',
 )
 
 shared_c_siphash_dep = declare_dependency(
-    include_directories: include_directories('c-siphash/src'),
-    link_with: shared_c_siphash
+    include_directories: shared_inc,
+    link_with: shared_c_siphash,
 )
 
 shared_n_acd = static_library(
     'n-acd',
-    sources: ['n-acd/src/n-acd.c'],
-    dependencies: [ shared_c_siphash_dep, shared_c_list_dep ]
+    sources: 'n-acd/src/n-acd.c',
+    include_directories: [
+        include_directories('c-siphash/src'),
+        include_directories('c-list/src'),
+    ],
+    dependencies: shared_c_siphash_dep,
 )
 
 shared_n_acd_dep = declare_dependency(
-    include_directories: include_directories('.'),
+    include_directories: shared_inc,
     link_with: shared_n_acd,
 )
 
-shared_inc = include_directories('.')
-
 version_conf = configuration_data()
 version_conf.set('NM_MAJOR_VERSION', nm_major_version)
 version_conf.set('NM_MINOR_VERSION', nm_minor_version)
 version_conf.set('NM_MICRO_VERSION', nm_micro_version)
 
-version = 'nm-version-macros.h'
-
 version_header = configure_file(
-  input: version + '.in',
-  output: version,
-  configuration: version_conf
+    input: 'nm-version-macros.h.in',
+    output: 'nm-version-macros.h',
+    configuration: version_conf,
 )
 
-shared_meta_setting = files('nm-meta-setting.c')
+shared_nm_utils_nm_meta_setting_c = files('nm-meta-setting.c')
 
-shared_test_utils = files('nm-test-utils-impl.c')
+shared_nm_test_utils_impl_c = files('nm-test-utils-impl.c')
 
-shared_siphash = files('nm-utils/siphash24.c')
+shared_nm_utils_nm_vpn_plugin_utils_c = files('nm-utils/nm-vpn-plugin-utils.c')
 
-shared_udev_utils = files('nm-utils/nm-udev-utils.c')
+shared_files_libnm_core = files('''
+    c-siphash/src/c-siphash.c
+    nm-utils/c-list-util.c
+    nm-utils/nm-dedup-multi.c
+    nm-utils/nm-enum-utils.c
+    nm-utils/nm-hash-utils.c
+    nm-utils/nm-random-utils.c
+    nm-utils/nm-shared-utils.c
+    nm-utils/nm-udev-utils.c
+'''.split())
 
-shared_utils = files(
-  'nm-utils/nm-enum-utils.c',
-  'nm-utils/nm-hash-utils.c',
-  'nm-utils/nm-random-utils.c',
-  'nm-utils/nm-shared-utils.c'
-)
+shared_files_clients_common = files('''
+    c-siphash/src/c-siphash.c
+    nm-utils/nm-enum-utils.c
+    nm-utils/nm-hash-utils.c
+    nm-utils/nm-random-utils.c
+    nm-utils/nm-shared-utils.c
+'''.split())
 
-shared_vpn_plugin_utils = files('nm-utils/nm-vpn-plugin-utils.c')
+shared_files_libnm_util = files('''
+    nm-utils/nm-shared-utils.c
+'''.split())
 
-shared_sources = shared_utils + shared_meta_setting + shared_udev_utils + files(
-  'nm-utils/c-list-util.c',
-  'nm-utils/nm-dedup-multi.c'
-)
+shared_files_libnm_glib = files('''
+    nm-utils/nm-udev-utils.c
+'''.split())
 
 shared_dep = declare_dependency(
-  include_directories: [
-    top_inc,
-    shared_inc,
-    include_directories('nm-utils')
-  ],
-  dependencies: glib_dep
+    include_directories: [
+        top_inc,
+        shared_inc,
+    ],
+    dependencies: glib_dep,
 )
diff --git a/shared/n-acd/src/n-acd.c b/shared/n-acd/src/n-acd.c
index ae149abb..9164f958 100644
--- a/shared/n-acd/src/n-acd.c
+++ b/shared/n-acd/src/n-acd.c
@@ -534,7 +534,11 @@ static int n_acd_handle_timeout(NAcd *acd) {
                          */
 
                         r = n_acd_send(acd, NULL);
-                        if (r < 0)
+                        /*
+                         * During probe we must respect the total timeout and so
+                         * we ignore errors caused by a down interface.
+                         */
+                        if (r < 0 && r != -N_ACD_E_DOWN)
                                 return r;
 
                         if (++acd->n_iteration >= N_ACD_RFC_PROBE_NUM)
@@ -559,11 +563,26 @@ static int n_acd_handle_timeout(NAcd *acd) {
                  */
 
                 r = n_acd_send(acd, &acd->config.ip);
-                if (r < 0)
-                        return r;
+                if (r < 0) {
+                        if (r != -N_ACD_E_DOWN)
+                                return r;
+                        /*
+                         * We want to send all the 3 announcements even if the
+                         * interface goes temporarily down. Therefore, if send()
+                         * fails, don't increment the iteration and try again.
+                         */
+                } else
+                        acd->n_iteration++;
 
-                if (++acd->n_iteration < N_ACD_RFC_ANNOUNCE_NUM) {
-                        r = n_acd_schedule(acd, acd->timeout_multiplier * N_ACD_RFC_ANNOUNCE_INTERVAL_USEC, 0);
+                if (acd->n_iteration < N_ACD_RFC_ANNOUNCE_NUM) {
+                        /*
+                         * Announcements are always scheduled according to the
+                         * time-intervals specified in the spec. We always use
+                         * the RFC5227-mandated multiplier.
+                         * If you reconsider this, note that timeout_multiplier
+                         * might be 0 here.
+                         */
+                        r = n_acd_schedule(acd, N_ACD_TIMEOUT_RFC5227 * N_ACD_RFC_ANNOUNCE_INTERVAL_USEC, 0);
                         if (r < 0)
                                 return r;
                 }
@@ -803,14 +822,12 @@ static int n_acd_dispatch_socket(NAcd *acd, struct epoll_event *event) {
                         return -EIO;
                 } else if (errno == ENETDOWN || errno == ENXIO) {
                         /*
-                         * We get ENETDOWN if the network-device goes down or is
-                         * removed. ENXIO might happen on async send-operations if the
-                         * network-device was unplugged and thus the kernel is no
-                         * longer aware of it.
-                         * In any case, we do not allow proceeding with this socket. We
-                         * stop the engine and notify the user gracefully.
+                         * The network device went down or was removed. Ignore
+                         * such errors and let the pending probe time out.
+                         * Subsequent reads will simply return EAGAIN until the
+                         * device is up again and has data queued.
                          */
-                        return -N_ACD_E_DOWN;
+                        return 0;
                 } else if (errno == EAGAIN) {
                         /*
                          * We cannot read data from the socket (we got EAGAIN). As a safety net
diff --git a/shared/n-acd/src/n-acd.h b/shared/n-acd/src/n-acd.h
index 46394dca..75646243 100644
--- a/shared/n-acd/src/n-acd.h
+++ b/shared/n-acd/src/n-acd.h
@@ -15,6 +15,8 @@ extern "C" {
 #include <netinet/in.h>
 #include <stdbool.h>
 
+#define N_ACD_TIMEOUT_RFC5227 (UINT64_C(9000))
+
 enum {
         _N_ACD_E_SUCCESS,
 
diff --git a/shared/nm-test-libnm-utils.h b/shared/nm-test-libnm-utils.h
index c4731a52..20a15e5f 100644
--- a/shared/nm-test-libnm-utils.h
+++ b/shared/nm-test-libnm-utils.h
@@ -42,18 +42,22 @@ typedef struct {
 
 NMTstcServiceInfo *nmtstc_service_init (void);
 void nmtstc_service_cleanup (NMTstcServiceInfo *info);
+NMTstcServiceInfo *nmtstc_service_available (NMTstcServiceInfo *info);
 
 static inline void _nmtstc_auto_service_cleanup (NMTstcServiceInfo **info)
 {
-	if (info && *info) {
-		nmtstc_service_cleanup (*info);
-		*info = NULL;
-	}
+	nmtstc_service_cleanup (g_steal_pointer (info));
 }
-
 #define NMTSTC_SERVICE_INFO_SETUP(sinfo) \
 	NM_PRAGMA_WARNING_DISABLE ("-Wunused-variable") \
-	__attribute__ ((cleanup(_nmtstc_auto_service_cleanup))) NMTstcServiceInfo *sinfo = nmtstc_service_init (); \
+	__attribute__ ((cleanup(_nmtstc_auto_service_cleanup))) NMTstcServiceInfo *sinfo = ({ \
+		NMTstcServiceInfo *_sinfo; \
+		\
+		_sinfo = nmtstc_service_init (); \
+		if (!nmtstc_service_available (_sinfo)) \
+			return; \
+		_sinfo; \
+	}); \
 	NM_PRAGMA_WARNING_REENABLE
 
 /*****************************************************************************/
diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c
index 998d792a..3add88c7 100644
--- a/shared/nm-test-utils-impl.c
+++ b/shared/nm-test-utils-impl.c
@@ -21,12 +21,15 @@
 #include "nm-default.h"
 
 #include <string.h>
+#include <sys/wait.h>
 
 #include "NetworkManager.h"
 #include "nm-dbus-compat.h"
 
 #include "nm-test-libnm-utils.h"
 
+#define NMTSTC_NM_SERVICE NM_BUILD_SRCDIR"/tools/test-networkmanager-service.py"
+
 /*****************************************************************************/
 
 static gboolean
@@ -72,13 +75,60 @@ _libdbus_create_proxy_test (DBusGConnection *bus)
 }
 #endif
 
+typedef struct {
+	GMainLoop *mainloop;
+	GDBusConnection *bus;
+	int exit_code;
+	bool exited:1;
+	bool name_found:1;
+} ServiceInitWaitData;
+
+static gboolean
+_service_init_wait_probe_name (gpointer user_data)
+{
+	ServiceInitWaitData *data = user_data;
+
+	if (!name_exists (data->bus, "org.freedesktop.NetworkManager"))
+		return G_SOURCE_CONTINUE;
+
+	data->name_found = TRUE;
+	g_main_loop_quit (data->mainloop);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+_service_init_wait_child_wait (GPid pid,
+                               gint status,
+                               gpointer user_data)
+{
+	ServiceInitWaitData *data = user_data;
+
+	data->exited = TRUE;
+	data->exit_code = status;
+	g_main_loop_quit (data->mainloop);
+}
+
+NMTstcServiceInfo *
+nmtstc_service_available (NMTstcServiceInfo *info)
+{
+	gs_free char *m = NULL;
+
+	if (info)
+		return info;
+
+	/* This happens, when test-networkmanager-service.py exits with 77 status
+	 * code. */
+	m = g_strdup_printf ("missing dependency for running NetworkManager stub service %s", NMTSTC_NM_SERVICE);
+	g_test_skip (m);
+	return NULL;
+}
+
 NMTstcServiceInfo *
 nmtstc_service_init (void)
 {
 	NMTstcServiceInfo *info;
-	const char *args[] = { TEST_NM_PYTHON, TEST_NM_SERVICE, NULL };
+	const char *args[] = { TEST_NM_PYTHON, NMTSTC_NM_SERVICE, NULL };
 	GError *error = NULL;
-	int i;
 
 	info = g_malloc0 (sizeof (*info));
 
@@ -90,18 +140,55 @@ nmtstc_service_init (void)
 	 * make sure the service exits if the test program crashes.
 	 */
 	g_spawn_async_with_pipes (NULL, (char **) args, NULL,
-	                          G_SPAWN_SEARCH_PATH,
+	                            G_SPAWN_SEARCH_PATH
+	                          | G_SPAWN_DO_NOT_REAP_CHILD,
 	                          NULL, NULL,
 	                          &info->pid, &info->keepalive_fd, NULL, NULL, &error);
 	g_assert_no_error (error);
 
-	/* Wait until the service is registered on the bus */
-	for (i = 1000; i > 0; i--) {
-		if (name_exists (info->bus, "org.freedesktop.NetworkManager"))
-			break;
-		g_usleep (G_USEC_PER_SEC / 50);
+	{
+		nm_auto_unref_gsource GSource *timeout_source = NULL;
+		nm_auto_unref_gsource GSource *child_source = NULL;
+		GMainContext *context = g_main_context_new ();
+		ServiceInitWaitData data = {
+			.bus = info->bus,
+			.mainloop = g_main_loop_new (context, FALSE),
+		};
+		gboolean had_timeout;
+
+		timeout_source = g_timeout_source_new (50);
+		g_source_set_callback (timeout_source, _service_init_wait_probe_name, &data, NULL);
+		g_source_attach (timeout_source, context);
+
+		child_source = g_child_watch_source_new (info->pid);
+		g_source_set_callback (child_source, (GSourceFunc)(void (*) (void)) _service_init_wait_child_wait, &data, NULL);
+		g_source_attach (child_source, context);
+
+		had_timeout = !nmtst_main_loop_run (data.mainloop, 3000);
+
+		g_source_destroy (timeout_source);
+		g_source_destroy (child_source);
+		g_main_loop_unref (data.mainloop);
+		g_main_context_unref (context);
+
+		if (had_timeout)
+			g_error ("test service %s did not start in time", NMTSTC_NM_SERVICE);
+		if (!data.name_found) {
+			g_assert (data.exited);
+			info->pid = NM_PID_T_INVAL;
+			nmtstc_service_cleanup (info);
+
+			if (   WIFEXITED (data.exit_code)
+			    && WEXITSTATUS (data.exit_code) == 77) {
+				/* If the stub service exited with status 77 it means that it decided
+				 * that it cannot conduct the tests and the test should be (gracefully)
+				 * skip. The likely reason for that, is that libnm is not available
+				 * via pygobject. */
+				return NULL;
+			}
+			g_error ("test service %s exited with error code %d", NMTSTC_NM_SERVICE, data.exit_code);
+		}
 	}
-	g_assert (i > 0);
 
 	/* Grab a proxy to our fake NM service to trigger tests */
 	info->proxy = g_dbus_proxy_new_sync (info->bus,
@@ -126,26 +213,45 @@ nmtstc_service_init (void)
 void
 nmtstc_service_cleanup (NMTstcServiceInfo *info)
 {
-	int i;
+	int ret;
+	gint64 t;
+	int status;
 
-	g_object_unref (info->proxy);
-	kill (info->pid, SIGTERM);
+	if (!info)
+		return;
 
-	/* Wait until the bus notices the service is gone */
-	for (i = 100; i > 0; i--) {
-		if (!name_exists (info->bus, "org.freedesktop.NetworkManager"))
-			break;
-		g_usleep (G_USEC_PER_SEC / 50);
-	}
-	g_assert (i > 0);
+	nm_close (nm_steal_fd (&info->keepalive_fd));
 
-	g_object_unref (info->bus);
-	nm_close (info->keepalive_fd);
+	g_clear_object (&info->proxy);
 
 #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_GLIB
 	g_clear_pointer (&info->libdbus.bus, dbus_g_connection_unref);
 #endif
 
+	if (info->pid != NM_PID_T_INVAL) {
+		kill (info->pid, SIGTERM);
+
+		t = g_get_monotonic_time ();
+again_wait:
+		ret = waitpid (info->pid, &status, WNOHANG);
+		if (ret == 0) {
+			if (t + 2000000 < g_get_monotonic_time ()) {
+				kill (info->pid, SIGKILL);
+				g_error ("child process %lld did not exit within timeout", (long long) info->pid);
+			}
+			g_usleep (G_USEC_PER_SEC / 50);
+			goto again_wait;
+		}
+		if (ret == -1 && errno == EINTR)
+			goto again_wait;
+
+		g_assert (ret == info->pid);
+	}
+
+	g_assert (!name_exists (info->bus, "org.freedesktop.NetworkManager"));
+
+	g_clear_object (&info->bus);
+
 	memset (info, 0, sizeof (*info));
 	g_free (info);
 }
diff --git a/shared/nm-utils/nm-enum-utils.c b/shared/nm-utils/nm-enum-utils.c
index b9bc6e88..023f8385 100644
--- a/shared/nm-utils/nm-enum-utils.c
+++ b/shared/nm-utils/nm-enum-utils.c
@@ -27,6 +27,62 @@
 
 #define IS_FLAGS_SEPARATOR(ch)  (NM_IN_SET ((ch), ' ', '\t', ',', '\n', '\r'))
 
+static void
+_ASSERT_enum_values_info (GType type,
+                          const NMUtilsEnumValueInfo *value_infos)
+{
+#if NM_MORE_ASSERTS > 5
+	nm_auto_unref_gtypeclass GTypeClass *klass = NULL;
+	gs_unref_hashtable GHashTable *ht = NULL;
+
+	klass = g_type_class_ref (type);
+
+	g_assert (G_IS_ENUM_CLASS (klass) || G_IS_FLAGS_CLASS (klass));
+
+	if (!value_infos)
+		return;
+
+	ht = g_hash_table_new (g_str_hash, g_str_equal);
+
+	for (; value_infos->nick; value_infos++) {
+
+		g_assert (value_infos->nick[0]);
+
+		/* duplicate nicks make no sense!! */
+		g_assert (!g_hash_table_contains (ht, value_infos->nick));
+		g_hash_table_add (ht, (gpointer) value_infos->nick);
+
+		if (G_IS_ENUM_CLASS (klass)) {
+			GEnumValue *enum_value;
+
+			enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (klass), value_infos->nick);
+			if (enum_value) {
+				/* we do allow specifying the same name via @value_infos and @type.
+				 * That might make sense, if @type comes from a library where older versions
+				 * of the library don't yet support the value. In this case, the caller can
+				 * provide the nick via @value_infos, to support the older library version.
+				 * And then, when actually running against a newer library version where
+				 * @type knows the nick, we have this situation.
+				 *
+				 * However, what never is allowed, is to use a name (nick) to re-number
+				 * the value. That is, if both @value_infos and @type contain a particular
+				 * nick, their numeric values must agree as well.
+				 */
+				g_assert (enum_value->value == value_infos->value);
+			}
+		} else {
+			GFlagsValue *flags_value;
+
+			flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (klass), value_infos->nick);
+			if (flags_value) {
+				/* see ENUM case above. */
+				g_assert (flags_value->value == (guint) value_infos->value);
+			}
+		}
+	}
+#endif
+}
+
 static gboolean
 _is_hex_string (const char *str)
 {
@@ -67,16 +123,18 @@ _nm_utils_enum_to_str_full (GType type,
                             const char *flags_separator,
                             const NMUtilsEnumValueInfo *value_infos)
 {
-	nm_auto_unref_gtypeclass GTypeClass *class = NULL;
+	nm_auto_unref_gtypeclass GTypeClass *klass = NULL;
+
+	_ASSERT_enum_values_info (type, value_infos);
 
 	if (   flags_separator
 	    && (   !flags_separator[0]
 	        || NM_STRCHAR_ANY (flags_separator, ch, !IS_FLAGS_SEPARATOR (ch))))
 		g_return_val_if_reached (NULL);
 
-	class = g_type_class_ref (type);
+	klass = g_type_class_ref (type);
 
-	if (G_IS_ENUM_CLASS (class)) {
+	if (G_IS_ENUM_CLASS (klass)) {
 		GEnumValue *enum_value;
 
 		for ( ; value_infos && value_infos->nick; value_infos++) {
@@ -84,13 +142,13 @@ _nm_utils_enum_to_str_full (GType type,
 				return g_strdup (value_infos->nick);
 		}
 
-		enum_value = g_enum_get_value (G_ENUM_CLASS (class), value);
+		enum_value = g_enum_get_value (G_ENUM_CLASS (klass), value);
 		if (   !enum_value
 		    || !_enum_is_valid_enum_nick (enum_value->value_nick))
 			return g_strdup_printf ("%d", value);
 		else
 			return g_strdup (enum_value->value_nick);
-	} else if (G_IS_FLAGS_CLASS (class)) {
+	} else if (G_IS_FLAGS_CLASS (klass)) {
 		GFlagsValue *flags_value;
 		GString *str = g_string_new ("");
 		unsigned uvalue = (unsigned) value;
@@ -120,7 +178,7 @@ _nm_utils_enum_to_str_full (GType type,
 		}
 
 		do {
-			flags_value = g_flags_get_first_value (G_FLAGS_CLASS (class), uvalue);
+			flags_value = g_flags_get_first_value (G_FLAGS_CLASS (klass), uvalue);
 			if (str->len)
 				g_string_append (str, flags_separator);
 			if (   !flags_value
@@ -159,7 +217,7 @@ _nm_utils_enum_from_str_full (GType type,
                               char **err_token,
                               const NMUtilsEnumValueInfo *value_infos)
 {
-	GTypeClass *class;
+	GTypeClass *klass;
 	gboolean ret = FALSE;
 	int value = 0;
 	gs_free char *str_clone = NULL;
@@ -169,13 +227,15 @@ _nm_utils_enum_from_str_full (GType type,
 
 	g_return_val_if_fail (str, FALSE);
 
+	_ASSERT_enum_values_info (type, value_infos);
+
 	str_clone = strdup (str);
 	s = nm_str_skip_leading_spaces (str_clone);
 	g_strchomp (s);
 
-	class = g_type_class_ref (type);
+	klass = g_type_class_ref (type);
 
-	if (G_IS_ENUM_CLASS (class)) {
+	if (G_IS_ENUM_CLASS (klass)) {
 		GEnumValue *enum_value;
 
 		if (s[0]) {
@@ -191,21 +251,15 @@ _nm_utils_enum_from_str_full (GType type,
 					value = (int) v64;
 					ret = TRUE;
 				}
-			} else {
-				enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (class), s);
-				if (enum_value) {
-					value = enum_value->value;
-					ret = TRUE;
-				} else {
-					nick = _find_value_info (value_infos, s);
-					if (nick) {
-						value = nick->value;
-						ret = TRUE;
-					}
-				}
+			} else if ((nick = _find_value_info (value_infos, s))) {
+				value = nick->value;
+				ret = TRUE;
+			} else if ((enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (klass), s))) {
+				value = enum_value->value;
+				ret = TRUE;
 			}
 		}
-	} else if (G_IS_FLAGS_CLASS (class)) {
+	} else if (G_IS_FLAGS_CLASS (klass)) {
 		GFlagsValue *flags_value;
 		unsigned uvalue = 0;
 
@@ -236,19 +290,13 @@ _nm_utils_enum_from_str_full (GType type,
 						break;
 					}
 					uvalue |= (unsigned) v64;
-				} else {
-					flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (class), s);
-					if (flags_value)
-						uvalue |= flags_value->value;
-					else {
-						nick = _find_value_info (value_infos, s);
-						if (nick)
-							uvalue = (unsigned) nick->value;
-						else {
-							ret = FALSE;
-							break;
-						}
-					}
+				} else if ((nick = _find_value_info (value_infos, s)))
+					uvalue |= (unsigned) nick->value;
+				else if ((flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (klass), s)))
+					uvalue |= flags_value->value;
+				else {
+					ret = FALSE;
+					break;
 				}
 			}
 
@@ -261,23 +309,23 @@ _nm_utils_enum_from_str_full (GType type,
 
 	NM_SET_OUT (err_token, !ret && s[0] ? g_strdup (s) : NULL);
 	NM_SET_OUT (out_value, ret ? value : 0);
-	g_type_class_unref (class);
+	g_type_class_unref (klass);
 	return ret;
 }
 
 const char **
 _nm_utils_enum_get_values (GType type, gint from, gint to)
 {
-	GTypeClass *class;
+	GTypeClass *klass;
 	GPtrArray *array;
 	gint i;
 	char sbuf[64];
 
-	class = g_type_class_ref (type);
+	klass = g_type_class_ref (type);
 	array = g_ptr_array_new ();
 
-	if (G_IS_ENUM_CLASS (class)) {
-		GEnumClass *enum_class = G_ENUM_CLASS (class);
+	if (G_IS_ENUM_CLASS (klass)) {
+		GEnumClass *enum_class = G_ENUM_CLASS (klass);
 		GEnumValue *enum_value;
 
 		for (i = 0; i < enum_class->n_values; i++) {
@@ -289,8 +337,8 @@ _nm_utils_enum_get_values (GType type, gint from, gint to)
 					g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "%d", enum_value->value)));
 			}
 		}
-	} else if (G_IS_FLAGS_CLASS (class)) {
-		GFlagsClass *flags_class = G_FLAGS_CLASS (class);
+	} else if (G_IS_FLAGS_CLASS (klass)) {
+		GFlagsClass *flags_class = G_FLAGS_CLASS (klass);
 		GFlagsValue *flags_value;
 
 		for (i = 0; i < flags_class->n_values; i++) {
@@ -303,12 +351,12 @@ _nm_utils_enum_get_values (GType type, gint from, gint to)
 			}
 		}
 	} else {
-		g_type_class_unref (class);
+		g_type_class_unref (klass);
 		g_ptr_array_free (array, TRUE);
 		g_return_val_if_reached (NULL);
 	}
 
-	g_type_class_unref (class);
+	g_type_class_unref (klass);
 	g_ptr_array_add (array, NULL);
 
 	return (const char **) g_ptr_array_free (array, FALSE);
diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h
index f1498dc4..010f1820 100644
--- a/shared/nm-utils/nm-glib.h
+++ b/shared/nm-utils/nm-glib.h
@@ -20,7 +20,6 @@
 #ifndef __NM_GLIB_H__
 #define __NM_GLIB_H__
 
-
 #include <gio/gio.h>
 #include <string.h>
 
@@ -86,7 +85,6 @@ g_steal_pointer (gpointer pp)
   (0 ? (*(pp)) : (g_steal_pointer) (pp))
 #endif
 
-
 static inline gboolean
 _nm_g_strv_contains (const gchar * const *strv,
                      const gchar         *str)
diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c
index 8d8c21ce..4bc12b7c 100644
--- a/shared/nm-utils/nm-hash-utils.c
+++ b/shared/nm-utils/nm-hash-utils.c
@@ -28,8 +28,6 @@
 #include "nm-shared-utils.h"
 #include "nm-random-utils.h"
 
-#include "siphash24.c"
-
 /*****************************************************************************/
 
 #define HASH_KEY_SIZE 16u
@@ -49,7 +47,7 @@ _get_hash_key_init (void)
 	} g_arr _nm_alignas (guint64);
 	static gsize g_lock;
 	const guint8 *g;
-	struct siphash siph_state;
+	CSipHash siph_state;
 	uint64_t h;
 	guint *p;
 
@@ -66,9 +64,9 @@ _get_hash_key_init (void)
 		/* 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);
+		c_siphash_init (&siph_state, g_arr.v8);
+		c_siphash_append (&siph_state, g_arr.v8, sizeof (g_arr.v8));
+		h = c_siphash_finalize (&siph_state);
 		p = (guint *) g_arr.v8;
 		if (sizeof (guint) < sizeof (h))
 			*p = *p ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32));
@@ -97,10 +95,10 @@ 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.
+	 * We don't use siphash, 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()).
+	 * full siphash).
 	 * Instead, different callers may set a different static_seed
 	 * so that nm_hash_str(NULL) != nm_hash_ptr(NULL).
 	 *
@@ -121,7 +119,7 @@ nm_hash_init (NMHashState *state, guint static_seed)
 	g = _get_hash_key ();
 	memcpy (seed, g, HASH_KEY_SIZE);
 	seed[0] ^= static_seed;
-	siphash24_init (&state->_state, (const guint8 *) seed);
+	c_siphash_init (&state->_state, (const guint8 *) seed);
 }
 
 guint
diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h
index 3bd3f652..b7742e0f 100644
--- a/shared/nm-utils/nm-hash-utils.h
+++ b/shared/nm-utils/nm-hash-utils.h
@@ -22,11 +22,11 @@
 #ifndef __NM_HASH_UTILS_H__
 #define __NM_HASH_UTILS_H__
 
-#include "siphash24.h"
+#include "c-siphash/src/c-siphash.h"
 #include "nm-macros-internal.h"
 
 struct _NMHashState {
-	struct siphash _state;
+	CSipHash _state;
 };
 
 typedef struct _NMHashState NMHashState;
@@ -42,7 +42,7 @@ nm_hash_complete (NMHashState *state)
 
 	nm_assert (state);
 
-	h = siphash24_finalize (&state->_state);
+	h = c_siphash_finalize (&state->_state);
 
 	/* we don't ever want to return a zero hash.
 	 *
@@ -57,7 +57,7 @@ nm_hash_update (NMHashState *state, const void *ptr, gsize n)
 	nm_assert (ptr);
 	nm_assert (n > 0);
 
-	siphash24_compress (ptr, n, &state->_state);
+	c_siphash_append (&state->_state, ptr, n);
 }
 
 #define nm_hash_update_val(state, val) \
@@ -168,7 +168,7 @@ nm_hash_update_mem (NMHashState *state, const void *ptr, gsize n)
 	 * instead. */
 	nm_hash_update (state, &n, sizeof (n));
 	if (n > 0)
-		siphash24_compress (ptr, n, &state->_state);
+		c_siphash_append (&state->_state, ptr, n);
 }
 
 static inline void
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index cc7205a4..908b25fd 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -153,6 +153,14 @@ _nm_auto_protect_errno (int *p_saved_errno)
 }
 #define NM_AUTO_PROTECT_ERRNO(errsv_saved) nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno)
 
+static inline void
+_nm_auto_unref_gsource (GSource **ptr)
+{
+	if (*ptr)
+		g_source_unref (g_steal_pointer (ptr));
+}
+#define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource)
+
 /*****************************************************************************/
 
 /* http://stackoverflow.com/a/11172679 */
@@ -1291,7 +1299,6 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
 #define false   0
 #endif
 
-
 #ifdef _G_BOOLEAN_EXPR
 /* g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR().
  * As glib's implementation uses a local variable _g_boolean_var_,
@@ -1368,4 +1375,6 @@ nm_close (int fd)
 	return r;
 }
 
+#define NM_PID_T_INVAL ((pid_t) -1)
+
 #endif /* __NM_MACROS_INTERNAL_H__ */
diff --git a/shared/nm-utils/nm-random-utils.c b/shared/nm-utils/nm-random-utils.c
index 65986b3d..3e968a8e 100644
--- a/shared/nm-utils/nm-random-utils.c
+++ b/shared/nm-utils/nm-random-utils.c
@@ -52,7 +52,7 @@
  * value.
  *
  * Note that if calling getrandom() fails because there is not enough
- * entroy (at early boot), the function will read /dev/urandom.
+ * entropy (at early boot), the function will read /dev/urandom.
  * Which of course, still has low entropy, and cause kernel to log
  * a warning.
  */
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 6937065c..d0019c11 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -549,6 +549,108 @@ nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_
 
 /*****************************************************************************/
 
+const char *
+nm_utils_dbus_path_get_last_component (const char *dbus_path)
+{
+	if (dbus_path) {
+		dbus_path = strrchr (dbus_path, '/');
+		if (dbus_path)
+			return dbus_path + 1;
+	}
+	return NULL;
+}
+
+static gint64
+_dbus_path_component_as_num (const char *p)
+{
+	gint64 n;
+
+	/* no odd stuff. No leading zeros, only a non-negative, decimal integer.
+	 *
+	 * Otherwise, there would be multiple ways to encode the same number "10"
+	 * and "010". That is just confusing. A number has no leading zeros,
+	 * if it has, it's not a number (as far as we are concerned here). */
+	if (p[0] == '0') {
+		if (p[1] != '\0')
+			return -1;
+		else
+			return 0;
+	}
+	if (!(p[0] >= '1' && p[0] <= '9'))
+		return -1;
+	if (!NM_STRCHAR_ALL (&p[1], ch, (ch >= '0' && ch <= '9')))
+		return -1;
+	n = _nm_utils_ascii_str_to_int64 (p, 10, 0, G_MAXINT64, -1);
+	nm_assert (n == -1 || nm_streq0 (p, nm_sprintf_bufa (100, "%"G_GINT64_FORMAT, n)));
+	return n;
+}
+
+int
+nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b)
+{
+	const char *l_a, *l_b;
+	gsize plen;
+	gint64 n_a, n_b;
+
+	/* compare function for two D-Bus paths. It behaves like
+	 * strcmp(), except, if both paths have the same prefix,
+	 * and both end in a (positive) number, then the paths
+	 * will be sorted by number. */
+
+	NM_CMP_SELF (dbus_path_a, dbus_path_b);
+
+	/* if one or both paths have no slash (and no last component)
+	 * compare the full paths directly. */
+	if (   !(l_a = nm_utils_dbus_path_get_last_component (dbus_path_a))
+	    || !(l_b = nm_utils_dbus_path_get_last_component (dbus_path_b)))
+		goto comp_full;
+
+	/* check if both paths have the same prefix (up to the last-component). */
+	plen = l_a - dbus_path_a;
+	if (plen != (l_b - dbus_path_b))
+		goto comp_full;
+	NM_CMP_RETURN (strncmp (dbus_path_a, dbus_path_b, plen));
+
+	n_a = _dbus_path_component_as_num (l_a);
+	n_b = _dbus_path_component_as_num (l_b);
+	if (n_a == -1 && n_b == -1)
+		goto comp_l;
+
+	/* both components must be convertiable to a number. If they are not,
+	 * (and only one of them is), then we must always strictly sort numeric parts
+	 * after non-numeric components. If we wouldn't, we wouldn't have
+	 * a total order.
+	 *
+	 * An example of a not total ordering would be:
+	 *   "8"   < "010"  (numeric)
+	 *   "0x"  < "8"    (lexical)
+	 *   "0x"  > "010"  (lexical)
+	 * We avoid this, by forcing that a non-numeric entry "0x" always sorts
+	 * before numeric entries.
+	 *
+	 * Additionally, _dbus_path_component_as_num() would also reject "010" as
+	 * not a valid number.
+	 */
+	if (n_a == -1)
+		return -1;
+	if (n_b == -1)
+		return 1;
+
+	NM_CMP_DIRECT (n_a, n_b);
+	nm_assert (nm_streq (dbus_path_a, dbus_path_b));
+	return 0;
+
+comp_full:
+	NM_CMP_DIRECT_STRCMP0 (dbus_path_a, dbus_path_b);
+	return 0;
+comp_l:
+	NM_CMP_DIRECT_STRCMP0 (l_a, l_b);
+	nm_assert (nm_streq (dbus_path_a, dbus_path_b));
+	return 0;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_utils_strsplit_set:
  * @str: the string to split.
@@ -1269,8 +1371,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 	char filename[256];
 	gs_free gchar *contents = NULL;
 	size_t length;
-	gs_strfreev gchar **tokens = NULL;
-	guint num_tokens;
+	gs_free const char **tokens = NULL;
 	gchar *p;
 	char state = ' ';
 	gint64 ppid = 0;
@@ -1290,7 +1391,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 	 * processes trying to fool us
 	 */
 	p = strrchr (contents, ')');
-	if (p == NULL)
+	if (!p)
 		goto fail;
 	p += 2; /* skip ') ' */
 	if (p - contents >= (int) length)
@@ -1298,11 +1399,9 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 
 	state = p[0];
 
-	tokens = g_strsplit (p, " ", 0);
+	tokens = nm_utils_strsplit_set (p, " ");
 
-	num_tokens = g_strv_length (tokens);
-
-	if (num_tokens < 20)
+	if (NM_PTRARRAY_LEN (tokens) < 20)
 		goto fail;
 
 	if (out_ppid) {
@@ -1359,3 +1458,44 @@ _nm_utils_strv_sort (const char **strv, gssize len)
 	                   nm_strcmp_p_with_data,
 	                   NULL);
 }
+
+/*****************************************************************************/
+
+gpointer
+_nm_utils_user_data_pack (int nargs, gconstpointer *args)
+{
+	int i;
+	gpointer *data;
+
+	nm_assert (nargs > 0);
+	nm_assert (args);
+
+	data = g_slice_alloc (((gsize) nargs) * sizeof (gconstpointer));
+	for (i = 0; i < nargs; i++)
+		data[i] = (gpointer) args[i];
+	return data;
+}
+
+void
+_nm_utils_user_data_unpack (gpointer user_data, int nargs, ...)
+{
+	gpointer *data = user_data;
+	va_list ap;
+	int i;
+
+	nm_assert (data);
+	nm_assert (nargs > 0);
+
+	va_start (ap, nargs);
+	for (i = 0; i < nargs; i++) {
+		gpointer *dst;
+
+		dst = va_arg (ap, gpointer *);
+		nm_assert (dst);
+
+		*dst = data[i];
+	}
+	va_end (ap);
+
+	g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), user_data);
+}
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index 84325bb7..4b081630 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -113,6 +113,9 @@ nm_ip_addr_set (int addr_family, gpointer dst, const NMIPAddr *src)
 #define NM_CMP_DIRECT_MEMCMP(a, b, size) \
     NM_CMP_RETURN (memcmp ((a), (b), (size)))
 
+#define NM_CMP_DIRECT_STRCMP0(a, b) \
+    NM_CMP_RETURN (g_strcmp0 ((a), (b)))
+
 #define NM_CMP_DIRECT_IN6ADDR(a, b) \
     G_STMT_START { \
         const struct in6_addr *const _a = (a); \
@@ -191,6 +194,18 @@ void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
 
 const char *nm_strquote (char *buf, gsize buf_len, const char *str);
 
+static inline gboolean
+nm_utils_is_separator (const char c)
+{
+	return NM_IN_SET (c, ' ', '\t');
+}
+
+/*****************************************************************************/
+
+const char *nm_utils_dbus_path_get_last_component (const char *dbus_path);
+
+int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b);
+
 /*****************************************************************************/
 
 const char **nm_utils_strsplit_set (const char *str, const char *delimiters);
@@ -440,6 +455,16 @@ nm_g_variant_unref_floating (GVariant *var)
 
 /*****************************************************************************/
 
+static inline int
+nm_utf8_collate0 (const char *a, const char *b)
+{
+	if (!a)
+		return !b ? 0 : -1;
+	if (!b)
+		return 1;
+	return g_utf8_collate (a, b);
+}
+
 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);
@@ -611,4 +636,16 @@ guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_
 
 /*****************************************************************************/
 
+gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args);
+
+#define nm_utils_user_data_pack(...) \
+	_nm_utils_user_data_pack(NM_NARG (__VA_ARGS__), (gconstpointer[]) { __VA_ARGS__ })
+
+void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...);
+
+#define nm_utils_user_data_unpack(user_data, ...) \
+	_nm_utils_user_data_unpack(user_data, NM_NARG (__VA_ARGS__), __VA_ARGS__)
+
+/*****************************************************************************/
+
 #endif /* __NM_SHARED_UTILS_H__ */
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index cc33a1ae..efbe6c9a 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -211,7 +211,6 @@ _nmtst_exit (void) \
 	nmtst_free (); \
 }
 
-
 static inline gboolean
 nmtst_initialized (void)
 {
@@ -276,7 +275,6 @@ BREAK_INNER_LOOPS:
 	return (char **) g_array_free (result, FALSE);
 }
 
-
 /* free instances allocated by nmtst (especially nmtst_init()) on shutdown
  * to release memory. After nmtst_free(), the test is uninitialized again. */
 static inline void
@@ -923,33 +921,26 @@ _nmtst_main_loop_run_timeout (gpointer user_data)
 {
 	GMainLoop **p_loop = user_data;
 
-	g_assert (p_loop);
-	g_assert (*p_loop);
-
-	g_main_loop_quit (*p_loop);
-	*p_loop = NULL;
-
+	g_assert (p_loop && *p_loop);
+	g_main_loop_quit (g_steal_pointer (p_loop));
 	return G_SOURCE_REMOVE;
 }
 
 static inline gboolean
 nmtst_main_loop_run (GMainLoop *loop, guint timeout_ms)
 {
-	GSource *source = NULL;
-	guint id = 0;
+	nm_auto_unref_gsource GSource *source = NULL;
 	GMainLoop *loopx = loop;
 
 	if (timeout_ms > 0) {
 		source = g_timeout_source_new (timeout_ms);
 		g_source_set_callback (source, _nmtst_main_loop_run_timeout, &loopx, NULL);
-		id = g_source_attach (source, g_main_loop_get_context (loop));
-		g_assert (id);
-		g_source_unref (source);
+		g_source_attach (source, g_main_loop_get_context (loop));
 	}
 
 	g_main_loop_run (loop);
 
-	if (source && loopx)
+	if (source)
 		g_source_destroy (source);
 
 	/* if the timeout was reached, return FALSE. */
@@ -1140,7 +1131,7 @@ _nmtst_assert_ip4_address (const char *file, int line, in_addr_t addr, const cha
 		char buf[100];
 
 		g_error ("%s:%d: Unexpected IPv4 address: expected %s, got %s",
-		         file, line, str_expected ? str_expected : "0.0.0.0",
+		         file, line, str_expected ?: "0.0.0.0",
 		         inet_ntop (AF_INET, &addr, buf, sizeof (buf)));
 	}
 }
@@ -1158,7 +1149,7 @@ _nmtst_assert_ip6_address (const char *file, int line, const struct in6_addr *ad
 		char buf[100];
 
 		g_error ("%s:%d: Unexpected IPv6 address: expected %s, got %s",
-		         file, line, str_expected ? str_expected : "::",
+		         file, line, str_expected ?: "::",
 		         inet_ntop (AF_INET6, addr, buf, sizeof (buf)));
 	}
 }
@@ -1831,7 +1822,6 @@ nmtst_assert_hwaddr_equals (gconstpointer hwaddr1, gssize hwaddr1_len, const cha
     nmtst_assert_hwaddr_equals (hwaddr1, hwaddr1_len, expected, __FILE__, __LINE__)
 #endif
 
-
 #if defined(__NM_SIMPLE_CONNECTION_H__) && defined(__NM_SETTING_CONNECTION_H__) && defined(__NM_KEYFILE_INTERNAL_H__)
 
 static inline NMConnection *
diff --git a/shared/nm-utils/siphash24.c b/shared/nm-utils/siphash24.c
deleted file mode 100644
index 8e59afb2..00000000
--- a/shared/nm-utils/siphash24.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
-   SipHash reference C implementation
-
-   Written in 2012 by
-   Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
-   Daniel J. Bernstein <djb@cr.yp.to>
-
-   To the extent possible under law, the author(s) have dedicated all copyright
-   and related and neighboring rights to this software to the public domain
-   worldwide. This software is distributed without any warranty.
-
-   You should have received a copy of the CC0 Public Domain Dedication along with
-   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
-
-   (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd)
-   (Refactored by Tom Gundersen to split up in several functions and follow systemd
-    coding style)
-*/
-
-#include "nm-default.h"
-
-#define assert(cond)   nm_assert (cond)
-#define _fallthrough_  _nm_fallthrough
-
-#include <stdio.h>
-
-#include "siphash24.h"
-#include "unaligned.h"
-
-static inline uint64_t rotate_left(uint64_t x, uint8_t b) {
-        assert(b < 64);
-
-        return (x << b) | (x >> (64 - b));
-}
-
-static inline void sipround(struct siphash *state) {
-        assert(state);
-
-        state->v0 += state->v1;
-        state->v1 = rotate_left(state->v1, 13);
-        state->v1 ^= state->v0;
-        state->v0 = rotate_left(state->v0, 32);
-        state->v2 += state->v3;
-        state->v3 = rotate_left(state->v3, 16);
-        state->v3 ^= state->v2;
-        state->v0 += state->v3;
-        state->v3 = rotate_left(state->v3, 21);
-        state->v3 ^= state->v0;
-        state->v2 += state->v1;
-        state->v1 = rotate_left(state->v1, 17);
-        state->v1 ^= state->v2;
-        state->v2 = rotate_left(state->v2, 32);
-}
-
-void siphash24_init(struct siphash *state, const uint8_t k[16]) {
-        uint64_t k0, k1;
-
-        assert(state);
-        assert(k);
-
-        k0 = unaligned_read_le64(k);
-        k1 = unaligned_read_le64(k + 8);
-
-        *state = (struct siphash) {
-                /* "somepseudorandomlygeneratedbytes" */
-                .v0 = 0x736f6d6570736575ULL ^ k0,
-                .v1 = 0x646f72616e646f6dULL ^ k1,
-                .v2 = 0x6c7967656e657261ULL ^ k0,
-                .v3 = 0x7465646279746573ULL ^ k1,
-                .padding = 0,
-                .inlen = 0,
-        };
-}
-
-void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
-
-        const uint8_t *in = _in;
-        const uint8_t *end = in + inlen;
-        size_t left = state->inlen & 7;
-        uint64_t m;
-
-        assert(in);
-        assert(state);
-
-        /* Update total length */
-        state->inlen += inlen;
-
-        /* If padding exists, fill it out */
-        if (left > 0) {
-                for ( ; in < end && left < 8; in ++, left ++)
-                        state->padding |= ((uint64_t) *in) << (left * 8);
-
-                if (in == end && left < 8)
-                        /* We did not have enough input to fill out the padding completely */
-                        return;
-
-#ifdef DEBUG
-                printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
-                printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
-                printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
-                printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
-                printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding);
-#endif
-
-                state->v3 ^= state->padding;
-                sipround(state);
-                sipround(state);
-                state->v0 ^= state->padding;
-
-                state->padding = 0;
-        }
-
-        end -= (state->inlen % sizeof(uint64_t));
-
-        for ( ; in < end; in += 8) {
-                m = unaligned_read_le64(in);
-#ifdef DEBUG
-                printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
-                printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
-                printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
-                printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
-                printf("(%3zu) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m);
-#endif
-                state->v3 ^= m;
-                sipround(state);
-                sipround(state);
-                state->v0 ^= m;
-        }
-
-        left = state->inlen & 7;
-        switch (left) {
-                case 7:
-                        state->padding |= ((uint64_t) in[6]) << 48;
-                        _fallthrough_;
-                case 6:
-                        state->padding |= ((uint64_t) in[5]) << 40;
-                        _fallthrough_;
-                case 5:
-                        state->padding |= ((uint64_t) in[4]) << 32;
-                        _fallthrough_;
-                case 4:
-                        state->padding |= ((uint64_t) in[3]) << 24;
-                        _fallthrough_;
-                case 3:
-                        state->padding |= ((uint64_t) in[2]) << 16;
-                        _fallthrough_;
-                case 2:
-                        state->padding |= ((uint64_t) in[1]) <<  8;
-                        _fallthrough_;
-                case 1:
-                        state->padding |= ((uint64_t) in[0]);
-                        _fallthrough_;
-                case 0:
-                        break;
-        }
-}
-
-uint64_t siphash24_finalize(struct siphash *state) {
-        uint64_t b;
-
-        assert(state);
-
-        b = state->padding | (((uint64_t) state->inlen) << 56);
-
-#ifdef DEBUG
-        printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
-        printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
-        printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
-        printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
-        printf("(%3zu) padding   %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding);
-#endif
-
-        state->v3 ^= b;
-        sipround(state);
-        sipround(state);
-        state->v0 ^= b;
-
-#ifdef DEBUG
-        printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
-        printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
-        printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
-        printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
-#endif
-        state->v2 ^= 0xff;
-
-        sipround(state);
-        sipround(state);
-        sipround(state);
-        sipround(state);
-
-        return state->v0 ^ state->v1 ^ state->v2  ^ state->v3;
-}
-
-uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]) {
-        struct siphash state;
-
-        assert(in);
-        assert(k);
-
-        siphash24_init(&state, k);
-        siphash24_compress(in, inlen, &state);
-
-        return siphash24_finalize(&state);
-}
diff --git a/shared/nm-utils/siphash24.h b/shared/nm-utils/siphash24.h
deleted file mode 100644
index 54e2420c..00000000
--- a/shared/nm-utils/siphash24.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include <inttypes.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-struct siphash {
-        uint64_t v0;
-        uint64_t v1;
-        uint64_t v2;
-        uint64_t v3;
-        uint64_t padding;
-        size_t inlen;
-};
-
-void siphash24_init(struct siphash *state, const uint8_t k[16]);
-void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
-#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
-
-uint64_t siphash24_finalize(struct siphash *state);
-
-uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]);
diff --git a/shared/nm-utils/unaligned.h b/shared/nm-utils/unaligned.h
index 73302b42..feddaa91 100644
--- a/shared/nm-utils/unaligned.h
+++ b/shared/nm-utils/unaligned.h
@@ -5,19 +5,6 @@
   This file is part of systemd.
 
   Copyright 2014 Tom Gundersen
-
-  systemd 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.1 of the License, or
-  (at your option) any later version.
-
-  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
 #include <endian.h>
diff --git a/shared/nm-version-macros.h b/shared/nm-version-macros.h
index 777bff80..2a37c26d 100644
--- a/shared/nm-version-macros.h
+++ b/shared/nm-version-macros.h
@@ -45,7 +45,7 @@
  * Evaluates to the micro version number of NetworkManager which this source
  * compiled against.
  */
-#define NM_MICRO_VERSION (3)
+#define NM_MICRO_VERSION (4)
 
 /**
  * NM_CHECK_VERSION: