about summary refs log tree commit diff
path: root/src/platform/nm-linux-platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/nm-linux-platform.c')
-rw-r--r--src/platform/nm-linux-platform.c468
1 files changed, 289 insertions, 179 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 3282e698..ded019e6 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -176,13 +176,22 @@
  * Forward declarations and enums
  ******************************************************************/
 
+enum {
+	DELAYED_ACTION_IDX_REFRESH_ALL_LINKS,
+	DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES,
+	DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES,
+	DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES,
+	DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES,
+	_DELAYED_ACTION_IDX_REFRESH_ALL_NUM,
+};
+
 typedef enum {
 	DELAYED_ACTION_TYPE_NONE                        = 0,
-	DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS           = (1LL << 0),
-	DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES   = (1LL << 1),
-	DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES   = (1LL << 2),
-	DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES      = (1LL << 3),
-	DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES      = (1LL << 4),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS           = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_LINKS),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES   = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES   = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES      = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES      = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES),
 	DELAYED_ACTION_TYPE_REFRESH_LINK                = (1LL << 5),
 	DELAYED_ACTION_TYPE_MASTER_CONNECTED            = (1LL << 6),
 	DELAYED_ACTION_TYPE_READ_NETLINK                = (1LL << 7),
@@ -198,6 +207,10 @@ typedef enum {
 	DELAYED_ACTION_TYPE_MAX                         = __DELAYED_ACTION_TYPE_MAX -1,
 } DelayedActionType;
 
