about summary refs log tree commit diff
path: root/src/platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform')
-rw-r--r--src/platform/nm-fake-platform.c1
-rw-r--r--src/platform/nm-linux-platform.c918
-rw-r--r--src/platform/nm-netlink.c183
-rw-r--r--src/platform/nm-netlink.h243
-rw-r--r--src/platform/nm-platform-utils.c31
-rw-r--r--src/platform/nm-platform.c57
-rw-r--r--src/platform/nm-platform.h37
-rw-r--r--src/platform/nmp-netns.c239
-rw-r--r--src/platform/nmp-object.c19
-rw-r--r--src/platform/nmp-object.h18
-rw-r--r--src/platform/tests/meson.build20
-rw-r--r--src/platform/tests/test-common.c12
-rw-r--r--src/platform/tests/test-common.h1
-rw-r--r--src/platform/tests/test-link.c79
-rw-r--r--src/platform/tests/test-route.c2
-rw-r--r--src/platform/wifi/nm-wifi-utils-nl80211.c201
-rw-r--r--src/platform/wifi/nm-wifi-utils-wext.c38
-rw-r--r--src/platform/wifi/nm-wifi-utils.c1
-rw-r--r--src/platform/wpan/nm-wpan-utils.c19
19 files changed, 1307 insertions, 812 deletions
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index 814d9cea..30466159 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -22,7 +22,6 @@
 
 #include "nm-fake-platform.h"
 
-#include <errno.h>
 #include <unistd.h>
 #include <netinet/icmp6.h>
 #include <netinet/in.h>
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 85214ec8..2f5c75b0 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -21,31 +21,30 @@
 
 #include "nm-linux-platform.h"
 
-#include <poll.h>
+#include <arpa/inet.h>
+#include <dlfcn.h>
 #include <endian.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <fcntl.h>
-#include <dlfcn.h>
-#include <arpa/inet.h>
-#include <netinet/icmp6.h>
-#include <netinet/in.h>
+#include <libudev.h>
 #include <linux/ip.h>
 #include <linux/if_arp.h>
 #include <linux/if_link.h>
 #include <linux/if_tun.h>
 #include <linux/if_tunnel.h>
 #include <linux/ip6_tunnel.h>
-#include <libudev.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
 
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "nm-setting-vlan.h"
 
-#include "nm-utils/nm-errno.h"
 #include "nm-utils/nm-secret-utils.h"
+#include "nm-utils/nm-c-list.h"
 #include "nm-netlink.h"
 #include "nm-core-utils.h"
 #include "nmp-object.h"
@@ -322,13 +321,27 @@ typedef enum {
 } DelayedActionType;
 
 #define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \
-	for ((iflags) = (DelayedActionType) 0x1LL; (iflags) <= DELAYED_ACTION_TYPE_MAX; (iflags) <<= 1) \
-		if (NM_FLAGS_ANY (flags_all, iflags))
+	for ((iflags) = (DelayedActionType) 0x1LL; \
+	     ({ \
+	         gboolean _good = FALSE; \
+	         \
+	         nm_assert (nm_utils_is_power_of_two (iflags)); \
+	         \
+	         while ((iflags) <= DELAYED_ACTION_TYPE_MAX) { \
+	             if (NM_FLAGS_ANY ((flags_all), (iflags))) { \
+	                 _good = TRUE; \
+	                 break; \
+	             } \
+	             (iflags) <<= 1; \
+	         } \
+	         _good; \
+	     }); \
+	     (iflags) <<= 1)
 
 typedef enum {
 	/* Negative values are errors from kernel. Add dummy member to
 	 * make enum signed. */
-	_WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = -1,
+	_WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = G_MININT,
 
 	WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN = 0,
 	WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK,
@@ -376,8 +389,8 @@ typedef struct {
 
 	bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM];
 
-	bool sysctl_get_warned;
 	GHashTable *sysctl_get_prev_values;
+	CList sysctl_list;
 
 	NMUdevClient *udev_client;
 
@@ -461,7 +474,7 @@ G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM)
             _LOG_print (__level, __domain, __errsv, self, \
                         _NM_UTILS_MACRO_FIRST (__VA_ARGS__) ": %s (%d)" \
                         _NM_UTILS_MACRO_REST (__VA_ARGS__), \
-                        g_strerror (__errsv), __errsv); \
+                        nm_strerror_native (__errsv), __errsv); \
         } \
     } G_STMT_END
 
@@ -512,7 +525,7 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result,
 		if (seq_result < 0) {
 			nm_utils_strbuf_append (&buf, &buf_size, "failure %d (%s%s%s)",
 			                        -((int) seq_result),
-			                        g_strerror (-((int) seq_result)),
+			                        nm_strerror_native (-((int) seq_result)),
 			                        errmsg ? " - " : "",
 			                        errmsg ?: "");
 		}
@@ -577,7 +590,7 @@ _support_kernel_extended_ifa_flags_detect (struct nl_msg *msg)
 
 	/* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6,
 	 * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */
-	if (NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family, AF_INET, AF_INET6))
+	if (!NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family, AF_INET, AF_INET6))
 		return;
 
 	/* see if the nl_msg contains the IFA_FLAGS attribute. If it does,
@@ -1100,7 +1113,7 @@ _linktype_get_type (NMPlatform *platform,
  ******************************************************************/
 
 #define NLMSG_TAIL(nmsg) \
-    ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+    ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN ((nmsg)->nlmsg_len)))
 
 /* copied from iproute2's addattr_l(). */
 static gboolean
@@ -1154,31 +1167,29 @@ _parse_af_inet6 (NMPlatform *platform,
                  guint8 *out_addr_gen_mode_inv,
                  gboolean *out_addr_gen_mode_valid)
 {
-	static const struct nla_policy policy[IFLA_INET6_MAX+1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_INET6_FLAGS]              = { .type = NLA_U32 },
 		[IFLA_INET6_CACHEINFO]          = { .minlen = nm_offsetofend (struct ifla_cacheinfo, retrans_time) },
 		[IFLA_INET6_CONF]               = { .minlen = 4 },
 		[IFLA_INET6_STATS]              = { .minlen = 8 },
 		[IFLA_INET6_ICMP6STATS]         = { .minlen = 8 },
-		[IFLA_INET6_TOKEN]              = { .minlen = sizeof(struct in6_addr) },
+		[IFLA_INET6_TOKEN]              = { .minlen = sizeof (struct in6_addr) },
 		[IFLA_INET6_ADDR_GEN_MODE]      = { .type = NLA_U8 },
 	};
-	struct nlattr *tb[IFLA_INET6_MAX+1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	struct in6_addr i6_token;
 	gboolean token_valid = FALSE;
 	gboolean addr_gen_mode_valid = FALSE;
 	guint8 i6_addr_gen_mode_inv = 0;
 
-	err = nla_parse_nested (tb, IFLA_INET6_MAX, attr, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, attr, policy) < 0)
 		return FALSE;
 
-	if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4)
+	if (tb[IFLA_INET6_CONF] && nla_len (tb[IFLA_INET6_CONF]) % 4)
 		return FALSE;
-	if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8)
+	if (tb[IFLA_INET6_STATS] && nla_len (tb[IFLA_INET6_STATS]) % 8)
 		return FALSE;
-	if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8)
+	if (tb[IFLA_INET6_ICMP6STATS] && nla_len (tb[IFLA_INET6_ICMP6STATS]) % 8)
 		return FALSE;
 
 	if (_check_addr_or_return_val (tb, IFLA_INET6_TOKEN, sizeof (struct in6_addr), FALSE)) {
@@ -1218,7 +1229,7 @@ _parse_af_inet6 (NMPlatform *platform,
 static NMPObject *
 _parse_lnk_gre (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_GRE_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_GRE_LINK]     = { .type = NLA_U32 },
 		[IFLA_GRE_IFLAGS]   = { .type = NLA_U16 },
 		[IFLA_GRE_OFLAGS]   = { .type = NLA_U16 },
@@ -1230,13 +1241,13 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data)
 		[IFLA_GRE_TOS]      = { .type = NLA_U8 },
 		[IFLA_GRE_PMTUDISC] = { .type = NLA_U8 },
 	};
-	struct nlattr *tb[IFLA_GRE_MAX + 1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 	NMPlatformLnkGre *props;
 	gboolean is_tap;
 
-	if (!info_data || !kind)
+	if (   !info_data
+	    || !kind)
 		return NULL;
 
 	if (nm_streq (kind, "gretap"))
@@ -1246,8 +1257,7 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data)
 	else
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	obj = nmp_object_new (is_tap ? NMP_OBJECT_TYPE_LNK_GRETAP : NMP_OBJECT_TYPE_LNK_GRE, NULL);
@@ -1287,25 +1297,25 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data)
 static NMPObject *
 _parse_lnk_infiniband (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_IPOIB_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_IPOIB_PKEY]   = { .type = NLA_U16 },
 		[IFLA_IPOIB_MODE]   = { .type = NLA_U16 },
 		[IFLA_IPOIB_UMCAST] = { .type = NLA_U16 },
 	};
-	struct nlattr *tb[IFLA_IPOIB_MAX + 1];
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPlatformLnkInfiniband *info;
 	NMPObject *obj;
-	int err;
 	const char *mode;
 
-	if (!info_data || g_strcmp0 (kind, "ipoib"))
+	if (   !info_data
+	    || !nm_streq0 (kind, "ipoib"))
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_IPOIB_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
-	if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE])
+	if (   !tb[IFLA_IPOIB_PKEY]
+	    || !tb[IFLA_IPOIB_MODE])
 		return NULL;
 
 	switch (nla_get_u16 (tb[IFLA_IPOIB_MODE])) {
@@ -1333,29 +1343,26 @@ _parse_lnk_infiniband (const char *kind, struct nlattr *info_data)
 static NMPObject *
 _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_IPTUN_LINK]        = { .type = NLA_U32 },
-		[IFLA_IPTUN_LOCAL]       = { .type = NLA_UNSPEC,
-		                             .minlen = sizeof (struct in6_addr)},
-		[IFLA_IPTUN_REMOTE]      = { .type = NLA_UNSPEC,
-		                             .minlen = sizeof (struct in6_addr)},
+		[IFLA_IPTUN_LOCAL]       = { .minlen = sizeof (struct in6_addr)},
+		[IFLA_IPTUN_REMOTE]      = { .minlen = sizeof (struct in6_addr)},
 		[IFLA_IPTUN_TTL]         = { .type = NLA_U8 },
 		[IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 },
 		[IFLA_IPTUN_FLOWINFO]    = { .type = NLA_U32 },
 		[IFLA_IPTUN_PROTO]       = { .type = NLA_U8 },
 		[IFLA_IPTUN_FLAGS]       = { .type = NLA_U32 },
 	};
-	struct nlattr *tb[IFLA_IPTUN_MAX + 1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 	NMPlatformLnkIp6Tnl *props;
 	guint32 flowinfo;
 
-	if (!info_data || g_strcmp0 (kind, "ip6tnl"))
+	if (   !info_data
+	    || !nm_streq0 (kind, "ip6tnl"))
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_IPTUN_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_IP6TNL, NULL);
@@ -1364,9 +1371,9 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data)
 	if (tb[IFLA_IPTUN_LINK])
 		props->parent_ifindex = nla_get_u32 (tb[IFLA_IPTUN_LINK]);
 	if (tb[IFLA_IPTUN_LOCAL])
-		memcpy (&props->local, nla_data (tb[IFLA_IPTUN_LOCAL]), sizeof (props->local));
+		props->local = *nla_data_as (struct in6_addr, tb[IFLA_IPTUN_LOCAL]);
 	if (tb[IFLA_IPTUN_REMOTE])
-		memcpy (&props->remote, nla_data (tb[IFLA_IPTUN_REMOTE]), sizeof (props->remote));
+		props->remote = *nla_data_as (struct in6_addr, tb[IFLA_IPTUN_REMOTE]);
 	if (tb[IFLA_IPTUN_TTL])
 		props->ttl = nla_get_u8 (tb[IFLA_IPTUN_TTL]);
 	if (tb[IFLA_IPTUN_ENCAP_LIMIT])
@@ -1387,23 +1394,22 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data)
 static NMPObject *
 _parse_lnk_ip6gre (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_GRE_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_GRE_LINK]        = { .type = NLA_U32 },
 		[IFLA_GRE_IFLAGS]      = { .type = NLA_U16 },
 		[IFLA_GRE_OFLAGS]      = { .type = NLA_U16 },
 		[IFLA_GRE_IKEY]        = { .type = NLA_U32 },
 		[IFLA_GRE_OKEY]        = { .type = NLA_U32 },
 		[IFLA_GRE_LOCAL]       = { .type = NLA_UNSPEC,
-		                             .minlen = sizeof (struct in6_addr)},
+		                           .minlen = sizeof (struct in6_addr)},
 		[IFLA_GRE_REMOTE]      = { .type = NLA_UNSPEC,
-		                             .minlen = sizeof (struct in6_addr)},
+		                           .minlen = sizeof (struct in6_addr)},
 		[IFLA_GRE_TTL]         = { .type = NLA_U8 },
 		[IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 },
 		[IFLA_GRE_FLOWINFO]    = { .type = NLA_U32 },
 		[IFLA_GRE_FLAGS]       = { .type = NLA_U32 },
 	};
-	struct nlattr *tb[IFLA_GRE_MAX + 1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 	NMPlatformLnkIp6Tnl *props;
 	guint32 flowinfo;
@@ -1419,8 +1425,7 @@ _parse_lnk_ip6gre (const char *kind, struct nlattr *info_data)
 	else
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	obj = nmp_object_new (is_tap ? NMP_OBJECT_TYPE_LNK_IP6GRETAP : NMP_OBJECT_TYPE_LNK_IP6GRE, NULL);
@@ -1439,9 +1444,9 @@ _parse_lnk_ip6gre (const char *kind, struct nlattr *info_data)
 	if (tb[IFLA_GRE_OKEY])
 		props->output_key = ntohl (nla_get_u32 (tb[IFLA_GRE_OKEY]));
 	if (tb[IFLA_GRE_LOCAL])
-		memcpy (&props->local, nla_data (tb[IFLA_GRE_LOCAL]), sizeof (props->local));
+		props->local = *nla_data_as (struct in6_addr, tb[IFLA_GRE_LOCAL]);
 	if (tb[IFLA_GRE_REMOTE])
-		memcpy (&props->remote, nla_data (tb[IFLA_GRE_REMOTE]), sizeof (props->remote));
+		props->remote = *nla_data_as (struct in6_addr, tb[IFLA_GRE_REMOTE]);
 	if (tb[IFLA_GRE_TTL])
 		props->ttl = nla_get_u8 (tb[IFLA_GRE_TTL]);
 	if (tb[IFLA_GRE_ENCAP_LIMIT])
@@ -1462,7 +1467,7 @@ _parse_lnk_ip6gre (const char *kind, struct nlattr *info_data)
 static NMPObject *
 _parse_lnk_ipip (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_IPTUN_LINK]     = { .type = NLA_U32 },
 		[IFLA_IPTUN_LOCAL]    = { .type = NLA_U32 },
 		[IFLA_IPTUN_REMOTE]   = { .type = NLA_U32 },
@@ -1470,16 +1475,15 @@ _parse_lnk_ipip (const char *kind, struct nlattr *info_data)
 		[IFLA_IPTUN_TOS]      = { .type = NLA_U8 },
 		[IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 },
 	};
-	struct nlattr *tb[IFLA_IPTUN_MAX + 1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 	NMPlatformLnkIpIp *props;
 
-	if (!info_data || g_strcmp0 (kind, "ipip"))
+	if (   !info_data
+	    || !nm_streq0 (kind, "ipip"))
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_IPTUN_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_IPIP, NULL);
@@ -1500,28 +1504,27 @@ _parse_lnk_ipip (const char *kind, struct nlattr *info_data)
 static NMPObject *
 _parse_lnk_macvlan (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_MACVLAN_MODE]  = { .type = NLA_U32 },
 		[IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 },
 	};
 	NMPlatformLnkMacvlan *props;
-	struct nlattr *tb[IFLA_MACVLAN_MAX + 1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 	gboolean tap;
 
-	if (!info_data)
+	if (   !info_data
+	    || !kind)
 		return NULL;
 
-	if (!g_strcmp0 (kind, "macvlan"))
+	if (nm_streq (kind, "macvlan"))
 		tap = FALSE;
-	else if (!g_strcmp0 (kind, "macvtap"))
+	else if (nm_streq (kind, "macvtap"))
 		tap = TRUE;
 	else
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_MACVLAN_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	if (!tb[IFLA_MACVLAN_MODE])
@@ -1543,7 +1546,7 @@ _parse_lnk_macvlan (const char *kind, struct nlattr *info_data)
 static NMPObject *
 _parse_lnk_macsec (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[__IFLA_MACSEC_MAX] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_MACSEC_SCI]            = { .type = NLA_U64 },
 		[IFLA_MACSEC_ICV_LEN]        = { .type = NLA_U8 },
 		[IFLA_MACSEC_CIPHER_SUITE]   = { .type = NLA_U64 },
@@ -1557,33 +1560,32 @@ _parse_lnk_macsec (const char *kind, struct nlattr *info_data)
 		[IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 },
 		[IFLA_MACSEC_VALIDATION]     = { .type = NLA_U8 },
 	};
-	struct nlattr *tb[__IFLA_MACSEC_MAX];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 	NMPlatformLnkMacsec *props;
 
-	if (!info_data || !nm_streq0 (kind, "macsec"))
+	if (   !info_data
+	    || !nm_streq0 (kind, "macsec"))
 		return NULL;
 
-	err = nla_parse_nested (tb, __IFLA_MACSEC_MAX - 1, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_MACSEC, NULL);
 	props = &obj->lnk_macsec;
 
-	props->sci = tb[IFLA_MACSEC_SCI] ? be64toh (nla_get_u64 (tb[IFLA_MACSEC_SCI])) : 0;
-	props->icv_length = tb[IFLA_MACSEC_ICV_LEN] ? nla_get_u8 (tb[IFLA_MACSEC_ICV_LEN]) : 0;
-	props->cipher_suite = tb [IFLA_MACSEC_CIPHER_SUITE] ? nla_get_u64 (tb[IFLA_MACSEC_CIPHER_SUITE]) : 0;
-	props->window = tb [IFLA_MACSEC_WINDOW] ? nla_get_u32 (tb[IFLA_MACSEC_WINDOW]) : 0;
-	props->encoding_sa = tb[IFLA_MACSEC_ENCODING_SA] ? !!nla_get_u8 (tb[IFLA_MACSEC_ENCODING_SA]) : 0;
-	props->encrypt = tb[IFLA_MACSEC_ENCRYPT] ? !!nla_get_u8 (tb[IFLA_MACSEC_ENCRYPT]) : 0;
-	props->protect = tb[IFLA_MACSEC_PROTECT] ? !!nla_get_u8 (tb[IFLA_MACSEC_PROTECT]) : 0;
-	props->include_sci = tb[IFLA_MACSEC_INC_SCI] ? !!nla_get_u8 (tb[IFLA_MACSEC_INC_SCI]) : 0;
-	props->es = tb[IFLA_MACSEC_ES] ? !!nla_get_u8 (tb[IFLA_MACSEC_ES]) : 0;
-	props->scb = tb[IFLA_MACSEC_SCB] ? !!nla_get_u8 (tb[IFLA_MACSEC_SCB]) : 0;
-	props->replay_protect = tb[IFLA_MACSEC_REPLAY_PROTECT] ? !!nla_get_u8 (tb[IFLA_MACSEC_REPLAY_PROTECT]) : 0;
-	props->validation = tb[IFLA_MACSEC_VALIDATION] ? nla_get_u8 (tb[IFLA_MACSEC_VALIDATION]) : 0;
+	if (tb[IFLA_MACSEC_SCI])            { props->sci            =   nla_get_be64 (tb[IFLA_MACSEC_SCI]);            }
+	if (tb[IFLA_MACSEC_ICV_LEN])        { props->icv_length     =   nla_get_u8   (tb[IFLA_MACSEC_ICV_LEN]);        }
+	if (tb[IFLA_MACSEC_CIPHER_SUITE])   { props->cipher_suite   =   nla_get_u64  (tb[IFLA_MACSEC_CIPHER_SUITE]);   }
+	if (tb[IFLA_MACSEC_WINDOW])         { props->window         =   nla_get_u32  (tb[IFLA_MACSEC_WINDOW]);         }
+	if (tb[IFLA_MACSEC_ENCODING_SA])    { props->encoding_sa    = !!nla_get_u8   (tb[IFLA_MACSEC_ENCODING_SA]);    }
+	if (tb[IFLA_MACSEC_ENCRYPT])        { props->encrypt        = !!nla_get_u8   (tb[IFLA_MACSEC_ENCRYPT]);        }
+	if (tb[IFLA_MACSEC_PROTECT])        { props->protect        = !!nla_get_u8   (tb[IFLA_MACSEC_PROTECT]);        }
+	if (tb[IFLA_MACSEC_INC_SCI])        { props->include_sci    = !!nla_get_u8   (tb[IFLA_MACSEC_INC_SCI]);        }
+	if (tb[IFLA_MACSEC_ES])             { props->es             = !!nla_get_u8   (tb[IFLA_MACSEC_ES]);             }
+	if (tb[IFLA_MACSEC_SCB])            { props->scb            = !!nla_get_u8   (tb[IFLA_MACSEC_SCB]);            }
+	if (tb[IFLA_MACSEC_REPLAY_PROTECT]) { props->replay_protect = !!nla_get_u8   (tb[IFLA_MACSEC_REPLAY_PROTECT]); }
+	if (tb[IFLA_MACSEC_VALIDATION])     { props->validation     =   nla_get_u8   (tb[IFLA_MACSEC_VALIDATION]);     }
 
 	return obj;
 }
@@ -1593,7 +1595,7 @@ _parse_lnk_macsec (const char *kind, struct nlattr *info_data)
 static NMPObject *
 _parse_lnk_sit (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_IPTUN_LINK]     = { .type = NLA_U32 },
 		[IFLA_IPTUN_LOCAL]    = { .type = NLA_U32 },
 		[IFLA_IPTUN_REMOTE]   = { .type = NLA_U32 },
@@ -1603,16 +1605,15 @@ _parse_lnk_sit (const char *kind, struct nlattr *info_data)
 		[IFLA_IPTUN_FLAGS]    = { .type = NLA_U16 },
 		[IFLA_IPTUN_PROTO]    = { .type = NLA_U8 },
 	};
-	struct nlattr *tb[IFLA_IPTUN_MAX + 1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 	NMPlatformLnkSit *props;
 
-	if (!info_data || g_strcmp0 (kind, "sit"))
+	if (   !info_data
+	    || !nm_streq0 (kind, "sit"))
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_IPTUN_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_SIT, NULL);
@@ -1635,7 +1636,7 @@ _parse_lnk_sit (const char *kind, struct nlattr *info_data)
 static NMPObject *
 _parse_lnk_tun (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_TUN_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_TUN_OWNER]               = { .type = NLA_U32 },
 		[IFLA_TUN_GROUP]               = { .type = NLA_U32 },
 		[IFLA_TUN_TYPE]                = { .type = NLA_U8 },