+#define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \
+	for ((iflags) = (DelayedActionType) 0x1LL; (iflags) <= DELAYED_ACTION_TYPE_MAX; (iflags) <<= 1) \
+		if (NM_FLAGS_HAS (flags_all, iflags))
+
 typedef enum {
 	/* Negative values are errors from kernel. Add dummy member to
 	 * make enum signed. */
@@ -290,49 +303,9 @@ _support_user_ipv6ll_detect (struct nlattr **tb)
  * Various utilities
  ******************************************************************/
 
-static guint
-_nm_ip_config_source_to_rtprot (NMIPConfigSource source)
-{
-	switch (source) {
-	case NM_IP_CONFIG_SOURCE_UNKNOWN:
-		return RTPROT_UNSPEC;
-	case NM_IP_CONFIG_SOURCE_KERNEL:
-	case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL:
-		return RTPROT_KERNEL;
-	case NM_IP_CONFIG_SOURCE_DHCP:
-		return RTPROT_DHCP;
-	case NM_IP_CONFIG_SOURCE_RDISC:
-		return RTPROT_RA;
-
-	default:
-		return RTPROT_STATIC;
-	}
-}
-
-static NMIPConfigSource
-_nm_ip_config_source_from_rtprot (guint rtprot)
-{
-	switch (rtprot) {
-	case RTPROT_UNSPEC:
-		return NM_IP_CONFIG_SOURCE_UNKNOWN;
-	case RTPROT_KERNEL:
-		return NM_IP_CONFIG_SOURCE_RTPROT_KERNEL;
-	case RTPROT_REDIRECT:
-		return NM_IP_CONFIG_SOURCE_KERNEL;
-	case RTPROT_RA:
-		return NM_IP_CONFIG_SOURCE_RDISC;
-	case RTPROT_DHCP:
-		return NM_IP_CONFIG_SOURCE_DHCP;
-
-	default:
-		return NM_IP_CONFIG_SOURCE_USER;
-	}
-}
-
 static void
-clear_host_address (int family, const void *network, int plen, void *dst)
+clear_host_address (int family, const void *network, guint8 plen, void *dst)
 {
-	g_return_if_fail (plen == (guint8)plen);
 	g_return_if_fail (network);
 
 	switch (family) {
@@ -1641,6 +1614,9 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
 	           ? sizeof (in_addr_t)
 	           : sizeof (struct in6_addr);
 
+	if (ifa->ifa_prefixlen > (is_v4 ? 32 : 128))
+		goto errout;
+
 	/*****************************************************************/
 
 	obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, NULL);
@@ -1779,6 +1755,9 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 	           ? sizeof (in_addr_t)
 	           : sizeof (struct in6_addr);
 
+	if (rtm->rtm_dst_len > (is_v4 ? 32 : 128))
+		goto errout;
+
 	/*****************************************************************
 	 * parse nexthops. Only handle routes with one nh.
 	 *****************************************************************/
@@ -1906,7 +1885,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 		 * */
 		obj->ip_route.source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED;
 	} else
-		obj->ip_route.source = _nm_ip_config_source_from_rtprot (rtm->rtm_protocol);
+		obj->ip_route.source = nmp_utils_ip_config_source_from_rtprot (rtm->rtm_protocol);
 
 	obj_result = obj;
 	obj = NULL;
@@ -2158,7 +2137,7 @@ _nl_msg_new_address (int nlmsg_type,
                      int family,
                      int ifindex,
                      gconstpointer address,
-                     int plen,
+                     guint8 plen,
                      gconstpointer peer_address,
                      guint32 flags,
                      int scope,
@@ -2255,7 +2234,7 @@ _nl_msg_new_route (int nlmsg_type,
                    NMIPConfigSource source,
                    unsigned char scope,
                    gconstpointer network,
-                   int plen,
+                   guint8 plen,
                    gconstpointer gateway,
                    guint32 metric,
                    guint32 mss,
@@ -2266,7 +2245,7 @@ _nl_msg_new_route (int nlmsg_type,
 		.rtm_family = family,
 		.rtm_tos = 0,
 		.rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */
-		.rtm_protocol = _nm_ip_config_source_to_rtprot (source),
+		.rtm_protocol = nmp_utils_ip_config_source_to_rtprot (source),
 		.rtm_scope = scope,
 		.rtm_type = RTN_UNICAST,
 		.rtm_flags = 0,
@@ -2369,9 +2348,10 @@ _support_kernel_extended_ifa_flags_get (void)
 
 typedef struct {
 	guint32 seq_number;
-	gint64 timeout_abs_ns;
 	WaitForNlResponseResult seq_result;
+	gint64 timeout_abs_ns;
 	WaitForNlResponseResult *out_seq_result;
+	gint *out_refresh_all_in_progess;
 } DelayedActionWaitForNlResponseData;
 
 typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate;
@@ -2379,7 +2359,10 @@ typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate;
 struct _NMLinuxPlatformPrivate {
 	struct nl_sock *nlh;
 	guint32 nlh_seq_next;
+#ifdef NM_MORE_LOGGING
 	guint32 nlh_seq_last_handled;
+#endif
+	guint32 nlh_seq_last_seen;
 	NMPCache *cache;
 	GIOChannel *event_channel;
 	guint event_id;
@@ -2390,10 +2373,18 @@ struct _NMLinuxPlatformPrivate {
 	GUdevClient *udev_client;
 
 	struct {
+		/* which delayed actions are scheduled, as marked in @flags.
+		 * Some types have additional arguments in the fields below. */
 		DelayedActionType flags;
+
+		/* counter that a refresh all action is in progress, separated
+		 * by type. */
+		gint refresh_all_in_progess[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM];
+
 		GPtrArray *list_master_connected;
 		GPtrArray *list_refresh_link;
 		GArray *list_wait_for_nl_response;
+
 		gint is_handling;
 	} delayed_action;
 
@@ -2412,12 +2403,21 @@ NM_LINUX_PLATFORM_GET_PRIVATE (const void *self)
 
 G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM)
 
+NMPlatform *
+nm_linux_platform_new (gboolean netns_support)
+{
+	return g_object_new (NM_TYPE_LINUX_PLATFORM,
+	                     NM_PLATFORM_REGISTER_SINGLETON, FALSE,
+	                     NM_PLATFORM_NETNS_SUPPORT, netns_support,
+	                     NULL);
+}
+
 void
 nm_linux_platform_setup (void)
 {
 	g_object_new (NM_TYPE_LINUX_PLATFORM,
-	              NM_PLATFORM_NETNS_SUPPORT, FALSE,
 	              NM_PLATFORM_REGISTER_SINGLETON, TRUE,
+	              NM_PLATFORM_NETNS_SUPPORT, FALSE,
 	              NULL);
 }
 
@@ -2733,49 +2733,51 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach
 
 /******************************************************************/
 
-static DelayedActionType
-delayed_action_refresh_from_object_type (NMPObjectType obj_type)
-{
-	switch (obj_type) {
-	case NMP_OBJECT_TYPE_LINK:          return DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS;
-	case NMP_OBJECT_TYPE_IP4_ADDRESS:   return DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES;
-	case NMP_OBJECT_TYPE_IP6_ADDRESS:   return DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES;
-	case NMP_OBJECT_TYPE_IP4_ROUTE:     return DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES;
-	case NMP_OBJECT_TYPE_IP6_ROUTE:     return DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES;
-	default: g_return_val_if_reached (DELAYED_ACTION_TYPE_NONE);
-	}
-}
-
-static NMPObjectType
-delayed_action_refresh_to_object_type (DelayedActionType action_type)
-{
-	switch (action_type) {
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS:             return NMP_OBJECT_TYPE_LINK;
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES:     return NMP_OBJECT_TYPE_IP4_ADDRESS;
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES:     return NMP_OBJECT_TYPE_IP6_ADDRESS;
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES:        return NMP_OBJECT_TYPE_IP4_ROUTE;
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES:        return NMP_OBJECT_TYPE_IP6_ROUTE;
-	default: g_return_val_if_reached (NMP_OBJECT_TYPE_UNKNOWN);
-	}
-}
-
-static const char *
-delayed_action_to_string (DelayedActionType action_type)
-{
-	switch (action_type) {
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS              : return "refresh-all-links";
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES      : return "refresh-all-ip4-addresses";
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES      : return "refresh-all-ip6-addresses";
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES         : return "refresh-all-ip4-routes";
-	case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES         : return "refresh-all-ip6-routes";
-	case DELAYED_ACTION_TYPE_REFRESH_LINK                   : return "refresh-link";
-	case DELAYED_ACTION_TYPE_MASTER_CONNECTED               : return "master-connected";
-	case DELAYED_ACTION_TYPE_READ_NETLINK                   : return "read-netlink";
-	case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE           : return "wait-for-nl-response";
-	default:
-		return "unknown";
-	}
-}
+_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_from_object_type, NMPObjectType, DelayedActionType,
+	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (DELAYED_ACTION_TYPE_NONE),
+	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_LINK,        DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS),
+	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP4_ADDRESS, DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES),
+	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ADDRESS, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES),
+	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP4_ROUTE,   DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES),
+	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ROUTE,   DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES),
+	NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (),
+);
+
+_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_to_object_type, DelayedActionType, NMPObjectType,
+	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (NMP_OBJECT_TYPE_UNKNOWN),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS,         NMP_OBJECT_TYPE_LINK),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES,    NMP_OBJECT_TYPE_IP4_ROUTE),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,    NMP_OBJECT_TYPE_IP6_ROUTE),
+	NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (),
+);
+
+_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_all_to_idx, DelayedActionType, guint,
+	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (0),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS,         DELAYED_ACTION_IDX_REFRESH_ALL_LINKS),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES,    DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,    DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES),
+	NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (),
+);
+
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType,
+	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS,         "refresh-all-links"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, "refresh-all-ip4-addresses"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES,    "refresh-all-ip4-routes"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,    "refresh-all-ip6-routes"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK,              "refresh-link"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_MASTER_CONNECTED,          "master-connected"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_READ_NETLINK,              "read-netlink"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE,      "wait-for-nl-response"),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_NONE),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (__DELAYED_ACTION_TYPE_MAX),
+);
 
 static const char *
 delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data, char *buf, gsize buf_size)
@@ -2826,6 +2828,24 @@ delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data
 
 /*****************************************************************************/
 
+static gboolean
+delayed_action_refresh_all_in_progress (NMPlatform *platform, DelayedActionType action_type)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+
+	nm_assert (nm_utils_is_power_of_two (action_type));
+	nm_assert (NM_FLAGS_ANY (action_type, DELAYED_ACTION_TYPE_REFRESH_ALL));
+	nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL));
+
+	if (NM_FLAGS_ANY (priv->delayed_action.flags, action_type))
+		return TRUE;
+
+	if (priv->delayed_action.refresh_all_in_progess[delayed_action_refresh_all_to_idx (action_type)] > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
 static void
 delayed_action_wait_for_nl_response_complete (NMPlatform *platform,
                                               guint idx,
@@ -2833,7 +2853,6 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform,
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	DelayedActionWaitForNlResponseData *data;
-	WaitForNlResponseResult *out_seq_result;
 
 	nm_assert (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
 	nm_assert (idx < priv->delayed_action.list_wait_for_nl_response->len);
@@ -2843,16 +2862,16 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform,
 
 	_LOGt_delayed_action (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, data, "complete");
 
-	out_seq_result = data->out_seq_result;
-
-	g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx);
-	/* Note: @data is invalidated at this point */
-
-	if (priv->delayed_action.list_wait_for_nl_response->len <= 0)
+	if (priv->delayed_action.list_wait_for_nl_response->len <= 1)
 		priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE;
+	if (data->out_seq_result)
+		*data->out_seq_result = seq_result;
+	if (data->out_refresh_all_in_progess) {
+		nm_assert (*data->out_refresh_all_in_progess > 0);
+		*data->out_refresh_all_in_progess -= 1;
+	}
 
-	if (out_seq_result)
-		*out_seq_result = seq_result;
+	g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx);
 }
 
 static void