@@ -1646,22 +1647,19 @@ _parse_lnk_tun (const char *kind, struct nlattr *info_data)
 		[IFLA_TUN_NUM_QUEUES]          = { .type = NLA_U32 },
 		[IFLA_TUN_NUM_DISABLED_QUEUES] = { .type = NLA_U32 },
 	};
-	struct nlattr *tb[IFLA_TUN_MAX + 1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 	NMPlatformLnkTun *props;
 
-	if (!info_data || !nm_streq0 (kind, "tun"))
+	if (   !info_data
+	    || !nm_streq0 (kind, "tun"))
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_TUN_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
-	if (!tb[IFLA_TUN_TYPE]) {
-		/* we require at least a type. */
+	if (!tb[IFLA_TUN_TYPE])
 		return NULL;
-	}
 
 	obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_TUN, NULL);
 	props = &obj->lnk_tun;
@@ -1708,7 +1706,7 @@ _vlan_qos_mapping_from_nla (struct nlattr *nlattr,
 
 	array = g_ptr_array_new ();
 	nla_for_each_nested (nla, nlattr, remaining) {
-		if (nla_len (nla) < sizeof(NMVlanQosMapping))
+		if (nla_len (nla) < sizeof (NMVlanQosMapping))
 			return FALSE;
 		g_ptr_array_add (array, nla_data (nla));
 	}
@@ -1749,22 +1747,22 @@ _vlan_qos_mapping_from_nla (struct nlattr *nlattr,
 static NMPObject *
 _parse_lnk_vlan (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_VLAN_MAX+1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_VLAN_ID]          = { .type = NLA_U16 },
 		[IFLA_VLAN_FLAGS]       = { .minlen = nm_offsetofend (struct ifla_vlan_flags, flags) },
 		[IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
 		[IFLA_VLAN_EGRESS_QOS]  = { .type = NLA_NESTED },
 		[IFLA_VLAN_PROTOCOL]    = { .type = NLA_U16 },
 	};
-	struct nlattr *tb[IFLA_VLAN_MAX+1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	nm_auto_nmpobj NMPObject *obj = NULL;
 	NMPObject *obj_result;
 
-	if (!info_data || g_strcmp0 (kind, "vlan"))
+	if (   !info_data
+	    || !nm_streq0 (kind, "vlan"))
 		return NULL;
 
-	if ((err = nla_parse_nested (tb, IFLA_VLAN_MAX, info_data, policy)) < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	if (!tb[IFLA_VLAN_ID])
@@ -1776,7 +1774,7 @@ _parse_lnk_vlan (const char *kind, struct nlattr *info_data)
 	if (tb[IFLA_VLAN_FLAGS]) {
 		struct ifla_vlan_flags flags;
 
-		nla_memcpy (&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags));
+		nla_memcpy (&flags, tb[IFLA_VLAN_FLAGS], sizeof (flags));
 
 		obj->lnk_vlan.flags = flags.flags;
 	}
@@ -1834,7 +1832,7 @@ struct nm_ifla_vxlan_port_range {
 static NMPObject *
 _parse_lnk_vxlan (const char *kind, struct nlattr *info_data)
 {
-	static const struct nla_policy policy[IFLA_VXLAN_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_VXLAN_ID]         = { .type = NLA_U32 },
 		[IFLA_VXLAN_GROUP]      = { .type = NLA_U32 },
 		[IFLA_VXLAN_GROUP6]     = { .type = NLA_UNSPEC,
@@ -1857,16 +1855,14 @@ _parse_lnk_vxlan (const char *kind, struct nlattr *info_data)
 		[IFLA_VXLAN_PORT]       = { .type = NLA_U16 },
 	};
 	NMPlatformLnkVxlan *props;
-	struct nlattr *tb[IFLA_VXLAN_MAX + 1];
-	struct nm_ifla_vxlan_port_range *range;
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	NMPObject *obj;
 
-	if (!info_data || g_strcmp0 (kind, "vxlan"))
+	if (   !info_data
+	    || !nm_streq0 (kind, "vxlan"))
 		return NULL;
 
-	err = nla_parse_nested (tb, IFLA_VXLAN_MAX, info_data, policy);
-	if (err < 0)
+	if (nla_parse_nested_arr (tb, info_data, policy) < 0)
 		return NULL;
 
 	obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL);
@@ -1881,10 +1877,10 @@ _parse_lnk_vxlan (const char *kind, struct nlattr *info_data)
 		props->group = nla_get_u32 (tb[IFLA_VXLAN_GROUP]);
 	if (tb[IFLA_VXLAN_LOCAL])
 		props->local = nla_get_u32 (tb[IFLA_VXLAN_LOCAL]);
-	if (tb[IFLA_VXLAN_GROUP6])
-		memcpy (&props->group6, nla_data (tb[IFLA_VXLAN_GROUP6]), sizeof (props->group6));
 	if (tb[IFLA_VXLAN_LOCAL6])
-		memcpy (&props->local6, nla_data (tb[IFLA_VXLAN_LOCAL6]), sizeof (props->local6));
+		props->local6 = *nla_data_as (struct in6_addr, tb[IFLA_VXLAN_LOCAL6]);
+	if (tb[IFLA_VXLAN_GROUP6])
+		props->group6 = *nla_data_as (struct in6_addr, tb[IFLA_VXLAN_GROUP6]);
 
 	if (tb[IFLA_VXLAN_AGEING])
 		props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]);
@@ -1899,7 +1895,9 @@ _parse_lnk_vxlan (const char *kind, struct nlattr *info_data)
 		props->dst_port = ntohs (nla_get_u16 (tb[IFLA_VXLAN_PORT]));
 
 	if (tb[IFLA_VXLAN_PORT_RANGE]) {
-		range = nla_data (tb[IFLA_VXLAN_PORT_RANGE]);
+		struct nm_ifla_vxlan_port_range *range;
+
+		range = nla_data_as (struct nm_ifla_vxlan_port_range, tb[IFLA_VXLAN_PORT_RANGE]);
 		props->src_port_min = ntohs (range->low);
 		props->src_port_max = ntohs (range->high);
 	}
@@ -1924,16 +1922,16 @@ static gboolean
 _wireguard_update_from_allowed_ips_nla (NMPWireGuardAllowedIP *allowed_ip,
                                         struct nlattr *nlattr)
 {
-	static const struct nla_policy policy[WGALLOWEDIP_A_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[WGALLOWEDIP_A_FAMILY]    = { .type = NLA_U16 },
 		[WGALLOWEDIP_A_IPADDR]    = { .minlen = sizeof (struct in_addr) },
 		[WGALLOWEDIP_A_CIDR_MASK] = { .type = NLA_U8 },
 	};
-	struct nlattr *tb[WGALLOWEDIP_A_MAX + 1];
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	int family;
 	int addr_len;
 
-	if (nla_parse_nested (tb, WGALLOWEDIP_A_MAX, nlattr, policy) < 0)
+	if (nla_parse_nested_arr (tb, nlattr, policy) < 0)
 		return FALSE;
 
 	if (!tb[WGALLOWEDIP_A_FAMILY])
@@ -1973,7 +1971,7 @@ _wireguard_update_from_peers_nla (CList *peers,
                                   GArray **p_allowed_ips,
                                   struct nlattr *peer_attr)
 {
-	static const struct nla_policy policy[WGPEER_A_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[WGPEER_A_PUBLIC_KEY]                    = { .minlen = NMP_WIREGUARD_PUBLIC_KEY_LEN },
 		[WGPEER_A_PRESHARED_KEY]                 = { },
 		[WGPEER_A_FLAGS]                         = { .type = NLA_U32 },
@@ -1984,10 +1982,10 @@ _wireguard_update_from_peers_nla (CList *peers,
 		[WGPEER_A_TX_BYTES]                      = { .type = NLA_U64 },
 		[WGPEER_A_ALLOWEDIPS]                    = { .type = NLA_NESTED },
 	};
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	WireGuardPeerConstruct *peer_c;
-	struct nlattr *tb[WGPEER_A_MAX + 1];
 
-	if (nla_parse_nested (tb, WGPEER_A_MAX, peer_attr, policy) < 0)
+	if (nla_parse_nested_arr (tb, peer_attr, policy) < 0)
 		return FALSE;
 
 	if (!tb[WGPEER_A_PUBLIC_KEY])
@@ -2017,12 +2015,14 @@ _wireguard_update_from_peers_nla (CList *peers,
 
 		nm_sock_addr_union_cpy_untrusted (&peer_c->data.endpoint,
 		                                  tb[WGPEER_A_ENDPOINT] ? nla_data (tb[WGPEER_A_ENDPOINT]) : NULL,
-		                                  tb[WGPEER_A_ENDPOINT] ? nla_len (tb[WGPEER_A_ENDPOINT])  : 0);
+		                                  tb[WGPEER_A_ENDPOINT] ? nla_len  (tb[WGPEER_A_ENDPOINT]) : 0);
 
 		if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL])
-			peer_c->data.persistent_keepalive_interval = nla_get_u64 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]);
-		if (tb[WGPEER_A_LAST_HANDSHAKE_TIME])
-			nla_memcpy (&peer_c->data.last_handshake_time, tb[WGPEER_A_LAST_HANDSHAKE_TIME], sizeof (peer_c->data.last_handshake_time));
+			peer_c->data.persistent_keepalive_interval = nla_get_u16 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]);
+		if (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) {
+			if (nla_len (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) >= sizeof (peer_c->data.last_handshake_time))
+				nla_memcpy (&peer_c->data.last_handshake_time, tb[WGPEER_A_LAST_HANDSHAKE_TIME], sizeof (peer_c->data.last_handshake_time));
+		}
 		if (tb[WGPEER_A_RX_BYTES])
 			peer_c->data.rx_bytes = nla_get_u64 (tb[WGPEER_A_RX_BYTES]);
 		if (tb[WGPEER_A_TX_BYTES])