@@ -2961,9 +2980,8 @@ delayed_action_handle_one (NMPlatform *platform)
 		priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL;
 
 		if (_LOGt_ENABLED ()) {
-			for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) {
-				if (NM_FLAGS_HAS (flags, iflags))
-					_LOGt_delayed_action (iflags, NULL, "handle");
+			FOR_EACH_DELAYED_ACTION (iflags, flags) {
+				_LOGt_delayed_action (iflags, NULL, "handle");
 			}
 		}
 
@@ -3048,9 +3066,8 @@ delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gp
 	priv->delayed_action.flags |= action_type;
 
 	if (_LOGt_ENABLED ()) {
-		for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) {
-			if (NM_FLAGS_HAS (action_type, iflags))
-				_LOGt_delayed_action (iflags, user_data, "schedule");
+		FOR_EACH_DELAYED_ACTION (iflags, action_type) {
+			_LOGt_delayed_action (iflags, user_data, "schedule");
 		}
 	}
 }
@@ -3058,12 +3075,14 @@ delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gp
 static void
 delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform,
                                               guint32 seq_number,
-                                              WaitForNlResponseResult *out_seq_result)
+                                              WaitForNlResponseResult *out_seq_result,
+                                              gint *out_refresh_all_in_progess)
 {
 	DelayedActionWaitForNlResponseData data = {
 		.seq_number = seq_number,
 		.timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)),
 		.out_seq_result = out_seq_result,