@@ -2070,7 +2070,7 @@ typedef struct {
 static int
 _wireguard_get_device_cb (struct nl_msg *msg, void *arg)
 {
-	static const struct nla_policy policy[WGDEVICE_A_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[WGDEVICE_A_IFINDEX]     = { .type = NLA_U32 },
 		[WGDEVICE_A_IFNAME]      = { .type = NLA_NUL_STRING, .maxlen = IFNAMSIZ },
 		[WGDEVICE_A_PRIVATE_KEY] = { },
@@ -2080,12 +2080,10 @@ _wireguard_get_device_cb (struct nl_msg *msg, void *arg)
 		[WGDEVICE_A_FWMARK]      = { .type = NLA_U32 },
 		[WGDEVICE_A_PEERS]       = { .type = NLA_NESTED },
 	};
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	WireGuardParseData *parse_data = arg;
-	struct nlattr *tb[WGDEVICE_A_MAX + 1];
-	int nlerr;
 
-	nlerr = genlmsg_parse (nlmsg_hdr (msg), 0, tb, WGDEVICE_A_MAX, policy);
-	if (nlerr < 0)
+	if (genlmsg_parse_arr (nlmsg_hdr (msg), 0, tb, policy) < 0)
 		return NL_SKIP;
 
 	if (tb[WGDEVICE_A_IFINDEX]) {
@@ -2164,7 +2162,7 @@ _wireguard_read_info (NMPlatform *platform /* used only as logging context */,
 	nm_assert (wireguard_family_id >= 0);
 	nm_assert (ifindex > 0);
 
-	_LOGT ("wireguard: fetching infomation for ifindex %d (genl-id %d)...", ifindex, wireguard_family_id);
+	_LOGT ("wireguard: fetching information for ifindex %d (genl-id %d)...", ifindex, wireguard_family_id);
 
 	msg = nlmsg_alloc ();
 
@@ -2373,8 +2371,9 @@ _wireguard_create_change_nlmsgs (NMPlatform *platform,
                                  int wireguard_family_id,
                                  const NMPlatformLnkWireGuard *lnk_wireguard,
                                  const NMPWireGuardPeer *peers,
+                                 const NMPlatformWireGuardChangePeerFlags *peer_flags,
                                  guint peers_len,
-                                 gboolean replace_peers,
+                                 NMPlatformWireGuardChangeFlags change_flags,
                                  GPtrArray **out_msgs)
 {
 	gs_unref_ptrarray GPtrArray *msgs = NULL;
@@ -2386,6 +2385,7 @@ _wireguard_create_change_nlmsgs (NMPlatform *platform,
 	struct nlattr *nest_curr_peer;
 	struct nlattr *nest_allowed_ips;
 	struct nlattr *nest_curr_allowed_ip;
+	NMPlatformWireGuardChangePeerFlags p_flags = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT;
 
 #define _nla_nest_end(msg, nest_start) \
 	G_STMT_START { \
@@ -2420,12 +2420,19 @@ again:
 	NLA_PUT_U32 (msg, WGDEVICE_A_IFINDEX, (guint32) ifindex);
 
 	if (idx_peer_curr == IDX_NIL) {
-		NLA_PUT (msg, WGDEVICE_A_PRIVATE_KEY, sizeof (lnk_wireguard->private_key), lnk_wireguard->private_key);
-		NLA_PUT_U16 (msg, WGDEVICE_A_LISTEN_PORT, lnk_wireguard->listen_port);
-		NLA_PUT_U32 (msg, WGDEVICE_A_FWMARK, lnk_wireguard->fwmark);
+		guint32 flags;
+
+		if (NM_FLAGS_HAS (change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY))
+			NLA_PUT (msg, WGDEVICE_A_PRIVATE_KEY, sizeof (lnk_wireguard->private_key), lnk_wireguard->private_key);
+		if (NM_FLAGS_HAS (change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT))
+			NLA_PUT_U16 (msg, WGDEVICE_A_LISTEN_PORT, lnk_wireguard->listen_port);
+		if (NM_FLAGS_HAS (change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK))
+			NLA_PUT_U32 (msg, WGDEVICE_A_FWMARK, lnk_wireguard->fwmark);
 
-		NLA_PUT_U32 (msg, WGDEVICE_A_FLAGS,
-		             replace_peers ? WGDEVICE_F_REPLACE_PEERS : ((guint32) 0u));
+		flags = 0;
+		if (NM_FLAGS_HAS (change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS))
+			flags |= WGDEVICE_F_REPLACE_PEERS;
+		NLA_PUT_U32 (msg, WGDEVICE_A_FLAGS, flags);
 	}
 
 	if (peers_len == 0)
@@ -2444,6 +2451,21 @@ again:
 	for (; idx_peer_curr < peers_len; idx_peer_curr++) {
 		const NMPWireGuardPeer *p = &peers[idx_peer_curr];
 
+		if (peer_flags) {
+			p_flags = peer_flags[idx_peer_curr];
+			if (!NM_FLAGS_ANY (p_flags,   NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME
+			                            | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY
+			                            | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL
+			                            | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT
+			                            | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS
+			                            | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS)) {
+				/* no flags set. We take that as indication to skip configuring the peer
+				 * entirely. */
+				nm_assert (p_flags == NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE);
+				continue;
+			}
+		}
+
 		nest_curr_peer = nla_nest_start (msg, 0);
 		if (!nest_curr_peer)
 			goto toobig_peers;
@@ -2451,63 +2473,77 @@ again:
 		if (nla_put (msg, WGPEER_A_PUBLIC_KEY, NMP_WIREGUARD_PUBLIC_KEY_LEN, p->public_key) < 0)
 			goto toobig_peers;
 
-		if (idx_allowed_ips_curr == IDX_NIL) {
-
-			if (nla_put (msg, WGPEER_A_PRESHARED_KEY, sizeof (p->preshared_key), p->preshared_key) < 0)
-				goto toobig_peers;
-
-			if (nla_put_uint16 (msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, p->persistent_keepalive_interval) < 0)
+		if (NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME)) {
+			/* all other p_flags are silently ignored. */
+			if (nla_put_uint32 (msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME) < 0)
 				goto toobig_peers;
+		} else {
 
-			if (nla_put_uint32 (msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS) < 0)
-				goto toobig_peers;
+			if (idx_allowed_ips_curr == IDX_NIL) {
+				if (   NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY)
+				    && nla_put (msg, WGPEER_A_PRESHARED_KEY, sizeof (p->preshared_key), p->preshared_key) < 0)
+					goto toobig_peers;
 
-			if (NM_IN_SET (p->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
-				if (nla_put (msg,
-				             WGPEER_A_ENDPOINT,
-				               p->endpoint.sa.sa_family == AF_INET
-				             ? sizeof (p->endpoint.in)
-				             : sizeof (p->endpoint.in6),
-				             &p->endpoint) < 0)
+				if (   NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL)
+				    && nla_put_uint16 (msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, p->persistent_keepalive_interval) < 0)
 					goto toobig_peers;
-			} else
-				nm_assert (p->endpoint.sa.sa_family == AF_UNSPEC);
-		}
 
-		if (p->allowed_ips_len > 0) {
-			if (idx_allowed_ips_curr == IDX_NIL)
-				idx_allowed_ips_curr = 0;
+				if (   NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS)
+				    && nla_put_uint32 (msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS) < 0)
+					goto toobig_peers;
 
-			nest_allowed_ips = nla_nest_start (msg, WGPEER_A_ALLOWEDIPS);
-			if (!nest_allowed_ips)
-				goto toobig_allowedips;
+				if (NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT)) {
+					if (NM_IN_SET (p->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
+						if (nla_put (msg,
+						             WGPEER_A_ENDPOINT,
+						               p->endpoint.sa.sa_family == AF_INET
+						             ? sizeof (p->endpoint.in)
+						             : sizeof (p->endpoint.in6),
+						             &p->endpoint) < 0)
+							goto toobig_peers;
+					} else {
+						/* I think there is no way to clear an endpoint, though there shold be. */
+						nm_assert (p->endpoint.sa.sa_family == AF_UNSPEC);
+					}
+				}
+			}
 
-			for (; idx_allowed_ips_curr < p->allowed_ips_len; idx_allowed_ips_curr++) {
-				const NMPWireGuardAllowedIP *aip = &p->allowed_ips[idx_allowed_ips_curr];
+			if (   NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS)
+			    && p->allowed_ips_len > 0) {
+				if (idx_allowed_ips_curr == IDX_NIL)
+					idx_allowed_ips_curr = 0;
 
-				nest_curr_allowed_ip = nla_nest_start (msg, 0);
-				if (!nest_curr_allowed_ip)
+				nest_allowed_ips = nla_nest_start (msg, WGPEER_A_ALLOWEDIPS);
+				if (!nest_allowed_ips)
 					goto toobig_allowedips;
 
-				g_return_val_if_fail (NM_IN_SET (aip->family, AF_INET, AF_INET6), -NME_BUG);
+				for (; idx_allowed_ips_curr < p->allowed_ips_len; idx_allowed_ips_curr++) {
+					const NMPWireGuardAllowedIP *aip = &p->allowed_ips[idx_allowed_ips_curr];
 
-				if (nla_put_uint16 (msg, WGALLOWEDIP_A_FAMILY, aip->family) < 0)
-					goto toobig_allowedips;
-				if (nla_put (msg,
-				             WGALLOWEDIP_A_IPADDR,
-				             nm_utils_addr_family_to_size (aip->family),
-				             &aip->addr) < 0)
-					goto toobig_allowedips;
-				if (nla_put_uint8 (msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0)
-					goto toobig_allowedips;
+					nest_curr_allowed_ip = nla_nest_start (msg, 0);
+					if (!nest_curr_allowed_ip)
+						goto toobig_allowedips;
 
-				_nla_nest_end (msg, nest_curr_allowed_ip);
-				nest_curr_allowed_ip = NULL;
-			}
-			idx_allowed_ips_curr = IDX_NIL;
+					g_return_val_if_fail (NM_IN_SET (aip->family, AF_INET, AF_INET6), -NME_BUG);
 
-			_nla_nest_end (msg, nest_allowed_ips);
-			nest_allowed_ips = NULL;
+					if (nla_put_uint16 (msg, WGALLOWEDIP_A_FAMILY, aip->family) < 0)
+						goto toobig_allowedips;
+					if (nla_put (msg,
+					             WGALLOWEDIP_A_IPADDR,
+					             nm_utils_addr_family_to_size (aip->family),
+					             &aip->addr) < 0)
+						goto toobig_allowedips;
+					if (nla_put_uint8 (msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0)
+						goto toobig_allowedips;
+
+					_nla_nest_end (msg, nest_curr_allowed_ip);
+					nest_curr_allowed_ip = NULL;
+				}
+				idx_allowed_ips_curr = IDX_NIL;
+
+				_nla_nest_end (msg, nest_allowed_ips);
+				nest_allowed_ips = NULL;
+			}
 		}
 
 		_nla_nest_end (msg, nest_curr_peer);
@@ -2555,8 +2591,9 @@ link_wireguard_change (NMPlatform *platform,
                        int ifindex,
                        const NMPlatformLnkWireGuard *lnk_wireguard,
                        const NMPWireGuardPeer *peers,
+                       const NMPlatformWireGuardChangePeerFlags *peer_flags,
                        guint peers_len,
-                       gboolean replace_peers)
+                       NMPlatformWireGuardChangeFlags change_flags)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	gs_unref_ptrarray GPtrArray *msgs = NULL;
@@ -2573,8 +2610,9 @@ link_wireguard_change (NMPlatform *platform,
 	                                     wireguard_family_id,
 	                                     lnk_wireguard,
 	                                     peers,
+	                                     peer_flags,
 	                                     peers_len,
-	                                     replace_peers,
+	                                     change_flags,
 	                                     &msgs);
 	if (r < 0) {
 		_LOGW ("wireguard: set-device, cannot construct netlink message: %s", nm_strerror (r));
@@ -2610,7 +2648,7 @@ link_wireguard_change (NMPlatform *platform,
 static NMPObject *
 _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only)
 {
-	static const struct nla_policy policy[IFLA_MAX+1] = {
+	static const struct nla_policy policy[] = {
 		[IFLA_IFNAME]           = { .type = NLA_STRING,
 		                            .maxlen = IFNAMSIZ },
 		[IFLA_MTU]              = { .type = NLA_U32 },
@@ -2637,18 +2675,12 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 		[IFLA_PHYS_PORT_ID]     = { .type = NLA_UNSPEC },
 		[IFLA_NET_NS_PID]       = { .type = NLA_U32 },
 		[IFLA_NET_NS_FD]        = { .type = NLA_U32 },
-	};
-	static const struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = {
-		[IFLA_INFO_KIND]        = { .type = NLA_STRING },
-		[IFLA_INFO_DATA]        = { .type = NLA_NESTED },
-		[IFLA_INFO_XSTATS]      = { .type = NLA_NESTED },
+		[IFLA_LINK_NETNSID]     = { },
 	};
 	const struct ifinfomsg *ifi;
-	struct nlattr *tb[IFLA_MAX+1];
-	struct nlattr *li[IFLA_INFO_MAX+1];
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	struct nlattr *nl_info_data = NULL;
 	const char *nl_info_kind = NULL;
-	int err;
 	nm_auto_nmpobj NMPObject *obj = NULL;
 	gboolean completed_from_cache_val = FALSE;
 	gboolean *completed_from_cache = cache ? &completed_from_cache_val : NULL;
@@ -2662,6 +2694,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 
 	if (!nlmsg_valid_hdr (nlh, sizeof (*ifi)))
 		return NULL;
+
 	ifi = nlmsg_data (nlh);
 
 	if (ifi->ifi_family != AF_UNSPEC)
@@ -2674,13 +2707,12 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 	if (id_only)
 		return g_steal_pointer (&obj);
 
-	err = nlmsg_parse (nlh, sizeof (*ifi), tb, IFLA_MAX, policy);
-	if (err < 0)
+	if (nlmsg_parse_arr (nlh, sizeof (*ifi), tb, policy) < 0)
 		return NULL;
 
 	if (!tb[IFLA_IFNAME])
 		return NULL;
-	nla_strlcpy(obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ);
+	nla_strlcpy (obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ);
 	if (!obj->link.name[0])
 		return NULL;
 
@@ -2702,8 +2734,14 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 	obj->link.mtu = nla_get_u32 (tb[IFLA_MTU]);
 
 	if (tb[IFLA_LINKINFO]) {
-		err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], policy_link_info);
-		if (err < 0)
+		static const struct nla_policy policy_link_info[] = {
+			[IFLA_INFO_KIND]        = { .type = NLA_STRING },
+			[IFLA_INFO_DATA]        = { .type = NLA_NESTED },
+			[IFLA_INFO_XSTATS]      = { .type = NLA_NESTED },
+		};
+		struct nlattr *li[G_N_ELEMENTS (policy_link_info)];
+
+		if (nla_parse_nested_arr (li, tb[IFLA_LINKINFO], policy_link_info) < 0)
 			return NULL;
 
 		if (li[IFLA_INFO_KIND])
@@ -2713,18 +2751,12 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 	}
 
 	if (tb[IFLA_STATS64]) {
-		/* tb[IFLA_STATS64] is only guaranteed to be 32bit-aligned,
-		 * so in general we can't access the rtnl_link_stats64 struct
-		 * members directly on 64bit architectures. */
-		char *stats = nla_data (tb[IFLA_STATS64]);
+		const char *stats = nla_data (tb[IFLA_STATS64]);
 
-#define READ_STAT64(member) \
-	unaligned_read_ne64 (stats + offsetof (struct rtnl_link_stats64, member))
-
-		obj->link.rx_packets = READ_STAT64 (rx_packets);
-		obj->link.rx_bytes   = READ_STAT64 (rx_bytes);
-		obj->link.tx_packets = READ_STAT64 (tx_packets);
-		obj->link.tx_bytes   = READ_STAT64 (tx_bytes);
+		obj->link.rx_packets = unaligned_read_ne64 (&stats[G_STRUCT_OFFSET (struct rtnl_link_stats64, rx_packets)]);
+		obj->link.rx_bytes   = unaligned_read_ne64 (&stats[G_STRUCT_OFFSET (struct rtnl_link_stats64, rx_bytes)]);
+		obj->link.tx_packets = unaligned_read_ne64 (&stats[G_STRUCT_OFFSET (struct rtnl_link_stats64, tx_packets)]);
+		obj->link.tx_bytes   = unaligned_read_ne64 (&stats[G_STRUCT_OFFSET (struct rtnl_link_stats64, tx_bytes)]);
 	}
 
 	obj->link.n_ifi_flags = ifi->ifi_flags;
@@ -2952,14 +2984,14 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 static NMPObject *
 _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
 {
-	static const struct nla_policy policy[IFA_MAX+1] = {
+	static const struct nla_policy policy[] = {
 		[IFA_LABEL]     = { .type = NLA_STRING,
-		                     .maxlen = IFNAMSIZ },
+		                    .maxlen = IFNAMSIZ },
 		[IFA_CACHEINFO] = { .minlen = nm_offsetofend (struct ifa_cacheinfo, tstamp) },
+		[IFA_FLAGS]     = { },
 	};
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	const struct ifaddrmsg *ifa;
-	struct nlattr *tb[IFA_MAX+1];
-	int err;
 	gboolean is_v4;
 	nm_auto_nmpobj NMPObject *obj = NULL;
 	int addr_len;
@@ -2967,14 +2999,15 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
 
 	if (!nlmsg_valid_hdr (nlh, sizeof (*ifa)))
 		return NULL;
-	ifa = nlmsg_data(nlh);
+
+	ifa = nlmsg_data (nlh);
 
 	if (!NM_IN_SET (ifa->ifa_family, AF_INET, AF_INET6))
 		return NULL;
+
 	is_v4 = ifa->ifa_family == AF_INET;
 
-	err = nlmsg_parse (nlh, sizeof(*ifa), tb, IFA_MAX, policy);
-	if (err < 0)
+	if (nlmsg_parse_arr (nlh, sizeof (*ifa), tb, policy) < 0)
 		return NULL;
 
 	addr_len = is_v4
@@ -3043,8 +3076,9 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
 	timestamp = 0;
 	/* IPv6 only */
 	if (tb[IFA_CACHEINFO]) {
-		const struct ifa_cacheinfo *ca = nla_data(tb[IFA_CACHEINFO]);
+		const struct ifa_cacheinfo *ca;
 
+		ca = nla_data_as (struct ifa_cacheinfo, tb[IFA_CACHEINFO]);
 		lifetime = ca->ifa_valid;
 		preferred = ca->ifa_prefered;
 		timestamp = ca->tstamp;
@@ -3063,7 +3097,7 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
 static NMPObject *
 _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 {
-	static const struct nla_policy policy[RTA_MAX+1] = {
+	static const struct nla_policy policy[] = {
 		[RTA_TABLE]     = { .type = NLA_U32 },
 		[RTA_IIF]       = { .type = NLA_U32 },
 		[RTA_OIF]       = { .type = NLA_U32 },
@@ -3075,8 +3109,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 		[RTA_MULTIPATH] = { .type = NLA_NESTED },
 	};
 	const struct rtmsg *rtm;
-	struct nlattr *tb[RTA_MAX + 1];
-	int err;
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
 	gboolean is_v4;
 	nm_auto_nmpobj NMPObject *obj = NULL;
 	int addr_len;
@@ -3084,13 +3117,21 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 		gboolean is_present;
 		int ifindex;
 		NMIPAddr gateway;
-	} nh;
+	} nh = {
+		.is_present = FALSE,
+	};
 	guint32 mss;
-	guint32 window = 0, cwnd = 0, initcwnd = 0, initrwnd = 0, mtu = 0, lock = 0;
+	guint32 window = 0;
+	guint32 cwnd = 0;
+	guint32 initcwnd = 0;
+	guint32 initrwnd = 0;
+	guint32 mtu = 0;
+	guint32 lock = 0;
 
 	if (!nlmsg_valid_hdr (nlh, sizeof (*rtm)))
 		return NULL;
-	rtm = nlmsg_data(nlh);
+
+	rtm = nlmsg_data (nlh);
 
 	/*****************************************************************
 	 * only handle ~normal~ routes.
@@ -3102,8 +3143,10 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 	if (rtm->rtm_type != RTN_UNICAST)
 		return NULL;
 
-	err = nlmsg_parse (nlh, sizeof (struct rtmsg), tb, RTA_MAX, policy);
-	if (err < 0)
+	if (nlmsg_parse_arr (nlh,
+	                     sizeof (struct rtmsg),
+	                     tb,
+	                     policy) < 0)
 		return NULL;
 
 	/*****************************************************************/
@@ -3120,39 +3163,49 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 	 * parse nexthops. Only handle routes with one nh.
 	 *****************************************************************/
 
-	memset (&nh, 0, sizeof (nh));
-
 	if (tb[RTA_MULTIPATH]) {
-		struct rtnexthop *rtnh = nla_data (tb[RTA_MULTIPATH]);
-		size_t tlen = nla_len(tb[RTA_MULTIPATH]);
+		size_t tlen = nla_len (tb[RTA_MULTIPATH]);
+		struct rtnexthop *rtnh;
 
-		while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) {
+		if (tlen < sizeof (*rtnh))
+			goto rta_multipath_done;
+
+		rtnh = nla_data_as (struct rtnexthop, tb[RTA_MULTIPATH]);
+
+		if (tlen < rtnh->rtnh_len)
+			goto rta_multipath_done;
+
+		while (TRUE) {
 
 			if (nh.is_present) {
 				/* we don't support multipath routes. */
 				return NULL;
 			}
-			nh.is_present = TRUE;
 
+			nh.is_present = TRUE;
 			nh.ifindex = rtnh->rtnh_ifindex;
 
-			if (rtnh->rtnh_len > sizeof(*rtnh)) {
-				struct nlattr *ntb[RTA_MAX + 1];
+			if (rtnh->rtnh_len > sizeof (*rtnh)) {
+				struct nlattr *ntb[G_N_ELEMENTS (policy)];
 
-				err = nla_parse (ntb, RTA_MAX, (struct nlattr *)
-				                 RTNH_DATA(rtnh),
-				                 rtnh->rtnh_len - sizeof (*rtnh),
-				                 policy);
-				if (err < 0)
+				if (nla_parse_arr (ntb,
+				                   (struct nlattr *) RTNH_DATA (rtnh),
+				                   rtnh->rtnh_len - sizeof (*rtnh),
+				                   policy) < 0)
 					return NULL;
 
 				if (_check_addr_or_return_null (ntb, RTA_GATEWAY, addr_len))
 					memcpy (&nh.gateway, nla_data (ntb[RTA_GATEWAY]), addr_len);
 			}
 
-			tlen -= RTNH_ALIGN(rtnh->rtnh_len);
-			rtnh = RTNH_NEXT(rtnh);
+			if (tlen < RTNH_ALIGN (rtnh->rtnh_len) + sizeof (*rtnh))
+				goto rta_multipath_done;
+
+			tlen -= RTNH_ALIGN (rtnh->rtnh_len);
+			rtnh = RTNH_NEXT (rtnh);
 		}
+rta_multipath_done:
+		;
 	}
 
 	if (   tb[RTA_OIF]
@@ -3186,8 +3239,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 
 	mss = 0;
 	if (tb[RTA_METRICS]) {
-		struct nlattr *mtb[RTAX_MAX + 1];
-		static const struct nla_policy rtax_policy[RTAX_MAX + 1] = {
+		static const struct nla_policy rtax_policy[] = {
 			[RTAX_LOCK]        = { .type = NLA_U32 },
 			[RTAX_ADVMSS]      = { .type = NLA_U32 },
 			[RTAX_WINDOW]      = { .type = NLA_U32 },
@@ -3196,9 +3248,9 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 			[RTAX_INITRWND]    = { .type = NLA_U32 },
 			[RTAX_MTU]         = { .type = NLA_U32 },
 		};
+		struct nlattr *mtb[G_N_ELEMENTS (rtax_policy)];
 
-		err = nla_parse_nested (mtb, RTAX_MAX, tb[RTA_METRICS], rtax_policy);
-		if (err < 0)
+		if (nla_parse_nested_arr (mtb, tb[RTA_METRICS], rtax_policy) < 0)
 			return NULL;
 
 		if (mtb[RTAX_LOCK])
@@ -3233,7 +3285,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 	obj->ip_route.plen = rtm->rtm_dst_len;
 
 	if (tb[RTA_PRIORITY])
-		obj->ip_route.metric = nla_get_u32(tb[RTA_PRIORITY]);
+		obj->ip_route.metric = nla_get_u32 (tb[RTA_PRIORITY]);
 
 	if (is_v4)
 		obj->ip4_route.gateway = nh.gateway.addr4;
@@ -3290,25 +3342,24 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 static NMPObject *
 _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only)
 {
-	NMPObject *obj = NULL;
-	const struct tcmsg *tcm;
-	struct nlattr *tb[TCA_MAX + 1];
-	int err;
-	static const struct nla_policy policy[TCA_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[TCA_KIND] = { .type = NLA_STRING },
 	};
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
+	const struct tcmsg *tcm;
+	NMPObject *obj;
 
-	if (!nlmsg_valid_hdr (nlh, sizeof (*tcm)))
-		return NULL;
-	tcm = nlmsg_data (nlh);
-
-	err = nlmsg_parse (nlh, sizeof (*tcm), tb, TCA_MAX, policy);
-	if (err < 0)
+	if (nlmsg_parse_arr (nlh,
+	                     sizeof (*tcm),
+	                     tb,
+	                     policy) < 0)
 		return NULL;
 
 	if (!tb[TCA_KIND])
 		return NULL;
 
+	tcm = nlmsg_data (nlh);
+
 	obj = nmp_object_new (NMP_OBJECT_TYPE_QDISC, NULL);
 
 	obj->qdisc.kind = g_intern_string (nla_get_string (tb[TCA_KIND]));
@@ -3324,25 +3375,21 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only)
 static NMPObject *
 _new_from_nl_tfilter (struct nlmsghdr *nlh, gboolean id_only)
 {
-	NMPObject *obj = NULL;
-	const struct tcmsg *tcm;
-	struct nlattr *tb[TCA_MAX + 1];
-	int err;
-	static const struct nla_policy policy[TCA_MAX + 1] = {
+	static const struct nla_policy policy[] = {
 		[TCA_KIND] = { .type = NLA_STRING },
 	};
+	struct nlattr *tb[G_N_ELEMENTS (policy)];
+	NMPObject *obj = NULL;
+	const struct tcmsg *tcm;
 
-	if (!nlmsg_valid_hdr (nlh, sizeof (*tcm)))
-		return NULL;
-	tcm = nlmsg_data (nlh);
-
-	err = nlmsg_parse (nlh, sizeof (*tcm), tb, TCA_MAX, policy);
-	if (err < 0)
+	if (nlmsg_parse_arr (nlh, sizeof (*tcm), tb, policy) < 0)
 		return NULL;
 
 	if (!tb[TCA_KIND])
 		return NULL;
 
+	tcm = nlmsg_data (nlh);
+
 	obj = nmp_object_new (NMP_OBJECT_TYPE_TFILTER, NULL);
 
 	obj->tfilter.kind = g_intern_string (nla_get_string (tb[TCA_KIND]));
@@ -3464,14 +3511,14 @@ _nl_msg_new_link_set_linkinfo (struct nl_msg *msg,
 	NLA_PUT_STRING (msg, IFLA_INFO_KIND, kind);
 
 	if (veth_peer) {
-		struct ifinfomsg ifi = { };
+		const struct ifinfomsg ifi = { };
 		struct nlattr *data, *info_peer;
 
 		if (!(data = nla_nest_start (msg, IFLA_INFO_DATA)))
 			goto nla_put_failure;
 		if (!(info_peer = nla_nest_start (msg, 1 /*VETH_INFO_PEER*/)))
 			goto nla_put_failure;
-		if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0)
+		if (nlmsg_append_struct (msg, &ifi) < 0)
 			goto nla_put_failure;
 		NLA_PUT_STRING (msg, IFLA_IFNAME, veth_peer);
 		nla_nest_end (msg, info_peer);
@@ -3575,7 +3622,7 @@ _nl_msg_new_link_set_linkinfo_vlan (struct nl_msg *msg,
 		for (i = 0; i < egress_qos_len; i++) {
 			if (VLAN_XGRESS_PRIO_VALID (egress_qos[i].to)) {
 				if (!qos) {
-					if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
+					if (!(qos = nla_nest_start (msg, IFLA_VLAN_EGRESS_QOS)))
 						goto nla_put_failure;
 				}
 				NLA_PUT (msg, i, sizeof (egress_qos[i]), &egress_qos[i]);
@@ -3583,7 +3630,7 @@ _nl_msg_new_link_set_linkinfo_vlan (struct nl_msg *msg,
 		}
 
 		if (qos)
-			nla_nest_end(msg, qos);
+			nla_nest_end (msg, qos);
 	}
 
 	nla_nest_end (msg, data);
@@ -3602,8 +3649,8 @@ _nl_msg_new_link (int nlmsg_type,
                   unsigned flags_mask,
                   unsigned flags_set)
 {
-	struct nl_msg *msg;
-	struct ifinfomsg ifi = {
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	const struct ifinfomsg ifi = {
 		.ifi_change = flags_mask,
 		.ifi_flags = flags_set,
 		.ifi_index = ifindex,
@@ -3613,15 +3660,15 @@ _nl_msg_new_link (int nlmsg_type,
 
 	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
 
-	if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0)
+	if (nlmsg_append_struct (msg, &ifi) < 0)
 		goto nla_put_failure;
 
 	if (ifname)
 		NLA_PUT_STRING (msg, IFLA_IFNAME, ifname);
 
-	return msg;
+	return g_steal_pointer (&msg);
+
 nla_put_failure:
-	nlmsg_free (msg);
 	g_return_val_if_reached (NULL);
 }
 
@@ -3640,7 +3687,7 @@ _nl_msg_new_address (int nlmsg_type,
                      guint32 preferred,
                      const char *label)
 {
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	struct ifaddrmsg am = {
 		.ifa_family = family,
 		.ifa_index = ifindex,
@@ -3667,7 +3714,7 @@ _nl_msg_new_address (int nlmsg_type,
 
 	addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr);
 
-	if (nlmsg_append (msg, &am, sizeof (am), NLMSG_ALIGNTO) < 0)
+	if (nlmsg_append_struct (msg, &am) < 0)
 		goto nla_put_failure;
 
 	if (address)
@@ -3698,7 +3745,7 @@ _nl_msg_new_address (int nlmsg_type,
 			.ifa_prefered = preferred,
 		};
 
-		NLA_PUT (msg, IFA_CACHEINFO, sizeof(ca), &ca);
+		NLA_PUT (msg, IFA_CACHEINFO, sizeof (ca), &ca);
 	}
 
 	if (flags & ~((guint32) 0xFF)) {
@@ -3711,10 +3758,9 @@ _nl_msg_new_address (int nlmsg_type,
 		NLA_PUT_U32 (msg, IFA_FLAGS, flags);
 	}
 
-	return msg;
+	return g_steal_pointer (&msg);
 
 nla_put_failure:
-	nlmsg_free (msg);
 	g_return_val_if_reached (NULL);
 }
 
@@ -3734,12 +3780,12 @@ _nl_msg_new_route (int nlmsg_type,
                    guint16 nlmsgflags,
                    const NMPObject *obj)
 {
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj);
 	gboolean is_v4 = klass->addr_family == AF_INET;
 	const guint32 lock = ip_route_get_lock_flag (NMP_OBJECT_CAST_IP_ROUTE (obj));
 	const guint32 table = nm_platform_route_table_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced, TRUE);
-	struct rtmsg rtmsg = {
+	const struct rtmsg rtmsg = {
 		.rtm_family = klass->addr_family,
 		.rtm_tos = is_v4
 		           ? obj->ip4_route.tos
@@ -3766,7 +3812,7 @@ _nl_msg_new_route (int nlmsg_type,
 
 	msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags);
 
-	if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0)
+	if (nlmsg_append_struct (msg, &rtmsg) < 0)
 		goto nla_put_failure;
 
 	addr_len = is_v4
@@ -3824,7 +3870,7 @@ _nl_msg_new_route (int nlmsg_type,
 		if (lock)
 			NLA_PUT_U32 (msg, RTAX_LOCK, lock);
 
-		nla_nest_end(msg, metrics);
+		nla_nest_end (msg, metrics);
 	}
 
 	/* We currently don't have need for multi-hop routes... */
@@ -3840,10 +3886,9 @@ _nl_msg_new_route (int nlmsg_type,
 	    && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM)
 		NLA_PUT_U8 (msg, RTA_PREF, obj->ip6_route.rt_pref);
 
-	return msg;
+	return g_steal_pointer (&msg);
 
 nla_put_failure:
-	nlmsg_free (msg);
 	g_return_val_if_reached (NULL);
 }
 
@@ -3852,8 +3897,8 @@ _nl_msg_new_qdisc (int nlmsg_type,
                    int nlmsg_flags,
                    const NMPlatformQdisc *qdisc)
 {
-	struct nl_msg *msg;
-	struct tcmsg tcm = {
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	const struct tcmsg tcm = {
 		.tcm_family = qdisc->addr_family,
 		.tcm_ifindex = qdisc->ifindex,
 		.tcm_handle = qdisc->handle,
@@ -3863,14 +3908,14 @@ _nl_msg_new_qdisc (int nlmsg_type,
 
 	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
 
-	if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0)
+	if (nlmsg_append_struct (msg, &tcm) < 0)
 		goto nla_put_failure;
 
 	NLA_PUT_STRING (msg, TCA_KIND, qdisc->kind);
 
-	return msg;
+	return g_steal_pointer (&msg);
+
 nla_put_failure:
-	nlmsg_free (msg);
 	g_return_val_if_reached (NULL);
 }
 
@@ -3924,10 +3969,10 @@ _nl_msg_new_tfilter (int nlmsg_type,
                      int nlmsg_flags,
                      const NMPlatformTfilter *tfilter)
 {
-	struct nl_msg *msg;
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
 	struct nlattr *tc_options;
 	struct nlattr *act_tab;
-	struct tcmsg tcm = {
+	const struct tcmsg tcm = {
 		.tcm_family = tfilter->addr_family,
 		.tcm_ifindex = tfilter->ifindex,
 		.tcm_handle = tfilter->handle,
@@ -3937,7 +3982,7 @@ _nl_msg_new_tfilter (int nlmsg_type,
 
 	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
 
-	if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0)
+	if (nlmsg_append_struct (msg, &tcm) < 0)
 		goto nla_put_failure;
 
 	NLA_PUT_STRING (msg, TCA_KIND, tfilter->kind);
@@ -3955,9 +4000,9 @@ _nl_msg_new_tfilter (int nlmsg_type,
 
 	nla_nest_end (msg, act_tab);
 
-	return msg;
+	return g_steal_pointer (&msg);
+
 nla_put_failure:
-	nlmsg_free (msg);
 	g_return_val_if_reached (NULL);
 }
 
@@ -4052,10 +4097,10 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat
 			errsv = errno;
 			if (errsv == ENOENT) {
 				_LOGD ("sysctl: failed to open '%s': (%d) %s",
-				       pathid, errsv, strerror (errsv));
+				       pathid, errsv, nm_strerror_native (errsv));
 			} else {
 				_LOGE ("sysctl: failed to open '%s': (%d) %s",
-				       pathid, errsv, strerror (errsv));
+				       pathid, errsv, nm_strerror_native (errsv));
 			}
 			errno = errsv;
 			return FALSE;
@@ -4066,10 +4111,10 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat
 			errsv = errno;
 			if (errsv == ENOENT) {
 				_LOGD ("sysctl: failed to openat '%s': (%d) %s",
-				       pathid, errsv, strerror (errsv));
+				       pathid, errsv, nm_strerror_native (errsv));
 			} else {
 				_LOGE ("sysctl: failed to openat '%s': (%d) %s",
-				       pathid, errsv, strerror (errsv));
+				       pathid, errsv, nm_strerror_native (errsv));
 			}
 			errno = errsv;
 			return FALSE;
@@ -4119,7 +4164,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat
 		}
 
 		_NMLOG (level, "sysctl: failed to set '%s' to '%s': (%d) %s",
-		        path, value, errsv, strerror (errsv));
+		        path, value, errsv, nm_strerror_native (errsv));
 	} else if (nwrote < len - 1) {
 		_LOGE ("sysctl: failed to set '%s' to '%s' after three attempts",
 		       path, value);
@@ -4146,8 +4191,8 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat
 
 static GSList *sysctl_clear_cache_list;
 
-static void
-_nm_logging_clear_platform_logging_cache_impl (void)
+void
+_nm_logging_clear_platform_logging_cache (void)
 {
 	while (sysctl_clear_cache_list) {
 		NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (sysctl_clear_cache_list->data);
@@ -4156,42 +4201,71 @@ _nm_logging_clear_platform_logging_cache_impl (void)
 
 		g_hash_table_destroy (priv->sysctl_get_prev_values);
 		priv->sysctl_get_prev_values = NULL;
-		priv->sysctl_get_warned = FALSE;
 	}
 }
 
+typedef struct {
+	const char *path;
+	CList lst;
+	char *value;
+	char path_data[];
+} SysctlCacheEntry;
+
+static void
+sysctl_cache_entry_free (SysctlCacheEntry *entry)
+{
+	c_list_unlink_stale (&entry->lst);
+	g_free (entry->value);
+	g_free (entry);
+}
+
 static void
 _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char *contents)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
-	const char *prev_value = NULL;
+	SysctlCacheEntry *entry = NULL;
 
 	if (!priv->sysctl_get_prev_values) {
-		_nm_logging_clear_platform_logging_cache = _nm_logging_clear_platform_logging_cache_impl;
 		sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform);
-		priv->sysctl_get_prev_values = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+		c_list_init (&priv->sysctl_list);
+		priv->sysctl_get_prev_values = g_hash_table_new_full (nm_pstr_hash,
+		                                                      nm_pstr_equal,
+		                                                      (GDestroyNotify) sysctl_cache_entry_free,
+		                                                      NULL);
 	} else
-		prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, pathid);
+		entry = g_hash_table_lookup (priv->sysctl_get_prev_values, &pathid);
 
-	if (prev_value) {
-		if (strcmp (prev_value, contents) != 0) {
+	if (entry) {
+		if (!nm_streq (entry->value, contents)) {
 			gs_free char *contents_escaped = g_strescape (contents, NULL);
-			gs_free char *prev_value_escaped = g_strescape (prev_value, NULL);
+			gs_free char *prev_value_escaped = g_strescape (entry->value, NULL);
 
 			_LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", pathid, contents_escaped, prev_value_escaped);
-			g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (pathid), g_strdup (contents));
+			g_free (entry->value);
+			entry->value = g_strdup (contents);
 		}
+		nm_c_list_move_front (&priv->sysctl_list, &entry->lst);
 	} else {
 		gs_free char *contents_escaped = g_strescape (contents, NULL);
+		SysctlCacheEntry *old;
+		size_t len;
+
+		len = strlen (pathid);
+		entry = g_malloc (sizeof (SysctlCacheEntry) + len + 1);
+		entry->value = g_strdup (contents);
+		entry->path = entry->path_data;
+		memcpy (entry->path_data, pathid, len + 1);
+
+		/* Remove oldest entry when the cache becomes too big */
+		if (g_hash_table_size (priv->sysctl_get_prev_values) > 1000) {
+			old = c_list_last_entry (&priv->sysctl_list, SysctlCacheEntry, lst);
+			g_hash_table_remove (priv->sysctl_get_prev_values, old);
+		}
 
 		_LOGD ("sysctl: reading '%s': '%s'", pathid, contents_escaped);
-		g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (pathid), g_strdup (contents));
 
-		if (   !priv->sysctl_get_warned
-		    && g_hash_table_size (priv->sysctl_get_prev_values) > 50000) {
-			_LOGW ("sysctl: the internal cache for debug-logging of sysctl values grew pretty large. You can clear it by disabling debug-logging: `nmcli general logging level KEEP domains PLATFORM:INFO`.");
-			priv->sysctl_get_warned = TRUE;
-		}
+		g_hash_table_add (priv->sysctl_get_prev_values, entry);
+		c_list_link_front (&priv->sysctl_list, &entry->lst);
 	}
 }
 
@@ -4575,9 +4649,8 @@ delayed_action_handle_one (NMPlatform *platform)
 		priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL;
 
 		if (_LOGt_ENABLED ()) {
-			FOR_EACH_DELAYED_ACTION (iflags, flags) {
+			FOR_EACH_DELAYED_ACTION (iflags, flags)
 				_LOGt_delayed_action (iflags, NULL, "handle");
-			}
 		}
 
 		delayed_action_handle_REFRESH_ALL (platform, flags);
@@ -4661,9 +4734,8 @@ delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gp
 	priv->delayed_action.flags |= action_type;
 
 	if (_LOGt_ENABLED ()) {
-		FOR_EACH_DELAYED_ACTION (iflags, action_type) {
+		FOR_EACH_DELAYED_ACTION (iflags, action_type)
 			_LOGt_delayed_action (iflags, user_data, "schedule");
-		}
 	}
 }
 
@@ -4993,7 +5065,7 @@ _nl_send_nlmsghdr (NMPlatform *platform,
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	guint32 seq;
-	int nle;
+	int errsv;
 
 	nm_assert (nlhdr);
 
@@ -5010,7 +5082,7 @@ _nl_send_nlmsghdr (NMPlatform *platform,
 		};
 		struct msghdr msg = {
 			.msg_name = &nladdr,
-			.msg_namelen = sizeof(nladdr),
+			.msg_namelen = sizeof (nladdr),
 			.msg_iov = &iov,
 			.msg_iovlen = 1,
 		};
@@ -5022,13 +5094,13 @@ _nl_send_nlmsghdr (NMPlatform *platform,
 
 		try_count = 0;
 again:
-		nle = sendmsg (nl_socket_get_fd (priv->nlh), &msg, 0);
-		if (nle < 0) {
-			nle = errno;
-			if (nle == EINTR && try_count++ < 100)
+		errsv = sendmsg (nl_socket_get_fd (priv->nlh), &msg, 0);
+		if (errsv < 0) {
+			errsv = errno;
+			if (errsv == EINTR && try_count++ < 100)
 				goto again;
-			_LOGD ("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", g_strerror (nle), nle);
-			return -nle;
+			_LOGD ("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", nm_strerror_native (errsv), errsv);
+			return -nm_errno_from_native (errsv);
 		}
 	}
 
@@ -5125,6 +5197,59 @@ do_request_link (NMPlatform *platform, int ifindex, const char *name)
 	delayed_action_handle_all (platform, FALSE);
 }
 
+static struct nl_msg *
+_nl_msg_new_dump (NMPObjectType obj_type,
+                  int preferred_addr_family)
+{
+	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+	const NMPClass *klass;
+
+	klass = nmp_class_from_type (obj_type);
+
+	nm_assert (klass);
+	nm_assert (klass->rtm_gettype > 0);
+
+	nlmsg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP);
+
+	if (klass->addr_family != AF_UNSPEC) {
+		/* if the class specifies a particular address family, then it is preferred. */
+		nm_assert (NM_IN_SET (preferred_addr_family, AF_UNSPEC, klass->addr_family));
+		preferred_addr_family = klass->addr_family;
+	}
+
+	switch (klass->obj_type) {
+	case NMP_OBJECT_TYPE_QDISC:
+	case NMP_OBJECT_TYPE_TFILTER:
+		{
+			const struct tcmsg tcmsg = {
+				.tcm_family = preferred_addr_family,
+			};
+
+			if (nlmsg_append_struct (nlmsg, &tcmsg) < 0)
+				g_return_val_if_reached (NULL);
+		}
+		break;
+	case NMP_OBJECT_TYPE_LINK:
+	case NMP_OBJECT_TYPE_IP4_ADDRESS:
+	case NMP_OBJECT_TYPE_IP6_ADDRESS:
+	case NMP_OBJECT_TYPE_IP4_ROUTE:
+	case NMP_OBJECT_TYPE_IP6_ROUTE:
+		{
+			const struct rtgenmsg gmsg = {
+				.rtgen_family = preferred_addr_family,
+			};
+
+			if (nlmsg_append_struct (nlmsg, &gmsg) < 0)
+				g_return_val_if_reached (NULL);
+		}
+		break;
+	default:
+		g_return_val_if_reached (NULL);
+	}
+
+	return g_steal_pointer (&nlmsg);
+}
+
 static void
 do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type)
 {
@@ -5135,16 +5260,18 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
 	action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL;
 
 	FOR_EACH_DELAYED_ACTION (iflags, action_type) {
+		NMPLookup lookup;
+
 		priv->pruning[delayed_action_refresh_all_to_idx (iflags)] = TRUE;
+		nmp_lookup_init_obj_type (&lookup,
+		                          delayed_action_refresh_to_object_type (iflags));
 		nmp_cache_dirty_set_all (nm_platform_get_cache (platform),
-		                         delayed_action_refresh_to_object_type (iflags));
+		                         &lookup);
 	}
 
 	FOR_EACH_DELAYED_ACTION (iflags, action_type) {
 		NMPObjectType obj_type = delayed_action_refresh_to_object_type (iflags);
-		const NMPClass *klass = nmp_class_from_type (obj_type);
 		nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-		int nle;
 		int *out_refresh_all_in_progress;
 
 		out_refresh_all_in_progress = &priv->delayed_action.refresh_all_in_progress[delayed_action_refresh_all_to_idx (iflags)];
@@ -5162,31 +5289,23 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
 
 		event_handler_read_netlink (platform, FALSE);
 
-		/* reimplement
-		 *   nl_rtgen_request (sk, klass->rtm_gettype, klass->addr_family, NLM_F_DUMP);
-		 * because we need the sequence number.
-		 */
-		nlmsg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP);
+		nlmsg = _nl_msg_new_dump (obj_type, AF_UNSPEC);
+		if (!nlmsg)
+			goto next_after_fail;
 
-		if (   klass->obj_type == NMP_OBJECT_TYPE_QDISC
-		    || klass->obj_type == NMP_OBJECT_TYPE_TFILTER) {
-			struct tcmsg tcmsg = {
-				.tcm_family = AF_UNSPEC,
-			};
-			nle = nlmsg_append (nlmsg, &tcmsg, sizeof (tcmsg), NLMSG_ALIGNTO);
-		} else {
-			struct rtgenmsg gmsg = {
-				.rtgen_family = klass->addr_family,
-			};
-			nle = nlmsg_append (nlmsg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO);
-		}
-		if (nle < 0)
-			continue;
+		if (_nl_send_nlmsg (platform,
+		                    nlmsg,
+		                    NULL,
+		                    NULL,
+		                    DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS,
+		                    out_refresh_all_in_progress) < 0)
+			goto next_after_fail;
 
-		if (_nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progress) < 0) {
-			nm_assert (*out_refresh_all_in_progress > 0);
-			*out_refresh_all_in_progress -= 1;
-		}
+		continue;
+
+next_after_fail:
+		nm_assert (*out_refresh_all_in_progress > 0);
+		*out_refresh_all_in_progress -= 1;
 	}
 }
 
@@ -5273,9 +5392,9 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 	NMPCacheOpsType cache_op;
 	struct nlmsghdr *msghdr;
 	char buf_nlmsghdr[400];
-	gboolean id_only = FALSE;
+	gboolean is_del = FALSE;
+	gboolean is_dump = FALSE;
 	NMPCache *cache = nm_platform_get_cache (platform);
-	gboolean is_dump;
 
 	msghdr = nlmsg_hdr (msg);
 
@@ -5286,37 +5405,38 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 	if (!handle_events)
 		return;
 
-	if (NM_IN_SET (msghdr->nlmsg_type, RTM_DELLINK, RTM_DELADDR, RTM_DELROUTE)) {
+	if (NM_IN_SET (msghdr->nlmsg_type, RTM_DELLINK,
+	                                   RTM_DELADDR,
+	                                   RTM_DELROUTE,
+	                                   RTM_DELQDISC,
+	                                   RTM_DELTFILTER)) {
 		/* The event notifies about a deleted object. We don't need to initialize all
 		 * fields of the object. */
-		id_only = TRUE;
+		is_del = TRUE;
 	}
 
-	obj = nmp_object_new_from_nl (platform, cache, msg, id_only);
+	obj = nmp_object_new_from_nl (platform, cache, msg, is_del);
 	if (!obj) {
 		_LOGT ("event-notification: %s: ignore",
 		       nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)));
 		return;
 	}
 
-	switch (msghdr->nlmsg_type) {
-	case RTM_NEWADDR:
-	case RTM_NEWLINK:
-	case RTM_NEWROUTE:
-	case RTM_NEWQDISC:
-	case RTM_NEWTFILTER:
+	if (   !is_del
+	    && NM_IN_SET (msghdr->nlmsg_type, RTM_NEWADDR,
+	                                      RTM_NEWLINK,
+	                                      RTM_NEWROUTE,
+	                                      RTM_NEWQDISC,
+	                                      RTM_NEWTFILTER)) {
 		is_dump = delayed_action_refresh_all_in_progress (platform,
 		                                                  delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj)));
-		break;
-	default:
-		is_dump = FALSE;
 	}
 
 	_LOGT ("event-notification: %s%s: %s",
 	       nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)),
 	       is_dump ? ", in-dump" : "",
 	       nmp_object_to_string (obj,
-	                             id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC,
+	                             is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC,
 	                             NULL, 0));
 
 	{
@@ -6067,6 +6187,7 @@ link_set_sriov_params (NMPlatform *platform,
 	gint64 current_num;
 	char ifname[IFNAMSIZ];
 	char buf[64];
+	int errsv;
 
 	if (!nm_platform_netns_push (platform, &netns))
 		return FALSE;
@@ -6114,7 +6235,8 @@ link_set_sriov_params (NMPlatform *platform,
 		                                                       ifname,
 		                                                      "device/sriov_numvfs"),
 		                             "0")) {
-			_LOGW ("link: couldn't reset SR-IOV num_vfs: %s", strerror (errno));
+			errsv = errno;
+			_LOGW ("link: couldn't reset SR-IOV num_vfs: %s", nm_strerror_native (errsv));
 			return FALSE;
 		}
 	}
@@ -6129,7 +6251,8 @@ link_set_sriov_params (NMPlatform *platform,
 	                                                          ifname,
 	                                                          "device/sriov_drivers_autoprobe"),
 	                                nm_sprintf_buf (buf, "%d", (int) autoprobe))) {
-		_LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", (int) autoprobe, strerror (errno));
+		errsv = errno;
+		_LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", (int) autoprobe, nm_strerror_native (errsv));
 		return FALSE;
 	}
 
@@ -6138,7 +6261,8 @@ link_set_sriov_params (NMPlatform *platform,
 	                                                       ifname,
 	                                                       "device/sriov_numvfs"),
 	                             nm_sprintf_buf (buf, "%u", num_vfs))) {
-		_LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", num_vfs, strerror (errno));
+		errsv = errno;
+		_LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", num_vfs, nm_strerror_native (errsv));
 		return FALSE;
 	}
 
@@ -7373,7 +7497,7 @@ ip6_address_add (NMPlatform *platform,
 	                             ifindex,
 	                             &addr,
 	                             plen,
-	                             &peer_addr,
+	                             IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr,
 	                             flags,
 	                             RT_SCOPE_UNIVERSE,
 	                             lifetime,
@@ -7548,7 +7672,7 @@ ip_route_get (NMPlatform *platform,
 		nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, NULL, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route);
 		if (nle < 0) {
 			_LOGE ("get-route: failure sending netlink request \"%s\" (%d)",
-			       g_strerror (-nle), -nle);
+			       nm_strerror_native (-nle), -nle);
 			return -NME_UNSPEC;
 		}
 
@@ -7792,20 +7916,22 @@ continue_reading:
 				err = -NME_NL_MSG_TRUNC;
 				abort_parsing = TRUE;
 			} else if (e->error) {
-				int errsv = e->error > 0 ? e->error : -e->error;
+				int errsv = nm_errno_native (e->error);
 
 				if (   NM_FLAGS_HAS (hdr->nlmsg_flags, NLM_F_ACK_TLVS)
 				    && hdr->nlmsg_len >= sizeof (*e) + e->msg.nlmsg_len) {
-					static const struct nla_policy policy[NLMSGERR_ATTR_MAX + 1] = {
+					static const struct nla_policy policy[] = {
 						[NLMSGERR_ATTR_MSG]     = { .type = NLA_STRING },
 						[NLMSGERR_ATTR_OFFS]    = { .type = NLA_U32 },
 					};
-					struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
+					struct nlattr *tb[G_N_ELEMENTS (policy)];
 					struct nlattr *tlvs;
 
 					tlvs = (struct nlattr *) ((char *) e + sizeof (*e) + e->msg.nlmsg_len - NLMSG_HDRLEN);
-					if (!nla_parse (tb, NLMSGERR_ATTR_MAX, tlvs,
-					                hdr->nlmsg_len - sizeof (*e) - e->msg.nlmsg_len, policy)) {
+					if (nla_parse_arr (tb,
+					                   tlvs,
+					                   hdr->nlmsg_len - sizeof (*e) - e->msg.nlmsg_len,
+					                   policy) >= 0) {
 						if (tb[NLMSGERR_ATTR_MSG])
 							extack_msg = nla_get_string (tb[NLMSGERR_ATTR_MSG]);
 					}
@@ -7813,11 +7939,11 @@ continue_reading:
 
 				/* Error message reported back from kernel. */
 				_LOGD ("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d",
-				       strerror (errsv),
+				       nm_strerror_native (errsv),
 				       errsv,
 				       NM_PRINT_FMT_QUOTED (extack_msg, " \"", extack_msg, "\"", ""),
 				       nlmsg_hdr (msg)->nlmsg_seq);
-				seq_result = -errsv;
+				seq_result = -NM_ERRNO_NATIVE (errsv);
 			} else
 				seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
 		} else
@@ -7976,7 +8102,7 @@ after_read:
 			int errsv = errno;
 
 			if (errsv != EINTR) {
-				_LOGE ("netlink: read: poll failed with %s", strerror (errsv));
+				_LOGE ("netlink: read: poll failed with %s", nm_strerror_native (errsv));
 				delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL);
 				return any;
 			}
diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c
index 6abc1c3a..71506a2c 100644
--- a/src/platform/nm-netlink.c
+++ b/src/platform/nm-netlink.c
@@ -25,8 +25,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#include "nm-utils/nm-errno.h"
-
 /*****************************************************************************/
 
 #ifndef SOL_NETLINK
@@ -104,21 +102,30 @@ nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len)
 	b = buf;
 
 	switch (hdr->nlmsg_type) {
-	case RTM_NEWLINK:    s = "RTM_NEWLINK";  break;
-	case RTM_DELLINK:    s = "RTM_DELLINK";  break;
-	case RTM_NEWADDR:    s = "RTM_NEWADDR";  break;
-	case RTM_DELADDR:    s = "RTM_DELADDR";  break;
-	case RTM_NEWROUTE:   s = "RTM_NEWROUTE"; break;
-	case RTM_DELROUTE:   s = "RTM_DELROUTE"; break;
-	case RTM_NEWQDISC:   s = "RTM_NEWQDISC"; break;
-	case RTM_DELQDISC:   s = "RTM_DELQDISC"; break;
-	case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break;
-	case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break;
-	case NLMSG_NOOP:     s = "NLMSG_NOOP"; break;
-	case NLMSG_ERROR:    s = "NLMSG_ERROR"; break;
-	case NLMSG_DONE:     s = "NLMSG_DONE"; break;
-	case NLMSG_OVERRUN:  s = "NLMSG_OVERRUN"; break;
-	default:             s = NULL;       break;
+	case RTM_GETLINK:    s = "RTM_GETLINK";     break;
+	case RTM_NEWLINK:    s = "RTM_NEWLINK";     break;
+	case RTM_DELLINK:    s = "RTM_DELLINK";     break;
+	case RTM_SETLINK:    s = "RTM_SETLINK";     break;
+	case RTM_GETADDR:    s = "RTM_GETADDR";     break;
+	case RTM_NEWADDR:    s = "RTM_NEWADDR";     break;
+	case RTM_DELADDR:    s = "RTM_DELADDR";     break;
+	case RTM_GETROUTE:   s = "RTM_GETROUTE";    break;
+	case RTM_NEWROUTE:   s = "RTM_NEWROUTE";    break;
+	case RTM_DELROUTE:   s = "RTM_DELROUTE";    break;
+	case RTM_GETRULE:    s = "RTM_GETRULE";     break;
+	case RTM_NEWRULE:    s = "RTM_NEWRULE";     break;
+	case RTM_DELRULE:    s = "RTM_DELRULE";     break;
+	case RTM_GETQDISC:   s = "RTM_GETQDISC";    break;
+	case RTM_NEWQDISC:   s = "RTM_NEWQDISC";    break;
+	case RTM_DELQDISC:   s = "RTM_DELQDISC";    break;
+	case RTM_GETTFILTER: s = "RTM_GETTFILTER";  break;
+	case RTM_NEWTFILTER: s = "RTM_NEWTFILTER";  break;
+	case RTM_DELTFILTER: s = "RTM_DELTFILTER";  break;
+	case NLMSG_NOOP:     s = "NLMSG_NOOP";      break;
+	case NLMSG_ERROR:    s = "NLMSG_ERROR";     break;
+	case NLMSG_DONE:     s = "NLMSG_DONE";      break;
+	case NLMSG_OVERRUN:  s = "NLMSG_OVERRUN";   break;
+	default:             s = NULL;              break;
 	}
 
 	if (s)
@@ -207,6 +214,8 @@ nlmsg_reserve (struct nl_msg *n, size_t len, int pad)
 	size_t nlmsg_len = n->nm_nlh->nlmsg_len;
 	size_t tlen;
 
+	nm_assert (pad >= 0);
+
 	if (len > n->nm_size)
 		return NULL;
 
@@ -261,11 +270,12 @@ nlmsg_alloc_size (size_t len)
 	if (len < sizeof (struct nlmsghdr))
 		len = sizeof (struct nlmsghdr);
 
-	nm = g_slice_new0 (struct nl_msg);
-
-	nm->nm_protocol = -1;
-	nm->nm_size = len;
-	nm->nm_nlh = g_malloc0 (len);
+	nm = g_slice_new (struct nl_msg);
+	*nm = (struct nl_msg) {
+		.nm_protocol = -1,
+		.nm_size = len,
+		.nm_nlh = g_malloc0 (len),
+	};
 	nm->nm_nlh->nlmsg_len = nlmsg_total_size (0);
 	return nm;
 }
@@ -320,10 +330,18 @@ void nlmsg_free (struct nl_msg *msg)
 /*****************************************************************************/
 
 int
-nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad)
+nlmsg_append (struct nl_msg *n,
+              const void *data,
+              size_t len,
+              int pad)
 {
 	void *tmp;
 
+	nm_assert (n);
+	nm_assert (data);
+	nm_assert (len > 0);
+	nm_assert (pad >= 0);
+
 	tmp = nlmsg_reserve (n, len, pad);
 	if (tmp == NULL)
 		return -ENOMEM;
@@ -367,48 +385,77 @@ nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq,
 	return nlh;
 }
 
-uint64_t
-nla_get_u64 (const struct nlattr *nla)
-{
-	uint64_t tmp = 0;
-
-	if (nla && nla_len (nla) >= sizeof (tmp))
-		memcpy (&tmp, nla_data (nla), sizeof (tmp));
-
-	return tmp;
-}
-
 size_t
-nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize)
-{
-	size_t srclen = nla_len (nla);
-	const char *src = nla_data (nla);
-
-	if (srclen > 0 && src[srclen - 1] == '\0')
-		srclen--;
+nla_strlcpy (char *dst,
+             const struct nlattr *nla,
+             size_t dstsize)
+{
+	const char *src;
+	size_t srclen;
+	size_t len;
+
+	/* - Always writes @dstsize bytes to @dst
+	 * - Copies the first non-NUL characters to @dst.
+	 *   Any characters after the first NUL bytes in @nla are ignored.
+	 * - If the string @nla is longer than @dstsize, the string
+	 *   gets truncated. @dst will always be NUL terminated. */
+
+	if (G_UNLIKELY (dstsize <= 1)) {
+		if (dstsize == 1)
+			dst[0] = '\0';
+		if (   nla
+		    && (srclen = nla_len (nla)) > 0)
+			return strnlen (nla_data (nla), srclen);
+		return 0;
+	}
 
-	if (dstsize > 0) {
-		size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen;
+	nm_assert (dst);
 
-		memset (dst, 0, dstsize);
-		memcpy (dst, src, len);
+	if (nla) {
+		srclen = nla_len (nla);
+		if (srclen > 0) {
+			src = nla_data (nla);
+			srclen = strnlen (src, srclen);
+			if (srclen > 0) {
+				len = NM_MIN (dstsize - 1, srclen);
+				memcpy (dst, src, len);
+				memset (&dst[len], 0, dstsize - len);
+				return srclen;
+			}
+		}
 	}
 
-	return srclen;
+	memset (dst, 0, dstsize);
+	return 0;
 }
 
-int
-nla_memcpy (void *dest, const struct nlattr *src, int count)
+size_t
+nla_memcpy (void *dst, const struct nlattr *nla, size_t dstsize)
 {
-	int minlen;
+	size_t len;
+	int srclen;
 
-	if (!src)
+	if (!nla)
 		return 0;
 
-	minlen = NM_MIN (count, (int) nla_len (src));
-	memcpy (dest, nla_data (src), minlen);
+	srclen = nla_len (nla);
 
-	return minlen;
+	if (srclen <= 0) {
+		nm_assert (srclen == 0);
+		return 0;
+	}
+
+	len = NM_MIN ((size_t) srclen, dstsize);
+	if (len > 0) {
+		/* there is a crucial difference between nla_strlcpy() and nla_memcpy().
+		 * The former always write @dstsize bytes (akin to strncpy()), here, we only
+		 * write the bytes that we actually have (leaving the remainder undefined). */
+		memcpy (dst,
+		        nla_data (nla),
+		        len);
+	}
+
+	return srclen;
 }
 
 int
@@ -548,7 +595,11 @@ validate_nla (const struct nlattr *nla, int maxtype,
 		return -NME_UNSPEC;
 
 	if (pt->type == NLA_STRING) {
-		const char *data = nla_data (nla);
+		const char *data;
+
+		nm_assert (minlen > 0);
+
+		data = nla_data (nla);
 		if (data[nla_len (nla) - 1] != '\0')
 			return -NME_UNSPEC;
 	}
@@ -574,15 +625,13 @@ nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
 		if (policy) {
 			nmerr = validate_nla (nla, maxtype, policy);
 			if (nmerr < 0)
-				goto errout;
+				return nmerr;
 		}
 
 		tb[type] = nla;
 	}
 
-	nmerr = 0;
-errout:
-	return nmerr;
+	return 0;
 }
 
 /*****************************************************************************/
@@ -721,21 +770,21 @@ genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
 static int
 _genl_parse_getfamily (struct nl_msg *msg, void *arg)
 {
-	static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
+	static const struct nla_policy ctrl_policy[] = {
 		[CTRL_ATTR_FAMILY_ID]    = { .type = NLA_U16 },
 		[CTRL_ATTR_FAMILY_NAME]  = { .type = NLA_STRING,
-		                            .maxlen = GENL_NAMSIZ },
+		                             .maxlen = GENL_NAMSIZ },
 		[CTRL_ATTR_VERSION]      = { .type = NLA_U32 },
 		[CTRL_ATTR_HDRSIZE]      = { .type = NLA_U32 },
 		[CTRL_ATTR_MAXATTR]      = { .type = NLA_U32 },
 		[CTRL_ATTR_OPS]          = { .type = NLA_NESTED },
 		[CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED },
 	};
-	struct nlattr *tb[CTRL_ATTR_MAX+1];
+	struct nlattr *tb[G_N_ELEMENTS (ctrl_policy)];
 	struct nlmsghdr *nlh = nlmsg_hdr (msg);
 	gint32 *response_data = arg;
 
-	if (genlmsg_parse (nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy))
+	if (genlmsg_parse_arr (nlh, 0, tb, ctrl_policy) < 0)
 		return NL_SKIP;
 
 	if (tb[CTRL_ATTR_FAMILY_ID])
@@ -1061,7 +1110,7 @@ do { \
 		nmerr = _cb->type##_cb ((msg), _cb->type##_arg); \
 		switch (nmerr) { \
 		case NL_OK: \
-			nmerr = 0; \
+			nm_assert (nmerr == 0); \
 			break; \
 		case NL_SKIP: \
 			goto skip; \
@@ -1206,6 +1255,7 @@ skip:
 		/* Multipart message not yet complete, continue reading */
 		nm_clear_g_free (&buf);
 
+		nmerr = 0;
 		goto continue_reading;
 	}
 
@@ -1333,6 +1383,7 @@ nl_recv (struct nl_sock *sk,
 	struct ucred tmpcreds;
 	gboolean tmpcreds_has = FALSE;
 	int retval;
+	int errsv;
 
 	nm_assert (nla);
 	nm_assert (buf && !*buf);
@@ -1361,10 +1412,10 @@ retry:
 	}
 
 	if (n < 0) {
-		if (errno == EINTR)
+		errsv = errno;
+		if (errsv == EINTR)
 			goto retry;
-
-		retval = -nm_errno_from_native (errno);
+		retval = -nm_errno_from_native (errsv);
 		goto abort;
 	}
 
diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h
index 84bbe27b..094a3c6f 100644
--- a/src/platform/nm-netlink.h
+++ b/src/platform/nm-netlink.h
@@ -25,6 +25,8 @@
 #include <linux/rtnetlink.h>
 #include <linux/genetlink.h>
 
+#include "nm-utils/unaligned.h"
+
 /*****************************************************************************/
 
 #define NLMSGERR_ATTR_UNUSED            0
@@ -87,8 +89,26 @@ struct nla_policy {
 
 /*****************************************************************************/
 
+/* static asserts that @tb and @policy are suitable arguments to nla_parse(). */
+#define _nl_static_assert_tb(tb, policy) \
+	G_STMT_START { \
+		\
+		G_STATIC_ASSERT_EXPR (G_N_ELEMENTS (tb) > 0); \
+		\
+		/* we allow @policy to be either NULL or a C array. */ \
+		G_STATIC_ASSERT_EXPR (   sizeof (policy) == sizeof (NULL) \
+		                      || G_N_ELEMENTS (tb) == (sizeof (policy) / sizeof (struct nla_policy))); \
+		\
+		/* For above check to work, we don't support policy being an array with same size as
+		 * sizeof(NULL), otherwise, the compile time check breaks down. */ \
+		G_STATIC_ASSERT_EXPR (sizeof (NULL) != G_N_ELEMENTS (tb) * sizeof (struct nla_policy)); \
+		\
+	} G_STMT_END
+
+/*****************************************************************************/
+
 static inline int
-nla_attr_size(int payload)
+nla_attr_size (int payload)
 {
 	nm_assert (payload >= 0);
 
@@ -104,7 +124,7 @@ nla_total_size (int payload)
 static inline int
 nla_padlen (int payload)
 {
-	return nla_total_size(payload) - nla_attr_size(payload);
+	return nla_total_size (payload) - nla_attr_size (payload);
 }
 
 struct nlattr *nla_reserve (struct nl_msg *msg, int attrtype, int attrlen);
@@ -112,32 +132,56 @@ struct nlattr *nla_reserve (struct nl_msg *msg, int attrtype, int attrlen);
 static inline int
 nla_len (const struct nlattr *nla)
 {
-	return nla->nla_len - NLA_HDRLEN;
+	nm_assert (nla);
+	nm_assert (nla->nla_len >= NLA_HDRLEN);
+
+	return ((int) nla->nla_len) - NLA_HDRLEN;
 }
 
 static inline int
 nla_type (const struct nlattr *nla)
 {
+	nm_assert (nla_len (nla) >= 0);
+
 	return nla->nla_type & NLA_TYPE_MASK;
 }
 
 static inline void *
 nla_data (const struct nlattr *nla)
 {
-	nm_assert (nla);
-	return (char *) nla + NLA_HDRLEN;
+	nm_assert (nla_len (nla) >= 0);
+
+	return &(((char *) nla)[NLA_HDRLEN]);
 }
 
+#define nla_data_as(type, nla) \
+	({ \
+		const struct nlattr *_nla = (nla); \
+		\
+		nm_assert (nla_len (_nla) >= sizeof (type)); \
+		\
+		/* note that casting the pointer is undefined behavior in C, if
+		 * the data has wrong alignment. Netlink data is aligned to 4 bytes,
+		 * that means, if the alignment is larger than 4, this is invalid. */ \
+		G_STATIC_ASSERT_EXPR (_nm_alignof (type) <= NLA_ALIGNTO); \
+		\
+		(type *) nla_data (_nla); \
+	})
+
 static inline uint8_t
 nla_get_u8 (const struct nlattr *nla)
 {
-	return *(const uint8_t *) nla_data (nla);
+	nm_assert (nla_len (nla) >= sizeof (uint8_t));
+
+	return *((const uint8_t *) nla_data (nla));
 }
 
-static inline uint8_t
+static inline int8_t
 nla_get_s8 (const struct nlattr *nla)
 {
-	return *(const int8_t *) nla_data (nla);
+	nm_assert (nla_len (nla) >= sizeof (int8_t));
+
+	return *((const int8_t *) nla_data (nla));
 }
 
 static inline uint8_t
@@ -152,39 +196,80 @@ nla_get_u8_cond (/*const*/ struct nlattr *const*tb, int attr, uint8_t default_va
 static inline uint16_t
 nla_get_u16 (const struct nlattr *nla)
 {
-	return *(const uint16_t *) nla_data (nla);
+	nm_assert (nla_len (nla) >= sizeof (uint16_t));
+
+	return *((const uint16_t *) nla_data (nla));
 }
 
 static inline uint32_t
-nla_get_u32(const struct nlattr *nla)
+nla_get_u32 (const struct nlattr *nla)
 {
-	return *(const uint32_t *) nla_data (nla);
+	nm_assert (nla_len (nla) >= sizeof (uint32_t));
+
+	return *((const uint32_t *) nla_data (nla));
 }
 
 static inline int32_t
-nla_get_s32(const struct nlattr *nla)
+nla_get_s32 (const struct nlattr *nla)
 {
-	return *(const int32_t *) nla_data (nla);
+	nm_assert (nla_len (nla) >= sizeof (int32_t));
+
+	return *((const int32_t *) nla_data (nla));
+}
+
+static inline uint64_t
+nla_get_u64 (const struct nlattr *nla)
+{
+	nm_assert (nla_len (nla) >= sizeof (uint64_t));
+
+	return unaligned_read_ne64 (nla_data (nla));
 }
 
-uint64_t nla_get_u64 (const struct nlattr *nla);
+static inline uint64_t
+nla_get_be64 (const struct nlattr *nla)
+{
+	nm_assert (nla_len (nla) >= sizeof (uint64_t));
+
+	return unaligned_read_be64 (nla_data (nla));
+}
 
 static inline char *
 nla_get_string (const struct nlattr *nla)
 {
+	nm_assert (nla_len (nla) >= 0);
+
 	return (char *) nla_data (nla);
 }
 
 size_t nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize);
 
-int nla_memcpy (void *dest, const struct nlattr *src, int count);
+size_t nla_memcpy (void *dst, const struct nlattr *nla, size_t dstsize);
+
+#define nla_memcpy_checked_size(dst, nla, dstsize) \
+	G_STMT_START { \
+		void *const _dst = (dst); \
+		const struct nlattr *const _nla = (nla); \
+		const size_t _dstsize = (dstsize); \
+		size_t _srcsize; \
+		\
+		/* assert that, if @nla is given, that it has the exact expected
+		 * size. This implies that the caller previously verified the length
+		 * of the attribute (via minlen/maxlen at nla_parse()). */ \
+		\
+		if (_nla) { \
+			_srcsize = nla_memcpy (_dst, _nla, _dstsize); \
+			nm_assert (_srcsize == _dstsize); \
+		} \
+	} G_STMT_END
 
 int nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data);
 
 static inline int
 nla_put_string (struct nl_msg *msg, int attrtype, const char *str)
 {
-	return nla_put(msg, attrtype, strlen(str) + 1, str);
+	nm_assert (str);
+
+	return nla_put (msg, attrtype, strlen (str) + 1, str);
 }
 
 static inline int
@@ -206,55 +291,55 @@ nla_put_uint32 (struct nl_msg *msg, int attrtype, uint32_t val)
 }
 
 #define NLA_PUT(msg, attrtype, attrlen, data) \
-	do { \
-		if (nla_put(msg, attrtype, attrlen, data) < 0) \
+	G_STMT_START { \
+		if (nla_put (msg, attrtype, attrlen, data) < 0) \
 			goto nla_put_failure; \
-	} while(0)
+	} G_STMT_END
 
 #define NLA_PUT_TYPE(msg, type, attrtype, value) \
-	do { \
+	G_STMT_START { \
 		type __nla_tmp = value; \
-		NLA_PUT(msg, attrtype, sizeof(type), &__nla_tmp); \
-	} while(0)
+		NLA_PUT (msg, attrtype, sizeof (type), &__nla_tmp); \
+	} G_STMT_END
 
 #define NLA_PUT_U8(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, uint8_t, attrtype, value)
+	NLA_PUT_TYPE (msg, uint8_t, attrtype, value)
 
 #define NLA_PUT_S8(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, int8_t, attrtype, value)
+	NLA_PUT_TYPE (msg, int8_t, attrtype, value)
 
 #define NLA_PUT_U16(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, uint16_t, attrtype, value)
+	NLA_PUT_TYPE (msg, uint16_t, attrtype, value)
 
 #define NLA_PUT_U32(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, uint32_t, attrtype, value)
+	NLA_PUT_TYPE (msg, uint32_t, attrtype, value)
 
 #define NLA_PUT_S32(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, int32_t, attrtype, value)
+	NLA_PUT_TYPE (msg, int32_t, attrtype, value)
 
 #define NLA_PUT_U64(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, uint64_t, attrtype, value)
+	NLA_PUT_TYPE (msg, uint64_t, attrtype, value)
 
 #define NLA_PUT_STRING(msg, attrtype, value) \
-	NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value)
+	NLA_PUT (msg, attrtype, (int) strlen (value) + 1, value)
 
 #define NLA_PUT_FLAG(msg, attrtype) \
-	NLA_PUT(msg, attrtype, 0, NULL)
+	NLA_PUT (msg, attrtype, 0, NULL)
 
 struct nlattr *nla_find (const struct nlattr *head, int len, int attrtype);
 
 static inline int
 nla_ok (const struct nlattr *nla, int remaining)
 {
-	return remaining >= (int) sizeof(*nla) &&
-	       nla->nla_len >= sizeof(*nla) &&
+	return remaining >= (int) sizeof (*nla) &&
+	       nla->nla_len >= sizeof (*nla) &&
 	       nla->nla_len <= remaining;
 }
 
 static inline struct nlattr *
-nla_next(const struct nlattr *nla, int *remaining)
+nla_next (const struct nlattr *nla, int *remaining)
 {
-	int totlen = NLA_ALIGN(nla->nla_len);
+	int totlen = NLA_ALIGN (nla->nla_len);
 
 	*remaining -= totlen;
 	return (struct nlattr *) ((char *) nla + totlen);
@@ -262,28 +347,47 @@ nla_next(const struct nlattr *nla, int *remaining)
 
 #define nla_for_each_attr(pos, head, len, rem) \
 	for (pos = head, rem = len; \
-	     nla_ok(pos, rem); \
-	     pos = nla_next(pos, &(rem)))
+	     nla_ok (pos, rem); \
+	     pos = nla_next (pos, &(rem)))
 
 #define nla_for_each_nested(pos, nla, rem) \
-	for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); \
-	     nla_ok(pos, rem); \
-	     pos = nla_next(pos, &(rem)))
+	for (pos = (struct nlattr *) nla_data (nla), rem = nla_len (nla); \
+	     nla_ok (pos, rem); \
+	     pos = nla_next (pos, &(rem)))
 
 void nla_nest_cancel (struct nl_msg *msg, const struct nlattr *attr);
 struct nlattr *nla_nest_start (struct nl_msg *msg, int attrtype);
 int nla_nest_end (struct nl_msg *msg, struct nlattr *start);
 
-int nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
+int nla_parse (struct nlattr *tb[],
+               int maxtype,
+               struct nlattr *head,
+               int len,
                const struct nla_policy *policy);
 
+#define nla_parse_arr(tb, head, len, policy) \
+	({ \
+		_nl_static_assert_tb ((tb), (policy)); \
+		\
+		nla_parse ((tb), G_N_ELEMENTS (tb) - 1, (head), (len), (policy)); \
+	})
+
 static inline int
-nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla,
+nla_parse_nested (struct nlattr *tb[],
+                  int maxtype,
+                  struct nlattr *nla,
                   const struct nla_policy *policy)
 {
-	return nla_parse (tb, maxtype, nla_data(nla), nla_len(nla), policy);
+	return nla_parse (tb, maxtype, nla_data (nla), nla_len (nla), policy);
 }
 
+#define nla_parse_nested_arr(tb, nla, policy) \
+	({ \
+		_nl_static_assert_tb ((tb), (policy)); \
+		\
+		nla_parse_nested ((tb), G_N_ELEMENTS (tb) - 1, (nla), (policy)); \
+	})
+
 /*****************************************************************************/
 
 struct nl_msg *nlmsg_alloc (void);
@@ -296,7 +400,13 @@ struct nl_msg *nlmsg_alloc_simple (int nlmsgtype, int flags);
 
 void *nlmsg_reserve (struct nl_msg *n, size_t len, int pad);
 
-int nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad);
+int nlmsg_append (struct nl_msg *n,
+                  const void *data,
+                  size_t len,
+                  int pad);
+
+#define nlmsg_append_struct(n, data) \
+	nlmsg_append (n, (data), sizeof (*(data)), NLMSG_ALIGNTO)
 
 void nlmsg_free (struct nl_msg *msg);
 
@@ -316,15 +426,15 @@ nlmsg_total_size (int payload)
 static inline int
 nlmsg_ok (const struct nlmsghdr *nlh, int remaining)
 {
-	return (remaining >= (int)sizeof(struct nlmsghdr) &&
-	       nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
+	return (remaining >= (int) sizeof (struct nlmsghdr) &&
+	       nlh->nlmsg_len >= sizeof (struct nlmsghdr) &&
 	       nlh->nlmsg_len <= remaining);
 }
 
 static inline struct nlmsghdr *
 nlmsg_next (struct nlmsghdr *nlh, int *remaining)
 {
-	int totlen = NLMSG_ALIGN(nlh->nlmsg_len);
+	int totlen = NLMSG_ALIGN (nlh->nlmsg_len);
 
 	*remaining -= totlen;
 
@@ -344,7 +454,7 @@ _nm_auto_nl_msg_cleanup (struct nl_msg **ptr)
 {
 	nlmsg_free (*ptr);
 }
-#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup)
+#define nm_auto_nlmsg nm_auto (_nm_auto_nl_msg_cleanup)
 
 static inline void *
 nlmsg_data (const struct nlmsghdr *nlh)
@@ -355,13 +465,13 @@ nlmsg_data (const struct nlmsghdr *nlh)
 static inline void *
 nlmsg_tail (const struct nlmsghdr *nlh)
 {
-	return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len);
+	return (unsigned char *) nlh + NLMSG_ALIGN (nlh->nlmsg_len);
 }
 
 struct nlmsghdr *nlmsg_hdr (struct nl_msg *n);
 
 static inline int
-nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen)
+nlmsg_valid_hdr (const struct nlmsghdr *nlh, int hdrlen)
 {
 	if (nlh->nlmsg_len < nlmsg_size (hdrlen))
 		return 0;
@@ -384,8 +494,8 @@ nlmsg_attrlen (const struct nlmsghdr *nlh, int hdrlen)
 static inline struct nlattr *
 nlmsg_attrdata (const struct nlmsghdr *nlh, int hdrlen)
 {
-	unsigned char *data = nlmsg_data(nlh);
-	return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen));
+	unsigned char *data = nlmsg_data (nlh);
+	return (struct nlattr *) (data + NLMSG_ALIGN (hdrlen));
 }
 
 static inline struct nlattr *
@@ -396,8 +506,19 @@ nlmsg_find_attr (struct nlmsghdr *nlh, int hdrlen, int attrtype)
 	                 attrtype);
 }
 
-int nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-                 int maxtype, const struct nla_policy *policy);
+int nlmsg_parse (struct nlmsghdr *nlh,
+                 int hdrlen,
+                 struct nlattr *tb[],
+                 int maxtype,
+                 const struct nla_policy *policy);
+
+#define nlmsg_parse_arr(nlh, hdrlen, tb, policy) \
+	({ \
+		_nl_static_assert_tb ((tb), (policy)); \
+		G_STATIC_ASSERT_EXPR ((hdrlen) >= 0); \
+		\
+		nlmsg_parse ((nlh), (hdrlen), (tb), G_N_ELEMENTS (tb) - 1, (policy)); \
+	})
 
 struct nlmsghdr *nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq,
                             int type, int payload, int flags);
@@ -499,8 +620,20 @@ struct nlattr *genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen);
 int genlmsg_len (const struct genlmsghdr *gnlh);
 int genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen);
 int genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen);