+		.out_refresh_all_in_progess = out_refresh_all_in_progess,
 	};
 
 	delayed_action_schedule (platform,
@@ -3150,7 +3169,7 @@ cache_prune_candidates_prune (NMPlatform *platform)
 static void
 cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data)
 {
-	NMPlatform *platform = NM_PLATFORM (user_data);
+	NMPlatform *platform = user_data;
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	const NMPClass *klass;
 	char str_buf[sizeof (_nm_utils_to_string_buffer)];
@@ -3162,6 +3181,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP
 	nm_assert (ops_type != NMP_CACHE_OPS_REMOVED || (new == NULL && NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old)));
 	nm_assert (ops_type != NMP_CACHE_OPS_UPDATED || (NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old) && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new)));
 	nm_assert (new == NULL || old == NULL || nmp_object_id_equal (new, old));
+	nm_assert (!old || !new || NMP_OBJECT_GET_CLASS (old) == NMP_OBJECT_GET_CLASS (new));
 
 	klass = old ? NMP_OBJECT_GET_CLASS (old) : NMP_OBJECT_GET_CLASS (new);
 
@@ -3368,17 +3388,56 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP
 				                         NULL);
 			}
 		}
+		break;
 	default:
 		break;
 	}
 }
 
+static void
+cache_post (NMPlatform *platform,
+            struct nlmsghdr *msghdr,
+            NMPCacheOpsType cache_op,
+            NMPObject *obj,
+            NMPObject *obj_cache)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+
+	nm_assert (NMP_OBJECT_IS_VALID (obj));
+	nm_assert (!obj_cache || nmp_object_id_equal (obj, obj_cache));
+
+	if (msghdr->nlmsg_type == RTM_NEWROUTE) {
+		DelayedActionType action_type;
+
+		action_type = NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE
+		                  ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES
+		                  : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES;
+		if (   !delayed_action_refresh_all_in_progress (platform, action_type)
+		    && nmp_cache_find_other_route_for_same_destination (priv->cache, obj)) {
+			/* via `iproute route change` the user can update an existing route which effectively
+			 * means that a new object (with a different ID) comes into existance, replacing the
+			 * old on. In other words, as the ID of the object changes, we really see a new
+			 * object with the old one deleted.
+			 * However, kernel decides not to send a RTM_DELROUTE event for that.
+			 *
+			 * To hack around that, check if the update leaves us with multiple routes for the
+			 * same network/plen,metric part. In that case, we cannot do better then requesting
+			 * all routes anew, which sucks.
+			 *
+			 * One mitigation to avoid a dump is only to request a new dump, if we are not in
+			 * the middle of an ongoing dump (delayed_action_refresh_all_in_progress). */
+			delayed_action_schedule (platform, action_type, NULL);
+		}
+	}
+}
+
 /******************************************************************/
 
 static int
 _nl_send_auto_with_seq (NMPlatform *platform,
                         struct nl_msg *nlmsg,
-                        WaitForNlResponseResult *out_seq_result)
+                        WaitForNlResponseResult *out_seq_result,
+                        gint *out_refresh_all_in_progess)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	guint32 seq;
@@ -3393,7 +3452,7 @@ _nl_send_auto_with_seq (NMPlatform *platform,
 
 	if (nle >= 0) {
 		nle = 0;
-		delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result);
+		delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_refresh_all_in_progess);
 	} else
 		_LOGD ("netlink: send: failed sending message: %s (%d)", nl_geterror (nle), nle);
 
@@ -3427,7 +3486,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha
 	                          0,
 	                          0);
 	if (nlmsg)
-		_nl_send_auto_with_seq (platform, nlmsg, NULL);
+		_nl_send_auto_with_seq (platform, nlmsg, NULL, NULL);
 }
 
 static void
@@ -3446,48 +3505,51 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
 	nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL));
 	action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL;
 
-	for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) {
-		if (NM_FLAGS_HAS (action_type, iflags))
-			cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags));
+	FOR_EACH_DELAYED_ACTION (iflags, action_type) {
+		cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags));
 	}
 