-int genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-                   int maxtype, const struct nla_policy *policy);
+
+int genlmsg_parse (struct nlmsghdr *nlh,
+                   int hdrlen,
+                   struct nlattr *tb[],
+                   int maxtype,
+                   const struct nla_policy *policy);
+
+#define genlmsg_parse_arr(nlh, hdrlen, tb, policy) \
+	({ \
+		_nl_static_assert_tb ((tb), (policy)); \
+		G_STATIC_ASSERT_EXPR ((hdrlen) >= 0); \
+		\
+		genlmsg_parse ((nlh), (hdrlen), (tb), G_N_ELEMENTS (tb) - 1, (policy)); \
+	})
 
 int genl_ctrl_resolve (struct nl_sock *sk, const char *name);
 
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index 772d4667..93cd0b3c 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -22,9 +22,7 @@
 
 #include "nm-platform-utils.h"
 
-#include <string.h>
 #include <unistd.h>
-#include <errno.h>
 #include <sys/ioctl.h>
 #include <linux/ethtool.h>
 #include <linux/sockios.h>
@@ -86,7 +84,7 @@ socket_handle_init (SocketHandle *shandle, int ifindex)
 	shandle->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
 	if (shandle->fd < 0) {
 		shandle->ifindex = 0;
-		return -errno;
+		return -NM_ERRNO_NATIVE (errno);
 	}
 
 	shandle->ifindex = ifindex;
@@ -159,8 +157,8 @@ ethtool_call_handle (SocketHandle *shandle, gpointer edata)
 		              shandle->ifindex,
 		              _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)),
 		              shandle->ifname,
-		              strerror (errsv));
-		return -errsv;
+		              nm_strerror_native (errsv));
+		return -NM_ERRNO_NATIVE (errsv);
 	}
 
 	nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success",
@@ -183,7 +181,7 @@ ethtool_call_ifindex (int ifindex, gpointer edata)
 		nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s",
 		              ifindex,
 		              _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)),
-		              g_strerror (-r));
+		              nm_strerror_native (-r));
 		return r;
 	}
 
@@ -489,7 +487,7 @@ nmp_utils_ethtool_get_features (int ifindex)
 		nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s",
 		              ifindex,
 		              "get-features",
-		              g_strerror (-r));
+		              nm_strerror_native (-r));
 		return FALSE;
 	}
 
@@ -620,7 +618,7 @@ nmp_utils_ethtool_set_features (int ifindex,
 		nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s",
 		              ifindex,
 		              "set-features",
-		              g_strerror (-r));
+		              nm_strerror_native (-r));
 		return FALSE;
 	}
 
@@ -656,7 +654,7 @@ nmp_utils_ethtool_set_features (int ifindex,
 		nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure setting features (%s)",
 		              ifindex,
 		              "set-features",
-		              g_strerror (-r));
+		              nm_strerror_native (-r));
 		return FALSE;
 	}
 
@@ -766,7 +764,7 @@ nmp_utils_ethtool_supports_vlans (int ifindex)
 		nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s",
 		              ifindex,
 		              "support-vlans",
-		              g_strerror (-r));
+		              nm_strerror_native (-r));
 		return FALSE;
 	}
 
@@ -805,7 +803,7 @@ nmp_utils_ethtool_get_peer_ifindex (int ifindex)
 		nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s",
 		              ifindex,
 		              "get-peer-ifindex",
-		              g_strerror (-r));
+		              nm_strerror_native (-r));
 		return FALSE;
 	}
 
@@ -894,7 +892,7 @@ nmp_utils_ethtool_get_link_settings (int ifindex,
                          | ADVERTISED_1000baseT_Full \
                          | ADVERTISED_10000baseT_Full )
 
-static inline guint32
+static guint32
 get_baset_mode (guint32 speed, NMPlatformLinkDuplexType duplex)
 {
 	if (duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN)
@@ -1045,13 +1043,14 @@ nmp_utils_mii_supports_carrier_detect (int ifindex)
 	int r;
 	struct ifreq ifr;
 	struct mii_ioctl_data *mii;
+	int errsv;
 
 	g_return_val_if_fail (ifindex > 0, FALSE);
 
 	if ((r = socket_handle_init (&shandle, ifindex)) < 0) {
 		nm_log_trace (LOGD_PLATFORM, "mii[%d]: carrier-detect no: failed creating ethtool socket: %s",
 		              ifindex,
-		              g_strerror (-r));
+		              nm_strerror_native (-r));
 		return FALSE;
 	}
 
@@ -1059,7 +1058,8 @@ nmp_utils_mii_supports_carrier_detect (int ifindex)
 	memcpy (ifr.ifr_name, shandle.ifname, IFNAMSIZ);
 
 	if (ioctl (shandle.fd, SIOCGMIIPHY, &ifr) < 0) {
-		nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, shandle.ifname, strerror (errno));
+		errsv = errno;
+		nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, shandle.ifname, nm_strerror_native (errsv));
 		return FALSE;
 	}
 
@@ -1068,7 +1068,8 @@ nmp_utils_mii_supports_carrier_detect (int ifindex)
 	mii->reg_num = MII_BMSR;
 
 	if (ioctl (shandle.fd, SIOCGMIIREG, &ifr) != 0) {
-		nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, shandle.ifname, strerror (errno));
+		errsv = errno;
+		nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, shandle.ifname, nm_strerror_native (errsv));
 		return FALSE;
 	}
 
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 4bb31b17..fe0cb662 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -23,13 +23,11 @@
 #include "nm-platform.h"
 
 #include <stdlib.h>
-#include <errno.h>
 #include <unistd.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <sys/socket.h>
 #include <netdb.h>
-#include <string.h>
 #include <linux/ip.h>
 #include <linux/if.h>
 #include <linux/if_tun.h>
@@ -41,7 +39,7 @@
 #include "nm-core-internal.h"
 #include "nm-utils/nm-dedup-multi.h"
 #include "nm-utils/nm-udev-utils.h"
-#include "nm-utils/nm-errno.h"
+#include "nm-utils/nm-secret-utils.h"
 
 #include "nm-core-utils.h"
 #include "nm-platform-utils.h"
@@ -1985,6 +1983,24 @@ nm_platform_link_get_lnk_wireguard (NMPlatform *self, int ifindex, const NMPlatf
 
 /*****************************************************************************/
 
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_wireguard_change_flags_to_string, NMPlatformWireGuardChangeFlags,
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE,            "none"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS,   "replace-peers"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY, "has-private-key"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT, "has-listen-port"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK,      "has-fwmark"),
+);
+
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_wireguard_change_peer_flags_to_string, NMPlatformWireGuardChangePeerFlags,
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE,                   "none"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME,              "remove"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY,      "psk"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL, "ka"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT,           "ep"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS,         "aips"),
+	NM_UTILS_FLAGS2STR (NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS,     "remove-aips"),
+);
+
 int
 nm_platform_link_wireguard_add (NMPlatform *self,
                                 const char *name,
@@ -1998,8 +2014,9 @@ nm_platform_link_wireguard_change (NMPlatform *self,
                                    int ifindex,
                                    const NMPlatformLnkWireGuard *lnk_wireguard,
                                    const NMPWireGuardPeer *peers,
+                                   const NMPlatformWireGuardChangePeerFlags *peer_flags,
                                    guint peers_len,
-                                   gboolean replace_peers)
+                                   NMPlatformWireGuardChangeFlags change_flags)
 {
 	_CHECK_SELF (self, klass, -NME_BUG);
 
@@ -2008,6 +2025,7 @@ nm_platform_link_wireguard_change (NMPlatform *self,
 	if (_LOGD_ENABLED ()) {
 		char buf_lnk[256];
 		char buf_peers[512];
+		char buf_change_flags[100];
 
 		buf_peers[0] = '\0';
 		if (peers_len > 0) {
@@ -2020,25 +2038,31 @@ nm_platform_link_wireguard_change (NMPlatform *self,
 				nm_utils_strbuf_append_str (&b, &len, " { ");
 				nm_platform_wireguard_peer_to_string (&peers[i], b, len);
 				nm_utils_strbuf_seek_end (&b, &len);
+				if (peer_flags) {
+					nm_utils_strbuf_append (&b, &len,
+					                       " (%s)",
+					                       _wireguard_change_peer_flags_to_string (peer_flags[i], buf_change_flags, sizeof (buf_change_flags)));
+				}
 				nm_utils_strbuf_append_str (&b, &len, " } ");
 			}
 			nm_utils_strbuf_append_str (&b, &len, "}");
 		}
 
-		_LOG3D ("link: change wireguard ifindex %d, %s, %u peers%s%s",
+		_LOG3D ("link: change wireguard ifindex %d, %s, (%s), %u peers%s",
 		        ifindex,
 		        nm_platform_lnk_wireguard_to_string (lnk_wireguard, buf_lnk, sizeof (buf_lnk)),
+		        _wireguard_change_flags_to_string (change_flags, buf_change_flags, sizeof (buf_change_flags)),
 		        peers_len,
-		        buf_peers,
-		        replace_peers ? " (replace-peers)" : " (update-peers)");
+		        buf_peers);
 	}
 
 	return klass->link_wireguard_change (self,
 	                                     ifindex,
 	                                     lnk_wireguard,
 	                                     peers,
+	                                     peer_flags,
 	                                     peers_len,
-	                                     replace_peers);
+	                                     change_flags);
 }
 
 /*****************************************************************************/
@@ -2893,7 +2917,7 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe
  * nm_platform_link_tun_get_properties:
  * @self: the #NMPlatform instance
  * @ifindex: the ifindex to look up
- * @out_properties: (out): (allow-none): return the read properties
+ * @out_properties: (out) (allow-none): return the read properties
  *
  * Only recent versions of kernel export tun properties via netlink.
  * So, if that's the case, then we have the NMPlatformLnkTun instance
@@ -4218,7 +4242,7 @@ nm_platform_ip_route_get_prune_list (NMPlatform *self,
  *   at the end of the operation. Note that if @routes contains
  *   the same route, then it will not be deleted. @routes overrules
  *   @routes_prune list.
- * @out_temporary_not_available: (allow-none): (out): routes that could
+ * @out_temporary_not_available: (allow-none) (out): routes that could
  *   currently not be synced. The caller shall keep them and try later again.
  *
  * Returns: %TRUE on success.
@@ -5610,6 +5634,7 @@ nm_platform_wireguard_peer_to_string (const NMPWireGuardPeer *peer, char *buf, g
 	char s_sockaddr[NM_UTILS_INET_ADDRSTRLEN + 100];
 	char s_endpoint[20 + sizeof (s_sockaddr)];
 	char s_addr[NM_UTILS_INET_ADDRSTRLEN];
+	char s_keepalive[100];
 	guint i;
 
 	nm_utils_to_string_buffer_init (&buf, &len);
@@ -5627,18 +5652,22 @@ nm_platform_wireguard_peer_to_string (const NMPWireGuardPeer *peer, char *buf, g
 
 	nm_utils_strbuf_append (&buf, &len,
 	                        "public-key %s"
-	                        "%s" /* preshared-key */
-	                        "%s" /* endpoint */
+	                        "%s"  /* preshared-key */
+	                        "%s"  /* endpoint */
 	                        " rx %"G_GUINT64_FORMAT
 	                        " tx %"G_GUINT64_FORMAT
+	                        "%s"  /* persistent-keepalive */
 	                        "%s", /* allowed-ips */
 	                        public_key_b64,
-	                        nm_utils_memeqzero (peer->preshared_key, sizeof (peer->preshared_key))
+	                        nm_utils_memeqzero_secret (peer->preshared_key, sizeof (peer->preshared_key))
 	                          ? ""
 	                          : " preshared-key (hidden)",
 	                        s_endpoint,
 	                        peer->rx_bytes,
 	                        peer->tx_bytes,
+	                        peer->persistent_keepalive_interval > 0
+	                          ? nm_sprintf_buf (s_keepalive, " keepalive %u", (guint) peer->persistent_keepalive_interval)
+	                          : "",
 	                        peer->allowed_ips_len > 0
 	                          ? " allowed-ips"
 	                          : "");
@@ -5676,7 +5705,7 @@ nm_platform_lnk_wireguard_to_string (const NMPlatformLnkWireGuard *lnk, char *bu
 	              ? " public-key "
 	              : "",
 	            public_b64 ?: "",
-	            nm_utils_memeqzero (lnk->private_key, sizeof (lnk->private_key))
+	            nm_utils_memeqzero_secret (lnk->private_key, sizeof (lnk->private_key))
 	              ? ""
 	              : " private-key (hidden)",
 	            lnk->listen_port,
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 412ac597..37aa58fd 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -29,7 +29,6 @@
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
 #include "nm-setting-ip-tunnel.h"
-#include "nm-utils/nm-errno.h"
 
 #define NM_TYPE_PLATFORM            (nm_platform_get_type ())
 #define NM_PLATFORM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform))
@@ -749,11 +748,35 @@ typedef enum {
 } NMPlatformLinkDuplexType;
 
 typedef enum {
-	NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS               = (1LL <<  0),
-	NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL                      = (1LL <<  1),
-	NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF                         = (1LL <<  2),
+	NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS                 = (1LL <<  0),
+	NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL                        = (1LL <<  1),
+	NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF                           = (1LL <<  2),
 } NMPlatformKernelSupportFlags;
 
+typedef enum {
+	NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE                        = 0,
+	NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS               = (1LL << 0),
+	NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY             = (1LL << 1),
+	NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT             = (1LL << 2),
+	NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK                  = (1LL << 3),
+} NMPlatformWireGuardChangeFlags;
+
+typedef enum {
+	NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE                   = 0,
+	NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME              = (1LL << 0),
+	NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY      = (1LL << 1),
+	NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL = (1LL << 2),
+	NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT           = (1LL << 3),
+	NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS         = (1LL << 4),
+	NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS     = (1LL << 5),
+
+	NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT =   NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY
+	                                                 | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL
+	                                                 | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT
+	                                                 | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS,
+
+} NMPlatformWireGuardChangePeerFlags;
+
 /*****************************************************************************/
 
 struct _NMPlatformPrivate;
@@ -831,8 +854,9 @@ typedef struct {
 	                              int ifindex,
 	                              const NMPlatformLnkWireGuard *lnk_wireguard,
 	                              const struct _NMPWireGuardPeer *peers,
+	                              const NMPlatformWireGuardChangePeerFlags *peer_flags,
 	                              guint peers_len,
-	                              gboolean replace_peers);
+	                              NMPlatformWireGuardChangeFlags change_flags);
 
 	gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags, const NMPlatformLink **out_link);
 	gboolean (*link_vlan_change) (NMPlatform *self,
@@ -1394,8 +1418,9 @@ int nm_platform_link_wireguard_change (NMPlatform *self,
                                        int ifindex,
                                        const NMPlatformLnkWireGuard *lnk_wireguard,
                                        const struct _NMPWireGuardPeer *peers,
+                                       const NMPlatformWireGuardChangePeerFlags *peer_flags,
                                        guint peers_len,
-                                       gboolean replace_peers);
+                                       NMPlatformWireGuardChangeFlags change_flags);
 
 const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address);
 
diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c
index f1092fe9..f34dc83d 100644
--- a/src/platform/nmp-netns.c
+++ b/src/platform/nmp-netns.c
@@ -19,15 +19,26 @@
  */
 
 #include "nm-default.h"
+
 #include "nmp-netns.h"
 
 #include <fcntl.h>
-#include <errno.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <pthread.h>
+
+/*****************************************************************************/
 
-#include "NetworkManagerUtils.h"
+/* NOTE: NMPNetns and all code used here must be thread-safe! */
+
+/* we may not call logging functions from the main-thread alone. Hence, we
+ * require locking from nm-logging. Indicate that by setting NM_THREAD_SAFE_ON_MAIN_THREAD
+ * to zero. */
+#undef NM_THREAD_SAFE_ON_MAIN_THREAD
+#define NM_THREAD_SAFE_ON_MAIN_THREAD 0
+
+/*****************************************************************************/
 
 #define PROC_SELF_NS_MNT "/proc/self/ns/mnt"
 #define PROC_SELF_NS_NET "/proc/self/ns/net"
@@ -116,49 +127,81 @@ typedef struct {
 	int ns_types;
 } NetnsInfo;
 
-static void _stack_push (NMPNetns *netns, int ns_types);
+static void _stack_push (GArray *netns_stack,
+                         NMPNetns *netns,
+                         int ns_types);
 static NMPNetns *_netns_new (GError **error);
 
 /*****************************************************************************/
 
-static GArray *netns_stack = NULL;
+static NMPNetns *
+_netns_get (NetnsInfo *info)
+{
+	nm_assert (!info || NMP_IS_NETNS (info->netns));
+	return info ? info->netns : NULL;
+}
+
+/*****************************************************************************/
+
+static _nm_thread_local GArray *_netns_stack = NULL;
 
 static void
-_stack_ensure_init_impl (void)
+_netns_stack_clear_cb (gpointer data)
 {
-	NMPNetns *netns;
-	GError *error = NULL;
+	NetnsInfo *info = data;
 
-	nm_assert (!netns_stack);
+	nm_assert (NMP_IS_NETNS (info->netns));
+	g_object_unref (info->netns);
+}
 
-	netns_stack = g_array_new (FALSE, FALSE, sizeof (NetnsInfo));
+static GArray *
+_netns_stack_get_impl (void)
+{
+	gs_unref_object NMPNetns *netns = NULL;
+	gs_free_error GError *error = NULL;
+	pthread_key_t key;
+	GArray *s;
+
+	s = g_array_new (FALSE, FALSE, sizeof (NetnsInfo));
+	g_array_set_clear_func (s, _netns_stack_clear_cb);
+	_netns_stack = s;
 
 	/* at the bottom of the stack we must try to create a netns instance
 	 * that we never pop. It's the base to which we need to return. */
-
 	netns = _netns_new (&error);
-
 	if (!netns) {
-		/* don't know how to recover from this error. Netns are not supported. */
 		_LOGE (NULL, "failed to create initial netns: %s", error->message);
-		g_clear_error (&error);
-		return;
+		return s;
 	}
 
-	_stack_push (netns, _CLONE_NS_ALL);
+	/* we leak this instance inside the stack. */
+	_stack_push (s, netns, _CLONE_NS_ALL);
 
-	/* we leak this instance inside netns_stack. It cannot be popped. */
-	g_object_unref (netns);
+	/* finally, register a destructor function to cleanup the array. If we fail
+	 * to do so, we will leak NMPNetns instances (and their file descriptor) when the
+	 * thread exits. */
+	if (pthread_key_create (&key, (void (*) (void *)) g_array_unref) != 0)
+		_LOGE (NULL, "failure to initialize thread-local storage");
+	else if (pthread_setspecific (key, s) != 0)
+		_LOGE (NULL, "failure to set thread-local storage");
+
+	return s;
 }
-#define _stack_ensure_init() \
-	G_STMT_START { \
-		if (G_UNLIKELY (!netns_stack)) { \
-			_stack_ensure_init_impl (); \
-		} \
-	} G_STMT_END
+
+#define _netns_stack_get() \
+	({ \
+		GArray *_s = _netns_stack; \
+		\
+		if (G_UNLIKELY (!_s)) \
+			_s = _netns_stack_get_impl (); \
+		_s; \
+	})
+
+/*****************************************************************************/
 
 static NMPNetns *
-_stack_current_netns (int ns_types)
+_stack_current_netns (GArray *netns_stack,
+                      int ns_types)
 {
 	guint j;
 
@@ -179,7 +222,9 @@ _stack_current_netns (int ns_types)
 }
 
 static int
-_stack_current_ns_types (NMPNetns *netns, int ns_types)
+_stack_current_ns_types (GArray *netns_stack,
+                         NMPNetns *netns,
+                         int ns_types)
 {
 	const int ns_types_check[] = { _CLONE_NS_ALL_V };
 	guint i, j;
@@ -212,27 +257,25 @@ _stack_current_ns_types (NMPNetns *netns, int ns_types)
 }
 
 static NetnsInfo *
-_stack_peek (void)
+_stack_peek (GArray *netns_stack)
 {
-	nm_assert (netns_stack);
-
 	if (netns_stack->len > 0)
 		return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
 	return NULL;
 }
 
 static NetnsInfo *
-_stack_bottom (void)
+_stack_bottom (GArray *netns_stack)
 {
-	nm_assert (netns_stack);
-
 	if (netns_stack->len > 0)
 		return &g_array_index (netns_stack, NetnsInfo, 0);
 	return NULL;
 }
 
 static void
-_stack_push (NMPNetns *netns, int ns_types)
+_stack_push (GArray *netns_stack,
+             NMPNetns *netns,
+             int ns_types)
 {
 	NetnsInfo *info;
 
@@ -244,13 +287,15 @@ _stack_push (NMPNetns *netns, int ns_types)
 	g_array_set_size (netns_stack, netns_stack->len + 1);
 
 	info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
-	info->netns = g_object_ref (netns);
-	info->ns_types = ns_types;
-	info->count = 1;
+	*info = (NetnsInfo) {
+		.netns    = g_object_ref (netns),
+		.ns_types = ns_types,
+		.count    = 1,
+	};
 }
 
 static void
-_stack_pop (void)
+_stack_pop (GArray *netns_stack)
 {
 	NetnsInfo *info;
 
@@ -262,13 +307,11 @@ _stack_pop (void)
 	nm_assert (NMP_IS_NETNS (info->netns));
 	nm_assert (info->count == 1);
 
-	g_object_unref (info->netns);
-
 	g_array_set_size (netns_stack, netns_stack->len - 1);
 }
 
 static guint
-_stack_size (void)
+_stack_size (GArray *netns_stack)
 {
 	nm_assert (netns_stack);
 
@@ -289,7 +332,7 @@ _netns_new (GError **error)
 		errsv = errno;
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 		             "Failed opening netns: %s",
-		             g_strerror (errsv));
+		             nm_strerror_native (errsv));
 		errno = errsv;
 		return NULL;
 	}
@@ -299,7 +342,7 @@ _netns_new (GError **error)
 		errsv = errno;
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 		             "Failed opening mntns: %s",
-		             g_strerror (errsv));
+		             nm_strerror_native (errsv));
 		nm_close (fd_net);
 		errno = errsv;
 		return NULL;
@@ -332,31 +375,33 @@ _setns (NMPNetns *self, int type)
 }
 
 static gboolean
-_netns_switch_push (NMPNetns *self, int ns_types)
+_netns_switch_push (GArray *netns_stack,
+                    NMPNetns *self,
+                    int ns_types)
 {
 	int errsv;
 
 	if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
-	    && !_stack_current_ns_types (self, CLONE_NEWNET)
+	    && !_stack_current_ns_types (netns_stack, self, CLONE_NEWNET)
 	    && _setns (self, CLONE_NEWNET) != 0) {
 		errsv = errno;
-		_LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
+		_LOGE (self, "failed to switch netns: %s", nm_strerror_native (errsv));
 		return FALSE;
 	}
 	if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
-	    && !_stack_current_ns_types (self, CLONE_NEWNS)
+	    && !_stack_current_ns_types (netns_stack, self, CLONE_NEWNS)
 	    && _setns (self, CLONE_NEWNS) != 0) {
 		errsv = errno;
-		_LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
+		_LOGE (self, "failed to switch mntns: %s", nm_strerror_native (errsv));
 
 		/* try to fix the mess by returning to the previous netns. */
 		if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
-	        && !_stack_current_ns_types (self, CLONE_NEWNET)) {
-			self = _stack_current_netns (CLONE_NEWNET);
+	        && !_stack_current_ns_types (netns_stack, self, CLONE_NEWNET)) {
+			self = _stack_current_netns (netns_stack, CLONE_NEWNET);
 			if (   self
 			    && _setns (self, CLONE_NEWNET) != 0) {
 				errsv = errno;
-				_LOGE (self, "failed to restore netns: %s", g_strerror (errsv));
+				_LOGE (self, "failed to restore netns: %s", nm_strerror_native (errsv));
 			}
 		}
 		return FALSE;
@@ -366,33 +411,36 @@ _netns_switch_push (NMPNetns *self, int ns_types)
 }
 
 static gboolean
-_netns_switch_pop (NMPNetns *self, int ns_types)
+_netns_switch_pop (GArray *netns_stack,
+                   NMPNetns *self,
+                   int ns_types)
 {
 	int errsv;
 	NMPNetns *current;
 	int success = TRUE;
 
 	if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
-	    && (!self || !_stack_current_ns_types (self, CLONE_NEWNET))) {
-		current = _stack_current_netns (CLONE_NEWNET);
+	    && (   !self
+	        || !_stack_current_ns_types (netns_stack, self, CLONE_NEWNET))) {
+		current = _stack_current_netns (netns_stack, CLONE_NEWNET);
 		if (!current) {
 			g_warn_if_reached ();
 			success = FALSE;
 		} else if (_setns (current, CLONE_NEWNET) != 0) {
 			errsv = errno;
-			_LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
+			_LOGE (self, "failed to switch netns: %s", nm_strerror_native (errsv));
 			success = FALSE;
 		}
 	}
 	if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
-	    && (!self || !_stack_current_ns_types (self, CLONE_NEWNS))) {
-		current = _stack_current_netns (CLONE_NEWNS);
+	    && (!self || !_stack_current_ns_types (netns_stack, self, CLONE_NEWNS))) {
+		current = _stack_current_netns (netns_stack, CLONE_NEWNS);
 		if (!current) {
 			g_warn_if_reached ();
 			success = FALSE;
 		} else if (_setns (current, CLONE_NEWNS) != 0) {
 			errsv = errno;
-			_LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
+			_LOGE (self, "failed to switch mntns: %s", nm_strerror_native (errsv));
 			success = FALSE;
 		}
 	}
@@ -423,32 +471,31 @@ nmp_netns_get_fd_mnt (NMPNetns *self)
 static gboolean
 _nmp_netns_push_type (NMPNetns *self, int ns_types)
 {
+	GArray *netns_stack = _netns_stack_get ();
 	NetnsInfo *info;
 	char sbuf[100];
 
-	_stack_ensure_init ();
-
-	info = _stack_peek ();
+	info = _stack_peek (netns_stack);
 	g_return_val_if_fail (info, FALSE);
 
 	if (info->netns == self && info->ns_types == ns_types) {
 		info->count++;
 		_LOGt (self, "push#%u* %s (increase count to %d)",
-		       _stack_size () - 1,
+		       _stack_size (netns_stack) - 1,
 		       _ns_types_to_str (ns_types, ns_types, sbuf), info->count);
 		return TRUE;
 	}
 
 	_LOGD (self, "push#%u %s",
-	       _stack_size (),
+	       _stack_size (netns_stack),
 	       _ns_types_to_str (ns_types,
-	                         _stack_current_ns_types (self, ns_types),
+	                         _stack_current_ns_types (netns_stack, self, ns_types),
 	                         sbuf));
 
-	if (!_netns_switch_push (self, ns_types))
+	if (!_netns_switch_push (netns_stack, self, ns_types))
 		return FALSE;
 
-	_stack_push (self, ns_types);
+	_stack_push (netns_stack, self, ns_types);
 	return TRUE;
 }
 
@@ -472,14 +519,13 @@ nmp_netns_push_type (NMPNetns *self, int ns_types)
 NMPNetns *
 nmp_netns_new (void)
 {
+	GArray *netns_stack = _netns_stack_get ();
 	NMPNetns *self;
 	int errsv;
 	GError *error = NULL;
 	unsigned long mountflags = 0;
 
-	_stack_ensure_init ();
-
-	if (!_stack_peek ()) {
+	if (!_stack_peek (netns_stack)) {
 		/* there are no netns instances. We cannot create a new one
 		 * (because after unshare we couldn't return to the original one). */
 		errno = ENOTSUP;
@@ -488,19 +534,19 @@ nmp_netns_new (void)
 
 	if (unshare (_CLONE_NS_ALL) != 0) {
 		errsv = errno;
-		_LOGE (NULL, "failed to create new net and mnt namespace: %s", g_strerror (errsv));
+		_LOGE (NULL, "failed to create new net and mnt namespace: %s", nm_strerror_native (errsv));
 		return NULL;
 	}
 
 	if (mount ("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) {
 		errsv = errno;
-		_LOGE (NULL, "failed mount --make-rslave: %s", g_strerror (errsv));
+		_LOGE (NULL, "failed mount --make-rslave: %s", nm_strerror_native (errsv));
 		goto err_out;
 	}
 
 	if (umount2 ("/sys", MNT_DETACH) != 0) {
 		errsv = errno;
-		_LOGE (NULL, "failed umount /sys: %s", g_strerror (errsv));
+		_LOGE (NULL, "failed umount /sys: %s", nm_strerror_native (errsv));
 		goto err_out;
 	}
 
@@ -509,7 +555,7 @@ nmp_netns_new (void)
 
 	if (mount ("sysfs", "/sys", "sysfs", mountflags, NULL) != 0) {
 		errsv = errno;
-		_LOGE (NULL, "failed mount /sys: %s", g_strerror (errsv));
+		_LOGE (NULL, "failed mount /sys: %s", nm_strerror_native (errsv));
 		goto err_out;
 	}
 
@@ -521,11 +567,11 @@ nmp_netns_new (void)
 		goto err_out;
 	}
 
-	_stack_push (self, _CLONE_NS_ALL);
+	_stack_push (netns_stack, self, _CLONE_NS_ALL);
 
 	return self;
 err_out:
-	_netns_switch_pop (NULL, _CLONE_NS_ALL);
+	_netns_switch_pop (netns_stack, NULL, _CLONE_NS_ALL);
 	errno = errsv;
 	return NULL;
 }
@@ -533,14 +579,13 @@ err_out:
 gboolean
 nmp_netns_pop (NMPNetns *self)
 {
+	GArray *netns_stack = _netns_stack_get ();
 	NetnsInfo *info;
 	int ns_types;
 
 	g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
 
-	_stack_ensure_init ();
-
-	info = _stack_peek ();
+	info = _stack_peek (netns_stack);
 
 	g_return_val_if_fail (info, FALSE);
 	g_return_val_if_fail (info->netns == self, FALSE);
@@ -548,52 +593,42 @@ nmp_netns_pop (NMPNetns *self)
 	if (info->count > 1) {
 		info->count--;
 		_LOGt (self, "pop#%u* (decrease count to %d)",
-		       _stack_size () - 1, info->count);
+		       _stack_size (netns_stack) - 1, info->count);
 		return TRUE;
 	}
 	g_return_val_if_fail (info->count == 1, FALSE);
 
 	/* cannot pop the original netns. */
-	g_return_val_if_fail (_stack_size () > 1, FALSE);
+	g_return_val_if_fail (_stack_size (netns_stack) > 1, FALSE);
 
-	_LOGD (self, "pop#%u", _stack_size () - 1);
+	_LOGD (self, "pop#%u", _stack_size (netns_stack) - 1);
 
 	ns_types = info->ns_types;
 
-	_stack_pop ();
+	_stack_pop (netns_stack);
 
-	return _netns_switch_pop (self, ns_types);
+	return _netns_switch_pop (netns_stack, self, ns_types);
 }
 
 NMPNetns *
 nmp_netns_get_current (void)
 {
-	NetnsInfo *info;
-
-	_stack_ensure_init ();
-
-	info = _stack_peek ();
-	return info ? info->netns : NULL;
+	return _netns_get (_stack_peek (_netns_stack_get ()));
 }
 
 NMPNetns *
 nmp_netns_get_initial (void)
 {
-	NetnsInfo *info;
-
-	_stack_ensure_init ();
-
-	info = _stack_bottom ();
-	return info ? info->netns : NULL;
+	return _netns_get (_stack_bottom (_netns_stack_get ()));
 }
 
 gboolean
 nmp_netns_is_initial (void)
 {
-	if (G_UNLIKELY (!netns_stack))
-		return TRUE;
+	GArray *netns_stack = _netns_stack_get ();
 
-	return nmp_netns_get_current () == nmp_netns_get_initial ();
+	return (   _netns_get (_stack_peek (netns_stack))
+	        == _netns_get (_stack_bottom (netns_stack)));
 }
 
 /*****************************************************************************/
@@ -618,7 +653,7 @@ nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd)
 		errsv = errno;
 		if (errsv != EEXIST) {
 			_LOGE (self, "bind: failed to create directory %s: %s",
-			       dirname, g_strerror (errsv));
+			       dirname, nm_strerror_native (errsv));
 			return FALSE;
 		}
 	}
@@ -626,7 +661,7 @@ nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd)
 	if ((fd = creat (filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
 		errsv = errno;
 		_LOGE (self, "bind: failed to create %s: %s",
-		       filename, g_strerror (errsv));
+		       filename, nm_strerror_native (errsv));
 		return FALSE;
 	}
 	nm_close (fd);
@@ -634,7 +669,7 @@ nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd)
 	if (mount (PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) {
 		errsv = errno;
 		_LOGE (self, "bind: failed to mount %s to %s: %s",
-		       PROC_SELF_NS_NET, filename, g_strerror (errsv));
+		       PROC_SELF_NS_NET, filename, nm_strerror_native (errsv));
 		unlink (filename);
 		return FALSE;
 	}
@@ -642,7 +677,7 @@ nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd)
 	if (out_fd) {
 		if ((fd = open (filename, O_RDONLY | O_CLOEXEC)) == -1) {
 			errsv = errno;
-			_LOGE (self, "bind: failed to open %s: %s", filename, g_strerror (errsv));
+			_LOGE (self, "bind: failed to open %s: %s", filename, nm_strerror_native (errsv));
 			umount2 (filename, MNT_DETACH);
 			unlink (filename);
 			return FALSE;
@@ -663,12 +698,12 @@ nmp_netns_bind_to_path_destroy (NMPNetns *self, const char *filename)
 
 	if (umount2 (filename, MNT_DETACH) != 0) {
 		errsv = errno;
-		_LOGE (self, "bind: failed to unmount2 %s: %s", filename, g_strerror (errsv));
+		_LOGE (self, "bind: failed to unmount2 %s: %s", filename, nm_strerror_native (errsv));
 		return FALSE;
 	}
 	if (unlink (filename) != 0) {
 		errsv = errno;
-		_LOGE (self, "bind: failed to unlink %s: %s", filename, g_strerror (errsv));
+		_LOGE (self, "bind: failed to unlink %s: %s", filename, nm_strerror_native (errsv));
 		return FALSE;
 	}
 	return TRUE;
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 80e65b49..6ec6fea1 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -1545,13 +1545,13 @@ _vt_cmd_plobj_id_hash_update (tfilter, NMPlatformTfilter, {
 	                     obj->handle);
 })
 
-static inline void
+static void
 _vt_cmd_plobj_hash_update_ip4_route (const NMPlatformObject *obj, NMHashState *h)
 {
 	return nm_platform_ip4_route_hash_update ((const NMPlatformIP4Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h);
 }
 
-static inline void
+static void
 _vt_cmd_plobj_hash_update_ip6_route (const NMPlatformObject *obj, NMHashState *h)
 {
 	return nm_platform_ip6_route_hash_update ((const NMPlatformIP6Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h);
@@ -2519,10 +2519,10 @@ nmp_cache_remove_netlink (NMPCache *cache,
  *    afterwards. Hence, during a dump, every update should move the object to the
  *    end of the list, to obtain the correct order. That means, to use NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE,
  *    instead of NM_DEDUP_MULTI_IDX_MODE_APPEND.
- * @out_obj_old: (allow-none): (out): return the object with same ID as @obj_hand_over,
+ * @out_obj_old: (allow-none) (out): return the object with same ID as @obj_hand_over,
  *    that was in the cache before update. If an object is returned, the caller must
  *    unref it afterwards.
- * @out_obj_new: (allow-none): (out): return the object from the cache after update.
+ * @out_obj_new: (allow-none) (out): return the object from the cache after update.
  *    The caller must unref this object.
  *
  * Returns: how the cache changed.
@@ -2920,15 +2920,15 @@ nmp_cache_update_link_master_connected (NMPCache *cache,
 /*****************************************************************************/
 
 void
-nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type)
+nmp_cache_dirty_set_all (NMPCache *cache,
+                         const NMPLookup *lookup)
 {
-	NMPObject obj_needle;
-
 	nm_assert (cache);
+	nm_assert (lookup);
 
 	nm_dedup_multi_index_dirty_set_head (cache->multi_idx,
-	                                     _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE),
-	                                     _nmp_object_stackinit_from_type (&obj_needle, obj_type));
+	                                     _idx_type_get (cache, lookup->cache_id_type),
+	                                     &lookup->selector_obj);
 }
 
 /*****************************************************************************/
@@ -2977,7 +2977,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
 		.sizeof_data                        = sizeof (NMPObjectLink),
 		.sizeof_public                      = sizeof (NMPlatformLink),
 		.obj_type_name                      = "link",
-		.addr_family                        = AF_UNSPEC,
 		.rtm_gettype                        = RTM_GETLINK,
 		.signal_type_id                     = NM_PLATFORM_SIGNAL_ID_LINK,
 		.signal_type                        = NM_PLATFORM_SIGNAL_LINK_CHANGED,
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index 4649441b..525e1440 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -31,6 +31,18 @@ struct udev_device;
 
 /*****************************************************************************/
 
+/* "struct __kernel_timespec" uses "long long", but we use gint64. In practice,
+ * these are the same types. */
+G_STATIC_ASSERT (sizeof (long long) == sizeof (gint64));
+
+typedef struct {
+	/* like "struct __kernel_timespec". */
+	gint64 tv_sec;
+	gint64 tv_nsec;
+} NMPTimespec64;
+
+/*****************************************************************************/
+
 typedef union {
 	struct sockaddr     sa;
 	struct sockaddr_in  in;
@@ -72,7 +84,8 @@ typedef struct {
 typedef struct _NMPWireGuardPeer {
 	NMSockAddrUnion endpoint;
 
-	struct timespec last_handshake_time;
+	NMPTimespec64 last_handshake_time;
+
 	guint64 rx_bytes;
 	guint64 tx_bytes;
 
@@ -771,7 +784,8 @@ NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache,
                                                         const NMPObject **out_obj_old,
                                                         const NMPObject **out_obj_new);
 
-void nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type);
+void nmp_cache_dirty_set_all (NMPCache *cache,
+                              const NMPLookup *lookup);
 
 NMPCache *nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev);
 void nmp_cache_free (NMPCache *cache);
diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build
index 3704e2ac..8086a46c 100644
--- a/src/platform/tests/meson.build
+++ b/src/platform/tests/meson.build
@@ -1,14 +1,14 @@
 test_units = [
-  ['test-link-fake',     'test-link.c',       test_nm_dep_fake,  30],
-  ['test-link-linux',    'test-link.c',       test_nm_dep_linux, 180],
-  ['test-address-fake',  'test-address.c',    test_nm_dep_fake,  30],
-  ['test-address-linux', 'test-address.c',    test_nm_dep_linux, 30],
-  ['test-general',       'test-general.c',    test_nm_dep,       30],
-  ['test-nmp-object',    'test-nmp-object.c', test_nm_dep,       30],
-  ['test-route-fake',    'test-route.c',      test_nm_dep_fake,  30],
-  ['test-route-linux',   'test-route.c',      test_nm_dep_linux, 30],
-  ['test-cleanup-fake',  'test-cleanup.c',    test_nm_dep_fake,  30],
-  ['test-cleanup-linux', 'test-cleanup.c',    test_nm_dep_linux, 30],
+  ['test-link-fake',     'test-link.c',       test_nm_dep_fake,  default_test_timeout],
+  ['test-link-linux',    'test-link.c',       test_nm_dep_linux, 900],
+  ['test-address-fake',  'test-address.c',    test_nm_dep_fake,  default_test_timeout],
+  ['test-address-linux', 'test-address.c',    test_nm_dep_linux, default_test_timeout],
+  ['test-general',       'test-general.c',    test_nm_dep,       default_test_timeout],
+  ['test-nmp-object',    'test-nmp-object.c', test_nm_dep,       default_test_timeout],
+  ['test-route-fake',    'test-route.c',      test_nm_dep_fake,  default_test_timeout],
+  ['test-route-linux',   'test-route.c',      test_nm_dep_linux, default_test_timeout],
+  ['test-cleanup-fake',  'test-cleanup.c',    test_nm_dep_fake,  default_test_timeout],
+  ['test-cleanup-linux', 'test-cleanup.c',    test_nm_dep_linux, default_test_timeout],
 ]
 
 foreach test_unit: test_units
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index 8e29b5ce..c510710b 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -1840,7 +1840,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 	if (e != 0) {
 		errsv = errno;
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		             "pipe() failed with %d (%s)", errsv, strerror (errsv));
+		             "pipe() failed with %d (%s)", errsv, nm_strerror_native (errsv));
 		return FALSE;
 	}
 
@@ -1848,7 +1848,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 	if (e != 0) {
 		errsv = errno;
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		             "pipe() failed with %d (%s)", errsv, strerror (errsv));
+		             "pipe() failed with %d (%s)", errsv, nm_strerror_native (errsv));
 		nm_close (pipefd_c2p[0]);
 		nm_close (pipefd_c2p[1]);
 		return FALSE;
@@ -1858,7 +1858,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 	if (pid < 0) {
 		errsv = errno;
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		             "fork() failed with %d (%s)", errsv, strerror (errsv));
+		             "fork() failed with %d (%s)", errsv, nm_strerror_native (errsv));
 		nm_close (pipefd_c2p[0]);
 		nm_close (pipefd_c2p[1]);
 		nm_close (pipefd_p2c[0]);
@@ -1919,7 +1919,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 			             "child process failed for unknown reason");
 		} else {
 			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			             "child process signaled failure %d (%s)", errsv, strerror (errsv));
+			             "child process signaled failure %d (%s)", errsv, nm_strerror_native (errsv));
 		}
 		nm_close (pipefd_p2c[1]);
 		kill (pid, SIGKILL);
@@ -2074,14 +2074,14 @@ main (int argc, char **argv)
 
 		if (unshare (CLONE_NEWNET | CLONE_NEWNS) != 0) {
 			errsv = errno;
-			g_error ("unshare(CLONE_NEWNET|CLONE_NEWNS) failed with %s (%d)", strerror (errsv), errsv);
+			g_error ("unshare(CLONE_NEWNET|CLONE_NEWNS) failed with %s (%d)", nm_strerror_native (errsv), errsv);
 		}
 
 		/* We need a read-only /sys so that the platform knows there's no udev. */
 		mount (NULL, "/sys", "sysfs", MS_SLAVE, NULL);
 		if (mount ("sys", "/sys", "sysfs", MS_RDONLY, NULL) != 0) {
 			errsv = errno;
-			g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv);
+			g_error ("mount(\"/sys\") failed with %s (%d)", nm_strerror_native (errsv), errsv);
 		}
 	}
 
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index 048fd9dd..aa1f5460 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -19,7 +19,6 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <syslog.h>
-#include <string.h>
 #include <arpa/inet.h>
 #include <linux/if.h>
 #include <linux/if_link.h>
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index bfd33058..71324301 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -860,9 +860,9 @@ _test_wireguard_change (NMPlatform *platform,
 			if ((i % 2) == 1) {
 				endpoint = (NMSockAddrUnion) {
 					.in = {
-						.sin_family = AF_INET,
-						.sin_addr   = nmtst_inet4_from_string (nm_sprintf_buf (s_addr, "192.168.7.%d", i)),
-						.sin_port   = htons (14000 + i),
+						.sin_family      = AF_INET,
+						.sin_addr.s_addr = nmtst_inet4_from_string (nm_sprintf_buf (s_addr, "192.168.7.%d", i)),
+						.sin_port        = htons (14000 + i),
 					},
 				};
 			} else {
@@ -912,8 +912,12 @@ _test_wireguard_change (NMPlatform *platform,
 	                                       ifindex,
 	                                       &lnk_wireguard,
 	                                       (const NMPWireGuardPeer *) peers->data,
+	                                       NULL,
 	                                       peers->len,
-	                                       TRUE);
+	                                         NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY
+	                                       | NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT
+	                                       | NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK
+	                                       | NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS);
 	g_assert (NMTST_NM_ERR_SUCCESS (r));
 }
 
@@ -2393,7 +2397,7 @@ _test_netns_check_skip (void)
 			support_errsv = errno;
 	}
 	if (!support) {
-		_LOGD ("setns() failed with \"%s\". This indicates missing support (valgrind?)", g_strerror (support_errsv));
+		_LOGD ("setns() failed with \"%s\". This indicates missing support (valgrind?)", nm_strerror_native (support_errsv));
 		g_test_skip ("No netns support (setns failed)");
 		return TRUE;
 	}
@@ -2983,6 +2987,69 @@ test_sysctl_netns_switch (void)
 
 /*****************************************************************************/
 