-	for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) {
-		if (NM_FLAGS_HAS (action_type, iflags)) {
-			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;
-			struct rtgenmsg gmsg = {
-				.rtgen_family = klass->addr_family,
-			};
-			int nle;
-
-			/* clear any delayed action that request a refresh of this object type. */
-			priv->delayed_action.flags &= ~iflags;
-			_LOGt_delayed_action (iflags, NULL, "handle (do-request-all)");
-			if (obj_type == NMP_OBJECT_TYPE_LINK) {
-				priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK;
-				g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0);
-				_LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)");
-			}
+	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;
+		struct rtgenmsg gmsg = {
+			.rtgen_family = klass->addr_family,
+		};
+		int nle;
+		gint *out_refresh_all_in_progess;
 
-			event_handler_read_netlink (platform, FALSE);
+		out_refresh_all_in_progess = &priv->delayed_action.refresh_all_in_progess[delayed_action_refresh_all_to_idx (iflags)];
+		nm_assert (*out_refresh_all_in_progess >= 0);
+		*out_refresh_all_in_progess += 1;
 
-			/* 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);
-			if (!nlmsg)
-				goto next;
+		/* clear any delayed action that request a refresh of this object type. */
+		priv->delayed_action.flags &= ~iflags;
+		_LOGt_delayed_action (iflags, NULL, "handle (do-request-all)");
+		if (obj_type == NMP_OBJECT_TYPE_LINK) {
+			priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK;
+			g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0);
+			_LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)");
+		}
 
-			nle = nlmsg_append (nlmsg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO);
-			if (nle < 0)
-				goto next;
+		event_handler_read_netlink (platform, FALSE);
 