+static gpointer
+_test_netns_mt_thread (gpointer data)
+{
+	NMPNetns *netns1 = data;
+	gs_unref_object NMPNetns *netns2 = NULL;
+	NMPNetns *netns_bottom;
+	NMPNetns *initial;
+
+	netns_bottom = nmp_netns_get_initial ();
+	g_assert (netns_bottom);
+
+	/* I don't know why, but we need to create a new netns here at least once.
+	 * Otherwise, setns(, CLONE_NEWNS) below fails with EINVAL (???).
+	 *
+	 * Something is not right here, but what?  */
+	netns2 = nmp_netns_new ();
+	nmp_netns_pop (netns2);
+	g_clear_object (&netns2);
+
+	nmp_netns_push (netns1);
+	nmp_netns_push_type (netns_bottom, CLONE_NEWNET);
+	nmp_netns_push_type (netns_bottom, CLONE_NEWNS);
+	nmp_netns_push_type (netns1, CLONE_NEWNS);
+	nmp_netns_pop (netns1);
+	nmp_netns_pop (netns_bottom);
+	nmp_netns_pop (netns_bottom);
+	nmp_netns_pop (netns1);
+
+	initial = nmp_netns_get_initial ();
+	g_assert (NMP_IS_NETNS (initial));
+	return g_object_ref (initial);
+}
+
+static void
+test_netns_mt (void)
+{
+	gs_unref_object NMPNetns *netns1 = NULL;
+	NMPNetns *initial_from_other_thread;
+	GThread *th;
+
+	if (_test_netns_check_skip ())
+		return;
+
+	netns1 = nmp_netns_new ();
+	g_assert (NMP_NETNS (netns1));
+	nmp_netns_pop (netns1);
+
+	th = g_thread_new ("nm-test-netns-mt", _test_netns_mt_thread, netns1);
+	initial_from_other_thread = g_thread_join (th);
+	g_assert (NMP_IS_NETNS (initial_from_other_thread));
+
+	if (nmtst_get_rand_bool ()) {
+		nmp_netns_push (initial_from_other_thread);
+		nmp_netns_pop (initial_from_other_thread);
+	}
+
+	g_object_add_weak_pointer (G_OBJECT (initial_from_other_thread), (gpointer *) &initial_from_other_thread);
+	g_object_unref (initial_from_other_thread);
+	g_assert (initial_from_other_thread == NULL);
+}
+
+/*****************************************************************************/
+
 static void
 ethtool_features_dump (const NMEthtoolFeatureStates *features)
 {
@@ -3137,6 +3204,8 @@ _nmtstp_setup_tests (void)
 		g_test_add_vtable ("/general/netns/push", 0, NULL, _test_netns_setup, test_netns_push, _test_netns_teardown);
 		g_test_add_vtable ("/general/netns/bind-to-path", 0, NULL, _test_netns_setup, test_netns_bind_to_path, _test_netns_teardown);
 
+		g_test_add_func ("/general/netns/mt", test_netns_mt);
+
 		g_test_add_func ("/general/sysctl/rename", test_sysctl_rename);
 		g_test_add_func ("/general/sysctl/netns-switch", test_sysctl_netns_switch);
 
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index 4c0f686f..2619ec52 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -767,7 +767,7 @@ test_ip (gconstpointer test_data)
 	const int EX_ = -1;
 	struct {
 		int ifindex;
-	} iface_data[10] = { 0 };
+	} iface_data[10] = { { 0 }, };
 	int order_idx[G_N_ELEMENTS (iface_data)] = { 0 };
 	guint order_len;
 	guint try;
diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c
index 0df3d2d8..4f7ede97 100644
--- a/src/platform/wifi/nm-wifi-utils-nl80211.c
+++ b/src/platform/wifi/nm-wifi-utils-nl80211.c
@@ -24,15 +24,12 @@
 
 #include "nm-wifi-utils-nl80211.h"
 
-#include <errno.h>
-#include <string.h>
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 #include <unistd.h>
 #include <linux/nl80211.h>
 #include <linux/if.h>
 
-#include "nm-utils/nm-errno.h"
 #include "platform/nm-netlink.h"
 #include "nm-wifi-utils-private.h"
 #include "platform/nm-platform.h"
@@ -184,8 +181,10 @@ nl80211_iface_info_handler (struct nl_msg *msg, void *arg)
 	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
 
-	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
-	               genlmsg_attrlen (gnlh, 0), NULL) < 0)
+	if (nla_parse_arr (tb,
+	                   genlmsg_attrdata (gnlh, 0),
+	                   genlmsg_attrlen (gnlh, 0),
+	                   NULL) < 0)
 		return NL_SKIP;
 
 	if (!tb[NL80211_ATTR_IFTYPE])
@@ -279,16 +278,18 @@ nl80211_get_wake_on_wlan_handler (struct nl_msg *msg, void *arg)
 	struct nlattr *trig[NUM_NL80211_WOWLAN_TRIG];
 	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
 
-	nla_parse (attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-	           genlmsg_attrlen(gnlh, 0), NULL);
+	nla_parse_arr (attrs,
+	               genlmsg_attrdata(gnlh, 0),
+	               genlmsg_attrlen(gnlh, 0),
+	               NULL);
 
 	if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
 		return NL_SKIP;
 
-	nla_parse (trig, MAX_NL80211_WOWLAN_TRIG,
-	           nla_data (attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
-	           nla_len (attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
-	           NULL);
+	nla_parse_arr (trig,
+	               nla_data (attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
+	               nla_len (attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
+	               NULL);
 
 	*wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE;
 	if (trig[NL80211_WOWLAN_TRIG_ANY])
@@ -415,33 +416,35 @@ find_ssid (guint8 *ies, guint32 ies_len,
 static int
 nl80211_bss_dump_handler (struct nl_msg *msg, void *arg)
 {
+	static const struct nla_policy bss_policy[] = {
+		[NL80211_BSS_TSF]                  = { .type = NLA_U64 },
+		[NL80211_BSS_FREQUENCY]            = { .type = NLA_U32 },
+		[NL80211_BSS_BSSID]                = { .minlen = ETH_ALEN },
+		[NL80211_BSS_BEACON_INTERVAL]      = { .type = NLA_U16 },
+		[NL80211_BSS_CAPABILITY]           = { .type = NLA_U16 },
+		[NL80211_BSS_INFORMATION_ELEMENTS] = { },
+		[NL80211_BSS_SIGNAL_MBM]           = { .type = NLA_U32 },
+		[NL80211_BSS_SIGNAL_UNSPEC]        = { .type = NLA_U8 },
+		[NL80211_BSS_STATUS]               = { .type = NLA_U32 },
+	};
 	struct nl80211_bss_info *info = arg;
 	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct nlattr *bss[NL80211_BSS_MAX + 1];
-	static const struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
-		[NL80211_BSS_TSF] = { .type = NLA_U64 },
-		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
-		[NL80211_BSS_BSSID] = { },
-		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
-		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
-		[NL80211_BSS_INFORMATION_ELEMENTS] = { },
-		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
-		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
-		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
-	};
+	struct nlattr *bss[G_N_ELEMENTS (bss_policy)];
 	guint32 status;
 
-	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
-	               genlmsg_attrlen (gnlh, 0), NULL) < 0)
+	if (nla_parse_arr (tb,
+	                   genlmsg_attrdata (gnlh, 0),
+	                   genlmsg_attrlen (gnlh, 0),
+	                   NULL) < 0)
 		return NL_SKIP;
 
 	if (tb[NL80211_ATTR_BSS] == NULL)
 		return NL_SKIP;
 
-	if (nla_parse_nested (bss, NL80211_BSS_MAX,
-	                      tb[NL80211_ATTR_BSS],
-	                      bss_policy))
+	if (nla_parse_nested_arr (bss,
+	                          tb[NL80211_ATTR_BSS],
+	                          bss_policy))
 		return NL_SKIP;
 
 	if (bss[NL80211_BSS_STATUS] == NULL)
@@ -461,21 +464,22 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg)
 		info->freq = nla_get_u32 (bss[NL80211_BSS_FREQUENCY]);
 
 	if (bss[NL80211_BSS_SIGNAL_UNSPEC])
-		info->beacon_signal =
-			nla_get_u8 (bss[NL80211_BSS_SIGNAL_UNSPEC]);
+		info->beacon_signal = nla_get_u8 (bss[NL80211_BSS_SIGNAL_UNSPEC]);
 
 	if (bss[NL80211_BSS_SIGNAL_MBM])
-		info->beacon_signal =
-			nl80211_xbm_to_percent (nla_get_u32 (bss[NL80211_BSS_SIGNAL_MBM]), 100);
+		info->beacon_signal = nl80211_xbm_to_percent (nla_get_u32 (bss[NL80211_BSS_SIGNAL_MBM]), 100);
 
 	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
 		guint8 *ssid;
 		guint32 ssid_len;
 
 		find_ssid (nla_data (bss[NL80211_BSS_INFORMATION_ELEMENTS]),
-			  nla_len (bss[NL80211_BSS_INFORMATION_ELEMENTS]),
-			  &ssid, &ssid_len);
-		if (ssid && ssid_len && ssid_len <= sizeof (info->ssid)) {
+		           nla_len (bss[NL80211_BSS_INFORMATION_ELEMENTS]),
+		           &ssid,
+		           &ssid_len);
+		if (   ssid
+		    && ssid_len
+		    && ssid_len <= sizeof (info->ssid)) {
 			memcpy (info->ssid, ssid, ssid_len);
 			info->ssid_len = ssid_len;
 		}
@@ -550,49 +554,50 @@ struct nl80211_station_info {
 static int
 nl80211_station_handler (struct nl_msg *msg, void *arg)
 {
-	struct nl80211_station_info *info = arg;
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
-	struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
-	struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
-	static const struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+	static const struct nla_policy stats_policy[] = {
 		[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
-		[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
-		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
-		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
-		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
-		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
-		[NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
-		[NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
-		[NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
-		[NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_RX_BYTES]      = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_BYTES]      = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_PACKETS]    = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_PACKETS]    = { .type = NLA_U32 },
+		[NL80211_STA_INFO_SIGNAL]        = { .type = NLA_U8 },
+		[NL80211_STA_INFO_TX_BITRATE]    = { .type = NLA_NESTED },
+		[NL80211_STA_INFO_LLID]          = { .type = NLA_U16 },
+		[NL80211_STA_INFO_PLID]          = { .type = NLA_U16 },
+		[NL80211_STA_INFO_PLINK_STATE]   = { .type = NLA_U8 },
 	};
-
-	static const struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
-		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
-		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+	static const struct nla_policy rate_policy[] = {
+		[NL80211_RATE_INFO_BITRATE]      = { .type = NLA_U16 },
+		[NL80211_RATE_INFO_MCS]          = { .type = NLA_U8 },
 		[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
-		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+		[NL80211_RATE_INFO_SHORT_GI]     = { .type = NLA_FLAG },
 	};
+	struct nlattr *rinfo[G_N_ELEMENTS (rate_policy)];
+	struct nlattr *sinfo[G_N_ELEMENTS (stats_policy)];
+	struct nl80211_station_info *info = arg;
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
 
-	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
-	               genlmsg_attrlen (gnlh, 0), NULL) < 0)
+	if (nla_parse_arr (tb,
+	                   genlmsg_attrdata (gnlh, 0),
+	                   genlmsg_attrlen (gnlh, 0),
+	                   NULL) < 0)
 		return NL_SKIP;
 
 	if (tb[NL80211_ATTR_STA_INFO] == NULL)
 		return NL_SKIP;
 
-	if (nla_parse_nested (sinfo, NL80211_STA_INFO_MAX,
-	                      tb[NL80211_ATTR_STA_INFO],
-	                      stats_policy))
+	if (nla_parse_nested_arr (sinfo,
+	                          tb[NL80211_ATTR_STA_INFO],
+	                          stats_policy))
 		return NL_SKIP;
 
 	if (sinfo[NL80211_STA_INFO_TX_BITRATE] == NULL)
 		return NL_SKIP;
 
-	if (nla_parse_nested (rinfo, NL80211_RATE_INFO_MAX,
-	                      sinfo[NL80211_STA_INFO_TX_BITRATE],
-	                      rate_policy))
+	if (nla_parse_nested_arr (rinfo,
+	                          sinfo[NL80211_STA_INFO_TX_BITRATE],
+	                          rate_policy))
 		return NL_SKIP;
 
 	if (rinfo[NL80211_RATE_INFO_BITRATE] == NULL)
@@ -717,37 +722,40 @@ struct nl80211_device_info {
 
 static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 {
+	static const struct nla_policy freq_policy[] = {
+		[NL80211_FREQUENCY_ATTR_FREQ]         = { .type = NLA_U32 },
+		[NL80211_FREQUENCY_ATTR_DISABLED]     = { .type = NLA_FLAG },
+#ifdef NL80211_FREQUENCY_ATTR_NO_IR
+		[NL80211_FREQUENCY_ATTR_NO_IR]        = { .type = NLA_FLAG },
+#else
+		[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_IBSS]      = { .type = NLA_FLAG },
+#endif
+		[NL80211_FREQUENCY_ATTR_RADAR]        = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+	};
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
 	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
 	struct nl80211_device_info *info = arg;
 	NMWifiUtilsNl80211 *self = info->self;
 	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
-	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+	struct nlattr *tb_freq[G_N_ELEMENTS (freq_policy)];
 	struct nlattr *nl_band;
 	struct nlattr *nl_freq;
 	int rem_freq;
 	int rem_band;
 	int freq_idx;
-	static const struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
-		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
-		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
-#ifdef NL80211_FREQUENCY_ATTR_NO_IR
-		[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
-#else
-		[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
-		[NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
-#endif
-		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
-		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
-	};
+
 #ifdef NL80211_FREQUENCY_ATTR_NO_IR
-	G_STATIC_ASSERT (NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR && NL80211_FREQUENCY_ATTR_NO_IBSS == NL80211_FREQUENCY_ATTR_NO_IR);
+	G_STATIC_ASSERT_EXPR (NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR && NL80211_FREQUENCY_ATTR_NO_IBSS == NL80211_FREQUENCY_ATTR_NO_IR);
 #else
-	G_STATIC_ASSERT (NL80211_FREQUENCY_ATTR_PASSIVE_SCAN != NL80211_FREQUENCY_ATTR_NO_IBSS);
+	G_STATIC_ASSERT_EXPR (NL80211_FREQUENCY_ATTR_PASSIVE_SCAN != NL80211_FREQUENCY_ATTR_NO_IBSS);
 #endif
 
-	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
-	               genlmsg_attrlen (gnlh, 0), NULL) < 0)
+	if (nla_parse_arr (tb,
+	                   genlmsg_attrdata (gnlh, 0),
+	                   genlmsg_attrlen (gnlh, 0),
+	                   NULL) < 0)
 		return NL_SKIP;
 
 	if (   tb[NL80211_ATTR_WIPHY] == NULL
@@ -793,14 +801,17 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 	info->num_freqs = 0;
 
 	nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-		if (nla_parse_nested (tb_band, NL80211_BAND_ATTR_MAX, nl_band,
-		                      NULL) < 0)
+		if (nla_parse_nested_arr (tb_band,
+		                          nl_band,
+		                          NULL) < 0)
 			return NL_SKIP;
 
-		nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS],
+		nla_for_each_nested (nl_freq,
+		                     tb_band[NL80211_BAND_ATTR_FREQS],
 		                     rem_freq) {
-			if (nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX,
-			                      nl_freq, freq_policy) < 0)
+			if (nla_parse_nested_arr (tb_freq,
+			                          nl_freq,
+			                          freq_policy) < 0)
 				continue;
 
 			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
@@ -815,21 +826,22 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 
 	freq_idx = 0;
 	nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-		if (nla_parse_nested (tb_band, NL80211_BAND_ATTR_MAX, nl_band,
-		                      NULL) < 0)
+		if (nla_parse_nested_arr (tb_band,
+		                          nl_band,
+		                          NULL) < 0)
 			return NL_SKIP;
 
 		nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS],
 		                    rem_freq) {
-			if (nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX,
-			                      nl_freq, freq_policy) < 0)
+			if (nla_parse_nested_arr (tb_freq,
+			                          nl_freq,
+			                          freq_policy) < 0)
 				continue;
 
 			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
 				continue;
 
-			info->freqs[freq_idx] =
-				nla_get_u32 (tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+			info->freqs[freq_idx] = nla_get_u32 (tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
 
 			info->caps |= NM_WIFI_DEVICE_CAP_FREQ_VALID;
 
@@ -844,11 +856,10 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 
 	/* Read security/encryption support */
 	if (tb[NL80211_ATTR_CIPHER_SUITES]) {
-		int num;
-		int i;
-		__u32 *ciphers = nla_data (tb[NL80211_ATTR_CIPHER_SUITES]);
+		guint32 *ciphers = nla_data (tb[NL80211_ATTR_CIPHER_SUITES]);
+		guint i, num;
 
-		num = nla_len (tb[NL80211_ATTR_CIPHER_SUITES]) / sizeof (__u32);
+		num = nla_len (tb[NL80211_ATTR_CIPHER_SUITES]) / sizeof (guint32);
 		for (i = 0; i < num; i++) {
 			switch (ciphers[i]) {
 			case WLAN_CIPHER_SUITE_WEP40:
diff --git a/src/platform/wifi/nm-wifi-utils-wext.c b/src/platform/wifi/nm-wifi-utils-wext.c
index 597f3152..3aa1720a 100644
--- a/src/platform/wifi/nm-wifi-utils-wext.c
+++ b/src/platform/wifi/nm-wifi-utils-wext.c
@@ -23,8 +23,6 @@
 
 #include "nm-wifi-utils-wext.h"
 
-#include <errno.h>
-#include <string.h>
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 #include <unistd.h>
@@ -117,7 +115,7 @@ get_ifname (int ifindex, char *buffer, const char *op)
 		errsv = errno;
 		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
 		       "error getting interface name for ifindex %d, operation '%s': %s (%d)",
-		       ifindex, op, g_strerror (errsv), errsv);
+		       ifindex, op, nm_strerror_native (errsv), errsv);
 		return FALSE;
 	}
 
@@ -129,15 +127,17 @@ wifi_wext_get_mode_ifname (NMWifiUtils *data, const char *ifname)
 {
 	NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data;
 	struct iwreq wrq;
+	int errsv;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
 	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 
 	if (ioctl (wext->fd, SIOCGIWMODE, &wrq) < 0) {
-		if (errno != ENODEV) {
+		errsv = errno;
+		if (errsv != ENODEV) {
 			_LOGW (LOGD_PLATFORM | LOGD_WIFI,
 			       "(%s): error %d getting card mode",
-			       ifname, errno);
+			       ifname, errsv);
 		}
 		return NM_802_11_MODE_UNKNOWN;
 	}
@@ -253,7 +253,7 @@ wifi_wext_get_freq (NMWifiUtils *data)
 	if (ioctl (wext->fd, SIOCGIWFREQ, &wrq) < 0) {
 		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
 		       "(%s): error getting frequency: %s",
-		       ifname, strerror (errno));
+		       ifname, nm_strerror_native (errno));
 		return 0;
 	}
 
@@ -291,7 +291,7 @@ wifi_wext_get_bssid (NMWifiUtils *data, guint8 *out_bssid)
 	if (ioctl (wext->fd, SIOCGIWAP, &wrq) < 0) {
 		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
 		       "(%s): error getting associated BSSID: %s",
-		       ifname, strerror (errno));
+		       ifname, nm_strerror_native (errno));
 		return FALSE;
 	}
 	memcpy (out_bssid, &(wrq.u.ap_addr.sa_data), ETH_ALEN);
@@ -429,7 +429,7 @@ wifi_wext_get_qual (NMWifiUtils *data)
 	if (ioctl (wext->fd, SIOCGIWSTATS, &wrq) < 0) {
 		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
 		       "(%s): error getting signal strength: %s",
-		       ifname, strerror (errno));
+		       ifname, nm_strerror_native (errno));
 		return -1;
 	}
 
@@ -476,7 +476,7 @@ wifi_wext_set_mesh_channel (NMWifiUtils *data, guint32 channel)
 	if (ioctl (wext->fd, SIOCSIWFREQ, &wrq) < 0) {
 		_LOGE (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
 		       "(%s): error setting channel to %d: %s",
-		       ifname, channel, strerror (errno));
+		       ifname, channel, nm_strerror_native (errno));
 		return FALSE;
 	}
 
@@ -506,15 +506,15 @@ wifi_wext_set_mesh_ssid (NMWifiUtils *data, const guint8 *ssid, gsize len)
 	if (ioctl (wext->fd, SIOCSIWESSID, &wrq) == 0)
 		return TRUE;
 
-	if (errno != ENODEV) {
+	errsv = errno;
+	if (errsv != ENODEV) {
 		gs_free char *ssid_str = NULL;
 
-		errsv = errno;
 		_LOGE (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
 		       "(%s): error setting SSID to %s: %s",
 		       ifname,
 		       (ssid_str = _nm_utils_ssid_to_string_arr (ssid, len)),
-		       strerror (errsv));
+		       nm_strerror_native (errsv));
 	}
 
 	return FALSE;
@@ -545,6 +545,7 @@ wext_get_range_ifname (NMWifiUtilsWext *wext,
 	int i = 26;
 	gboolean success = FALSE;
 	struct iwreq wrq;
+	int errsv;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
 	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
@@ -561,11 +562,14 @@ wext_get_range_ifname (NMWifiUtilsWext *wext,
 				*response_len = wrq.u.data.length;
 			success = TRUE;
 			break;
-		} else if (errno != EAGAIN) {
-			_LOGE (LOGD_PLATFORM | LOGD_WIFI,
-			       "(%s): couldn't get driver range information (%d).",
-			       ifname, errno);
-			break;
+		} else {
+			errsv = errno;
+			if (errsv != EAGAIN) {
+				_LOGE (LOGD_PLATFORM | LOGD_WIFI,
+				       "(%s): couldn't get driver range information (%d).",
+				       ifname, errsv);
+				break;
+			}
 		}
 
 		g_usleep (G_USEC_PER_SEC / 4);
diff --git a/src/platform/wifi/nm-wifi-utils.c b/src/platform/wifi/nm-wifi-utils.c
index 25d71c6a..96071faa 100644
--- a/src/platform/wifi/nm-wifi-utils.c
+++ b/src/platform/wifi/nm-wifi-utils.c
@@ -25,7 +25,6 @@
 
 #include <sys/stat.h>
 #include <stdio.h>
-#include <string.h>
 #include <fcntl.h>
 
 #include "nm-wifi-utils-private.h"
diff --git a/src/platform/wpan/nm-wpan-utils.c b/src/platform/wpan/nm-wpan-utils.c
index f8708bc6..b7a51e9b 100644
--- a/src/platform/wpan/nm-wpan-utils.c
+++ b/src/platform/wpan/nm-wpan-utils.c
@@ -23,7 +23,6 @@
 
 #include <linux/if.h>
 
-#include "nm-utils/nm-errno.h"
 #include "platform/linux/nl802154.h"
 #include "platform/nm-netlink.h"
 #include "platform/nm-platform-utils.h"
@@ -154,17 +153,19 @@ struct nl802154_interface {
 static int
 nl802154_get_interface_handler (struct nl_msg *msg, void *arg)
 {
+	static const struct nla_policy nl802154_policy[] = {
+		[NL802154_ATTR_PAN_ID]     = { .type = NLA_U16 },
+		[NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
+	};
+	struct nlattr *tb[G_N_ELEMENTS (nl802154_policy)];
 	struct nl802154_interface *info = arg;
 	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
-	struct nlattr *tb[NL802154_ATTR_MAX + 1] = { 0, };
-	static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX + 1] = {
-		[NL802154_ATTR_PAN_ID] =            { .type = NLA_U16 },
-		[NL802154_ATTR_SHORT_ADDR] =        { .type = NLA_U16 },
-	};
 
-	if (nla_parse (tb, NL802154_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
-	               genlmsg_attrlen (gnlh, 0), nl802154_policy) < 0)
-	return NL_SKIP;
+	if (nla_parse_arr (tb,
+	                   genlmsg_attrdata (gnlh, 0),
+	                   genlmsg_attrlen (gnlh, 0),
+	                   nl802154_policy) < 0)
+		return NL_SKIP;
 
 	if (tb[NL802154_ATTR_PAN_ID])
 		info->pan_id = le16toh (nla_get_u16 (tb[NL802154_ATTR_PAN_ID]));