-			_nl_send_auto_with_seq (platform, nlmsg, NULL);
+		/* 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);
+		if (!nlmsg)
+			continue;
+
+		nle = nlmsg_append (nlmsg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO);
+		if (nle < 0)
+			continue;
+
+		if (_nl_send_auto_with_seq (platform, nlmsg, NULL, out_refresh_all_in_progess) < 0) {
+			nm_assert (*out_refresh_all_in_progess > 0);
+			*out_refresh_all_in_progess -= 1;
 		}
-next:
-		;
 	}
 }
 
@@ -3499,14 +3561,41 @@ do_request_one_type (NMPlatform *platform, NMPObjectType obj_type)
 }
 
 static void
-event_seq_check (NMPlatform *platform, struct nl_msg *msg, WaitForNlResponseResult seq_result)
+event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	DelayedActionWaitForNlResponseData *data;
-	guint32 seq_number;
 	guint i;
 
-	seq_number = nlmsg_hdr (msg)->nlmsg_seq;
+	if (NM_IN_SET (seq_number, 0, priv->nlh_seq_last_seen))
+		return;
+
+	if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
+		nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0);
+
+		for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) {
+			data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i);
+
+			if (data->seq_number == priv->nlh_seq_last_seen) {
+				if (data->out_refresh_all_in_progess) {
+					nm_assert (*data->out_refresh_all_in_progess > 0);
+					*data->out_refresh_all_in_progess -= 1;
+					data->out_refresh_all_in_progess = NULL;
+					break;
+				}
+			}
+		}
+	}
+
+	priv->nlh_seq_last_seen = seq_number;
+}
+
+static void
+event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult seq_result)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	DelayedActionWaitForNlResponseData *data;
+	guint i;
 
 	if (seq_number == 0)
 		return;
@@ -3531,9 +3620,11 @@ event_seq_check (NMPlatform *platform, struct nl_msg *msg, WaitForNlResponseResu
 		}
 	}
 
+#ifdef NM_MORE_LOGGING
 	if (seq_number != priv->nlh_seq_last_handled)
 		_LOGt ("netlink: recvmsg: unwaited sequence number %u", seq_number);
 	priv->nlh_seq_last_handled = seq_number;
+#endif
 }
 
 static void
@@ -3581,6 +3672,9 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 	case RTM_NEWADDR:
 	case RTM_NEWROUTE:
 		cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform);
+
+		cache_post (platform, msghdr, cache_op, obj, obj_cache);
+
 		do_emit_signal (platform, obj_cache, cache_op, was_visible);
 		break;
 
@@ -3730,7 +3824,7 @@ do_add_link_with_lookup (NMPlatform *platform,
 		}
 	}
 
-	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
+	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL);
 	if (nle < 0) {
 		_LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)",
 		       name,
@@ -3781,7 +3875,7 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *
 
 	event_handler_read_netlink (platform, FALSE);
 
-	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
+	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL);
 	if (nle < 0) {
 		_LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)",
 		       NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
@@ -3834,7 +3928,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *
 
 	event_handler_read_netlink (platform, FALSE);
 
-	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
+	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL);
 	if (nle < 0) {
 		_LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)",
 		       NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
@@ -3895,7 +3989,7 @@ do_change_link (NMPlatform *platform,
 		return NM_PLATFORM_ERROR_UNSPECIFIED;
 
 retry:
-	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
+	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL);
 	if (nle < 0) {
 		_LOGE ("do-change-link[%d]: failure sending netlink request \"%s\" (%d)",
 		       ifindex,
@@ -3964,7 +4058,7 @@ link_add (NMPlatform *platform,
 	       name, nm_link_type_to_string (type), (int) type);
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
-	                          NLM_F_CREATE,
+	                          NLM_F_CREATE | NLM_F_EXCL,
 	                          0,
 	                          name,
 	                          0,
@@ -4356,7 +4450,7 @@ vlan_add (NMPlatform *platform,
 	       name, parent, vlan_id, (unsigned int) vlan_flags);
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
-	                          NLM_F_CREATE,
+	                          NLM_F_CREATE | NLM_F_EXCL,
 	                          0,
 	                          name,
 	                          0,
@@ -4400,7 +4494,7 @@ link_gre_add (NMPlatform *platform,
 	       nm_utils_inet4_ntop (props->remote, buffer));
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
-	                          NLM_F_CREATE,
+	                          NLM_F_CREATE | NLM_F_EXCL,
 	                          0,
 	                          name,
 	                          0,
@@ -4456,7 +4550,7 @@ link_ip6tnl_add (NMPlatform *platform,
 	       nm_utils_inet6_ntop (&props->remote, buffer));
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
-	                          NLM_F_CREATE,
+	                          NLM_F_CREATE | NLM_F_EXCL,
 	                          0,
 	                          name,
 	                          0,
@@ -4516,7 +4610,7 @@ link_ipip_add (NMPlatform *platform,
 	       nm_utils_inet4_ntop (props->remote, buffer));
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
-	                          NLM_F_CREATE,
+	                          NLM_F_CREATE | NLM_F_EXCL,
 	                          0,
 	                          name,
 	                          0,
@@ -4566,7 +4660,7 @@ link_macvlan_add (NMPlatform *platform,
 	       props->mode);
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
-	                          NLM_F_CREATE,
+	                          NLM_F_CREATE | NLM_F_EXCL,
 	                          0,
 	                          name,
 	                          0,
@@ -4616,7 +4710,7 @@ link_sit_add (NMPlatform *platform,
 	       nm_utils_inet4_ntop (props->remote, buffer));
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
-	                          NLM_F_CREATE,
+	                          NLM_F_CREATE | NLM_F_EXCL,
 	                          0,
 	                          name,
 	                          0,
@@ -4665,7 +4759,7 @@ link_vxlan_add (NMPlatform *platform,
 	       name, props->parent_ifindex, props->id);
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
-	                          NLM_F_CREATE,
+	                          NLM_F_CREATE | NLM_F_EXCL,
 	                          0,
 	                          name,
 	                          0,
@@ -5266,7 +5360,7 @@ static gboolean
 ip4_address_add (NMPlatform *platform,
                  int ifindex,
                  in_addr_t addr,
-                 int plen,
+                 guint8 plen,
                  in_addr_t peer_addr,
                  guint32 lifetime,
                  guint32 preferred,
@@ -5297,7 +5391,7 @@ static gboolean
 ip6_address_add (NMPlatform *platform,
                  int ifindex,
                  struct in6_addr addr,
-                 int plen,
+                 guint8 plen,
                  struct in6_addr peer_addr,
                  guint32 lifetime,
                  guint32 preferred,
@@ -5324,7 +5418,7 @@ ip6_address_add (NMPlatform *platform,
 }
 
 static gboolean
-ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address)
+ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address)
 {
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
 	NMPObject obj_id;
@@ -5349,7 +5443,7 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen,
 }
 
 static gboolean
-ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
+ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen)
 {
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
 	NMPObject obj_id;
@@ -5374,7 +5468,7 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int
 }
 
 static const NMPlatformIP4Address *
-ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address)
+ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address)
 {
 	NMPObject obj_id;
 	const NMPObject *obj;
@@ -5387,7 +5481,7 @@ ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in
 }
 
 static const NMPlatformIP6Address *
-ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
+ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen)
 {
 	NMPObject obj_id;
 	const NMPObject *obj;
@@ -5454,7 +5548,7 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags fl
 
 static gboolean
 ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
-               in_addr_t network, int plen, in_addr_t gateway,
+               in_addr_t network, guint8 plen, in_addr_t gateway,
                in_addr_t pref_src, guint32 metric, guint32 mss)
 {
 	NMPObject obj_id;
@@ -5479,7 +5573,7 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
 
 static gboolean
 ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
-               struct in6_addr network, int plen, struct in6_addr gateway,
+               struct in6_addr network, guint8 plen, struct in6_addr gateway,
                guint32 metric, guint32 mss)
 {
 	NMPObject obj_id;
@@ -5503,7 +5597,7 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
 }
 
 static gboolean
-ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric)
+ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
@@ -5559,7 +5653,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen
 }
 
 static gboolean
-ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric)
+ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric)
 {
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
 	NMPObject obj_id;
@@ -5587,7 +5681,7 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, in
 }
 
 static const NMPlatformIP4Route *
-ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric)
+ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric)
 {
 	NMPObject obj_id;
 	const NMPObject *obj;
@@ -5600,7 +5694,7 @@ ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, g
 }
 
 static const NMPlatformIP6Route *
-ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric)
+ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric)
 {
 	NMPObject obj_id;
 	const NMPObject *obj;
@@ -5684,6 +5778,8 @@ continue_reading:
 	while (nlmsg_ok (hdr, n)) {
 		nm_auto_nlmsg struct nl_msg *msg = NULL;
 		gboolean abort_parsing = FALSE;
+		gboolean process_valid_msg = FALSE;
+		guint32 seq_number;
 
 		msg = nlmsg_convert (hdr);
 		if (!msg) {
@@ -5768,7 +5864,21 @@ continue_reading:
 				seq_result = -errsv;
 			} else
 				seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
-		} else {
+		} else
+			process_valid_msg = TRUE;
+
+		seq_number = nlmsg_hdr (msg)->nlmsg_seq;
+
+		/* check whether the seq number is different from before, and
+		 * whether the previous number (@nlh_seq_last_seen) is a pending
+		 * refresh-all request. In that case, the pending request is thereby
+		 * completed.
+		 *
+		 * We must do that before processing the message with event_valid_msg(),
+		 * because we must track the completion of the pending request before that. */
+		event_seq_check_refresh_all (platform, seq_number);
+
+		if (process_valid_msg) {
 			/* Valid message (not checking for MULTIPART bit to
 			 * get along with broken kernels. NL_SKIP has no
 			 * effect on this.  */
@@ -5778,7 +5888,7 @@ continue_reading:
 			seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
 		}
 
-		event_seq_check (platform, msg, seq_result);
+		event_seq_check (platform, seq_number, seq_result);
 
 		if (abort_parsing)
 			goto stop;