summary refs log tree commit diff
path: root/src/nm-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r--src/nm-manager.c875
1 files changed, 543 insertions, 332 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 289dcf83..f8be6d37 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -310,7 +310,8 @@ static void _emit_device_added_removed (NMManager *self,
 
 static NMActiveConnection *_new_active_connection (NMManager *self,
                                                    gboolean is_vpn,
-                                                   NMConnection *connection,
+                                                   NMSettingsConnection *sett_conn,
+                                                   NMConnection *incompl_conn,
                                                    NMConnection *applied,
                                                    const char *specific_object,
                                                    NMDevice *device,
@@ -319,7 +320,7 @@ static NMActiveConnection *_new_active_connection (NMManager *self,
                                                    NMActivationReason activation_reason,
                                                    GError **error);
 
-static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data);
+static void policy_activating_ac_changed (GObject *object, GParamSpec *pspec, gpointer user_data);
 
 static gboolean find_master (NMManager *self,
                              NMConnection *connection,
@@ -331,7 +332,8 @@ static gboolean find_master (NMManager *self,
 
 static void nm_manager_update_state (NMManager *manager);
 
-static void connection_changed (NMManager *self, NMConnection *connection);
+static void connection_changed (NMManager *self,
+                                NMSettingsConnection *sett_conn);
 static void device_sleep_cb (NMDevice *device,
                              GParamSpec *pspec,
                              NMManager *self);
@@ -353,7 +355,7 @@ static void active_connection_parent_active (NMActiveConnection *active,
                                              NMManager *self);
 
 static NMActiveConnection *active_connection_find (NMManager *self,
-                                                   NMSettingsConnection *settings_connection,
+                                                   NMSettingsConnection *sett_conn,
                                                    const char *uuid,
                                                    NMActiveConnectionState max_state,
                                                    GPtrArray **out_all_matching);
@@ -947,7 +949,7 @@ nm_manager_get_active_connections (NMManager *manager)
 
 static NMActiveConnection *
 active_connection_find (NMManager *self,
-                        NMSettingsConnection *settings_connection,
+                        NMSettingsConnection *sett_conn,
                         const char *uuid,
                         NMActiveConnectionState max_state /* candidates in state @max_state will be found */,
                         GPtrArray **out_all_matching)
@@ -957,16 +959,18 @@ active_connection_find (NMManager *self,
 	NMActiveConnection *best_ac = NULL;
 	GPtrArray *all = NULL;
 
-	nm_assert (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection));
+	nm_assert (!sett_conn || NM_IS_SETTINGS_CONNECTION (sett_conn));
 	nm_assert (!out_all_matching || !*out_all_matching);
 
 	c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
-		NMSettingsConnection *con;
+		NMSettingsConnection *ac_conn;
 
-		con = nm_active_connection_get_settings_connection (ac);
-		if (settings_connection && con != settings_connection)
+		ac_conn = nm_active_connection_get_settings_connection (ac);
+		if (   sett_conn
+		    && sett_conn != ac_conn)
 			continue;
-		if (uuid && !nm_streq0 (uuid, nm_connection_get_uuid (NM_CONNECTION (con))))
+		if (   uuid
+		    && !nm_streq0 (uuid, nm_settings_connection_get_uuid (ac_conn)))
 			continue;
 		if (nm_active_connection_get_state (ac) > max_state)
 			continue;
@@ -1005,47 +1009,73 @@ active_connection_find (NMManager *self,
 
 static NMActiveConnection *
 active_connection_find_by_connection (NMManager *self,
+                                      NMSettingsConnection *sett_conn,
                                       NMConnection *connection,
                                       NMActiveConnectionState max_state,
                                       GPtrArray **out_all_matching)
 {
-	gboolean is_settings_connection;
-
 	nm_assert (NM_IS_MANAGER (self));
-	nm_assert (NM_IS_CONNECTION (connection));
+	nm_assert (!sett_conn || NM_IS_SETTINGS_CONNECTION (sett_conn));
+	nm_assert (!connection || NM_IS_CONNECTION (connection));
+	nm_assert (sett_conn || connection);
+	nm_assert (!connection || !sett_conn || connection == nm_settings_connection_get_connection (sett_conn));
 
-	is_settings_connection = NM_IS_SETTINGS_CONNECTION (connection);
 	/* Depending on whether connection is a settings connection,
 	 * either lookup by object-identity of @connection, or compare the UUID */
 	return active_connection_find (self,
-	                               is_settings_connection ? NM_SETTINGS_CONNECTION (connection) : NULL,
-	                               is_settings_connection ? NULL : nm_connection_get_uuid (connection),
+	                               sett_conn,
+	                               sett_conn ? NULL : nm_connection_get_uuid (connection),
 	                               max_state,
 	                               out_all_matching);
 }
 
+typedef struct {
+	NMManager *self;
+	gboolean for_auto_activation;
+} GetActivatableConnectionsFilterData;
+
 static gboolean
 _get_activatable_connections_filter (NMSettings *settings,
-                                     NMSettingsConnection *connection,
+                                     NMSettingsConnection *sett_conn,
                                      gpointer user_data)
 {
-	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
+	const GetActivatableConnectionsFilterData *d = user_data;
+	NMConnectionMultiConnect multi_connect;
+
+	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (sett_conn),
 	                  NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
 		return FALSE;
 
+	multi_connect = _nm_connection_get_multi_connect (nm_settings_connection_get_connection (sett_conn));
+	if (   multi_connect == NM_CONNECTION_MULTI_CONNECT_MULTIPLE
+	    || (   multi_connect == NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE
+	        && !d->for_auto_activation))
+		return TRUE;
+
 	/* the connection is activatable, if it has no active-connections that are in state
 	 * activated, activating, or waiting to be activated. */
-	return !active_connection_find (user_data, connection, NULL, NM_ACTIVE_CONNECTION_STATE_ACTIVATED, NULL);
+	return !active_connection_find (d->self,
+	                                sett_conn,
+	                                NULL,
+	                                NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+	                                NULL);
 }
 
 NMSettingsConnection **
-nm_manager_get_activatable_connections (NMManager *manager, guint *out_len, gboolean sort)
+nm_manager_get_activatable_connections (NMManager *manager,
+                                        gboolean for_auto_activation,
+                                        gboolean sort,
+                                        guint *out_len)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	const GetActivatableConnectionsFilterData d = {
+		.self = manager,
+		.for_auto_activation = for_auto_activation,
+	};
 
 	return nm_settings_get_connections_clone (priv->settings, out_len,
 	                                          _get_activatable_connections_filter,
-	                                          manager,
+	                                          (gpointer) &d,
 	                                          sort ? nm_settings_connection_cmp_autoconnect_priority_p_with_data : NULL,
 	                                          NULL);
 }
@@ -1056,8 +1086,8 @@ active_connection_get_by_path (NMManager *self, const char *path)
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *ac;
 
-	ac = (NMActiveConnection *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
-	                                                           path);
+	ac = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
+	                                    path);
 	if (   !ac
 	    || !NM_IS_ACTIVE_CONNECTION (ac)
 	    || c_list_is_empty (&ac->active_connections_lst))
@@ -1193,8 +1223,8 @@ nm_manager_get_device_by_path (NMManager *self, const char *path)
 
 	g_return_val_if_fail (path, NULL);
 
-	device = (NMDevice *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
-	                                                     path);
+	device = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
+	                                        path);
 	if (   !device
 	    || !NM_IS_DEVICE (device)
 	    || c_list_is_empty (&device->devices_lst))
@@ -1244,7 +1274,7 @@ find_device_by_permanent_hw_addr (NMManager *self, const char *hwaddr)
 }
 
 static NMDevice *
-find_device_by_ip_iface (NMManager *self, const gchar *iface)
+find_device_by_ip_iface (NMManager *self, const char *iface)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDevice *device;
@@ -1290,7 +1320,7 @@ find_device_by_iface (NMManager *self,
 
 		if (strcmp (nm_device_get_iface (candidate), iface))
 			continue;
-		if (connection && !nm_device_check_connection_compatible (candidate, connection))
+		if (connection && !nm_device_check_connection_compatible (candidate, connection, NULL))
 			continue;
 		if (slave) {
 			if (!nm_device_is_master (candidate))
@@ -1459,20 +1489,23 @@ manager_device_state_changed (NMDevice *device,
 	    && new_state > NM_DEVICE_STATE_UNMANAGED)
 		retry_connections_for_parent_device (self, device);
 
-	switch (new_state) {
-	case NM_DEVICE_STATE_UNMANAGED:
-	case NM_DEVICE_STATE_UNAVAILABLE:
-	case NM_DEVICE_STATE_DISCONNECTED:
-	case NM_DEVICE_STATE_PREPARE:
-	case NM_DEVICE_STATE_FAILED:
+	if (NM_IN_SET (new_state,
+	               NM_DEVICE_STATE_UNMANAGED,
+	               NM_DEVICE_STATE_UNAVAILABLE,
+	               NM_DEVICE_STATE_DISCONNECTED,
+	               NM_DEVICE_STATE_PREPARE,
+	               NM_DEVICE_STATE_FAILED))
 		_notify (self, PROP_ACTIVE_CONNECTIONS);
-		break;
-	default:
-		break;
-	}
 
-	if (   new_state == NM_DEVICE_STATE_UNAVAILABLE
-	    || new_state == NM_DEVICE_STATE_DISCONNECTED)
+	if (NM_IN_SET (new_state,
+	               NM_DEVICE_STATE_UNMANAGED,
+	               NM_DEVICE_STATE_DISCONNECTED,
+	               NM_DEVICE_STATE_ACTIVATED))
+		nm_manager_write_device_state (self, device);
+
+	if (NM_IN_SET (new_state,
+	               NM_DEVICE_STATE_UNAVAILABLE,
+	               NM_DEVICE_STATE_DISCONNECTED))
 		nm_settings_device_added (priv->settings, device);
 }
 
@@ -1707,7 +1740,9 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 			return candidate;
 
 		if (   !first_compatible
-		    && nm_device_check_connection_compatible (candidate, NM_CONNECTION (parent_connection)))
+		    && nm_device_check_connection_compatible (candidate,
+		                                              nm_settings_connection_get_connection (parent_connection),
+		                                              NULL))
 			first_compatible = candidate;
 	}
 
@@ -1801,13 +1836,13 @@ const char *
 nm_manager_iface_for_uuid (NMManager *self, const char *uuid)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMSettingsConnection *connection;
+	NMSettingsConnection *sett_conn;
 
-	connection = nm_settings_get_connection_by_uuid (priv->settings, uuid);
-	if (!connection)
+	sett_conn = nm_settings_get_connection_by_uuid (priv->settings, uuid);
+	if (!sett_conn)
 		return NULL;
 
-	return nm_connection_get_interface_name (NM_CONNECTION (connection));
+	return nm_connection_get_interface_name (nm_settings_connection_get_connection (sett_conn));
 }
 
 NMDevice *
@@ -1877,7 +1912,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 
 	/* See if there's a device that is already compatible with this connection */
 	c_list_for_each_entry (dev_candidate, &priv->devices_lst_head, devices_lst) {
-		if (nm_device_check_connection_compatible (dev_candidate, connection)) {
+		if (nm_device_check_connection_compatible (dev_candidate, connection, NULL)) {
 			if (nm_device_is_real (dev_candidate)) {
 				_LOG3D (LOGD_DEVICE, connection, "already created virtual interface name %s",
 				       iface);
@@ -1930,10 +1965,10 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	                                                 NULL, NULL,
 	                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 	for (i = 0; connections[i]; i++) {
-		NMConnection *candidate = NM_CONNECTION (connections[i]);
+		NMConnection *candidate = nm_settings_connection_get_connection (connections[i]);
 		NMSettingConnection *s_con;
 
-		if (!nm_device_check_connection_compatible (device, candidate))
+		if (!nm_device_check_connection_compatible (device, candidate, NULL))
 			continue;
 
 		s_con = nm_connection_get_setting_connection (candidate);
@@ -1976,18 +2011,19 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 	                                                 NULL, NULL,
 	                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 	for (i = 0; connections[i]; i++) {
-		NMConnection *candidate = NM_CONNECTION (connections[i]);
+		NMSettingsConnection *sett_conn = connections[i];
+		NMConnection *connection = nm_settings_connection_get_connection (sett_conn);
 		gs_free_error GError *error = NULL;
 		gs_free char *ifname = NULL;
 		NMDevice *parent;
 
-		parent = find_parent_device_for_connection (self, candidate, NULL);
+		parent = find_parent_device_for_connection (self, connection, NULL);
 		if (parent == device) {
 			/* Only try to activate devices that don't already exist */
-			ifname = nm_manager_get_connection_iface (self, candidate, &parent, &error);
+			ifname = nm_manager_get_connection_iface (self, connection, &parent, &error);
 			if (ifname) {
 				if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname))
-					connection_changed (self, candidate);
+					connection_changed (self, sett_conn);
 			}
 		}
 	}
@@ -1995,9 +2031,10 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 
 static void
 connection_changed (NMManager *self,
-                    NMConnection *connection)
+                    NMSettingsConnection *sett_conn)
 {
 	NMDevice *device;
+	NMConnection *connection = nm_settings_connection_get_connection (sett_conn);
 
 	if (!nm_connection_is_virtual (connection))
 		return;
@@ -2014,20 +2051,20 @@ connection_changed (NMManager *self,
 
 static void
 connection_added_cb (NMSettings *settings,
-                     NMConnection *connection,
+                     NMSettingsConnection *sett_conn,
                      NMManager *self)
 {
-	connection_changed (self, connection);
+	connection_changed (self, sett_conn);
 }
 
 static void
 connection_updated_cb (NMSettings *settings,
-                       NMConnection *connection,
+                       NMSettingsConnection *sett_conn,
                        gboolean by_user,
                        NMManager *self)
 {
 	if (by_user)
-		connection_changed (self, connection);
+		connection_changed (self, sett_conn);
 }
 
 /*****************************************************************************/
@@ -2370,6 +2407,20 @@ done:
 	g_clear_error (&error);
 }
 
+static gboolean
+new_activation_allowed_for_connection (NMManager *self,
+                                       NMSettingsConnection *connection)
+{
+	if (NM_IN_SET (_nm_connection_get_multi_connect (nm_settings_connection_get_connection (connection)),
+	               NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE,
+	               NM_CONNECTION_MULTI_CONNECT_MULTIPLE))
+		return TRUE;
+
+	return !active_connection_find (self, connection, NULL,
+	                                NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+	                                NULL);
+}
+
 /**
  * get_existing_connection:
  * @manager: #NMManager instance
@@ -2386,12 +2437,12 @@ get_existing_connection (NMManager *self,
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	gs_unref_object NMConnection *connection = NULL;
-	NMSettingsConnection *added = NULL;
+	NMSettingsConnection *added;
 	GError *error = NULL;
 	gs_free_error GError *gen_error = NULL;
 	NMDevice *master = NULL;
 	int ifindex = nm_device_get_ifindex (device);
-	NMSettingsConnection *matched;
+	NMSettingsConnection *matched = NULL;
 	NMSettingsConnection *connection_checked = NULL;
 	gboolean assume_state_guess_assume = FALSE;
 	const char *assume_state_connection_uuid = NULL;
@@ -2457,28 +2508,25 @@ get_existing_connection (NMManager *self,
 	 */
 	if (   assume_state_connection_uuid
 	    && (connection_checked = nm_settings_get_connection_by_uuid (priv->settings, assume_state_connection_uuid))
-	    && !active_connection_find (self, connection_checked, NULL,
-	                                NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
-	                                NULL)
-	    && nm_device_check_connection_compatible (device, NM_CONNECTION (connection_checked))) {
+	    && new_activation_allowed_for_connection (self, connection_checked)
+	    && nm_device_check_connection_compatible (device,
+	                                              nm_settings_connection_get_connection (connection_checked),
+	                                              NULL)) {
 
 		if (connection) {
-			NMConnection *const connections[] = {
-				NM_CONNECTION (connection_checked),
-				NULL,
-			};
-
-			matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections,
-			                                                             connection,
-			                                                             TRUE,
-			                                                             nm_device_has_carrier (device),
-			                                                             nm_device_get_route_metric (device, AF_INET),
-			                                                             nm_device_get_route_metric (device, AF_INET6),
-			                                                             NULL, NULL));
+			NMConnection *con = nm_settings_connection_get_connection (connection_checked);
+
+			if (nm_utils_match_connection ((NMConnection *[]) { con, NULL },
+			                               connection,
+			                               TRUE,
+			                               nm_device_has_carrier (device),
+			                               nm_device_get_route_metric (device, AF_INET),
+			                               nm_device_get_route_metric (device, AF_INET6),
+			                               NULL, NULL))
+				matched = connection_checked;
 		} else
 			matched = connection_checked;
-	} else
-		matched = NULL;
+	}
 
 	if (!matched && only_by_uuid) {
 		_LOG2D (LOGD_DEVICE, device, "assume: cannot generate connection: %s",
@@ -2487,32 +2535,51 @@ get_existing_connection (NMManager *self,
 	}
 
 	if (!matched && assume_state_guess_assume) {
-		gs_free NMSettingsConnection **connections = NULL;
+		gs_free NMSettingsConnection **sett_conns = NULL;
 		guint len, i, j;
 
 		/* the state file doesn't indicate a connection UUID to assume. Search the
 		 * persistent connections for a matching candidate. */
-		connections = nm_manager_get_activatable_connections (self, &len, FALSE);
+		sett_conns = nm_manager_get_activatable_connections (self, FALSE, FALSE, &len);
 		if (len > 0) {
 			for (i = 0, j = 0; i < len; i++) {
-				NMConnection *con = NM_CONNECTION (connections[i]);
+				NMSettingsConnection *sett_conn = sett_conns[i];
 
-				if (   con != NM_CONNECTION (connection_checked)
-				    && nm_device_check_connection_compatible (device, con))
-					connections[j++] = connections[i];
+				if (   sett_conn != connection_checked
+				    && nm_device_check_connection_compatible (device,
+				                                              nm_settings_connection_get_connection (sett_conn),
+				                                              NULL))
+					sett_conns[j++] = sett_conn;
 			}
-			connections[j] = NULL;
+			sett_conns[j] = NULL;
 			len = j;
-			g_qsort_with_data (connections, len, sizeof (connections[0]),
-			                   nm_settings_connection_cmp_timestamp_p_with_data, NULL);
-
-			matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection ((NMConnection *const*) connections,
-			                                                             connection,
-			                                                             FALSE,
-			                                                             nm_device_has_carrier (device),
-			                                                             nm_device_get_route_metric (device, AF_INET),
-			                                                             nm_device_get_route_metric (device, AF_INET6),
-			                                                             NULL, NULL));
+			if (len > 0) {
+				gs_free NMConnection **conns = NULL;
+				NMConnection *con;
+
+				g_qsort_with_data (sett_conns, len, sizeof (sett_conns[0]),
+				                   nm_settings_connection_cmp_timestamp_p_with_data, NULL);
+
+				conns = nm_settings_connections_array_to_connections (sett_conns, len);
+
+				con = nm_utils_match_connection (conns,
+				                                 connection,
+				                                 FALSE,
+				                                 nm_device_has_carrier (device),
+				                                 nm_device_get_route_metric (device, AF_INET),
+				                                 nm_device_get_route_metric (device, AF_INET6),
+				                                 NULL,
+				                                 NULL);
+				if (con) {
+					for (i = 0; i < len; i++) {
+						if (conns[i] == con) {
+							matched = sett_conns[i];
+							break;
+						}
+					}
+					nm_assert (matched);
+				}
+			}
 		}
 	}
 
@@ -2541,7 +2608,7 @@ get_existing_connection (NMManager *self,
 		return NULL;
 	}
 
-	nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added),
+	nm_settings_connection_set_flags (added,
 	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED |
 	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
 	                                  TRUE);
@@ -2553,7 +2620,7 @@ static gboolean
 recheck_assume_connection (NMManager *self,
                            NMDevice *device)
 {
-	NMSettingsConnection *connection;
+	NMSettingsConnection *sett_conn;
 	gboolean was_unmanaged = FALSE;
 	gboolean generated = FALSE;
 	NMDeviceState state;
@@ -2575,9 +2642,9 @@ recheck_assume_connection (NMManager *self,
 		return FALSE;
 	}
 
-	connection = get_existing_connection (self, device, &generated);
+	sett_conn = get_existing_connection (self, device, &generated);
 	/* log  no reason. get_existing_connection() already does it. */
-	if (!connection)
+	if (!sett_conn)
 		return FALSE;
 
 	nm_device_sys_iface_state_set (device,
@@ -2609,7 +2676,8 @@ recheck_assume_connection (NMManager *self,
 		subject = nm_auth_subject_new_internal ();
 		active = _new_active_connection (self,
 		                                 FALSE,
-		                                 NM_CONNECTION (connection),
+		                                 sett_conn,
+		                                 NULL,
 		                                 NULL,
 		                                 NULL,
 		                                 device,
@@ -2620,7 +2688,7 @@ recheck_assume_connection (NMManager *self,
 
 		if (!active) {
 			_LOGW (LOGD_DEVICE, "assume: assumed connection %s failed to activate: %s",
-			       nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)),
+			       nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)),
 			       error->message);
 			g_error_free (error);
 
@@ -2632,7 +2700,7 @@ recheck_assume_connection (NMManager *self,
 
 			if (generated) {
 				_LOG2D (LOGD_DEVICE, device, "assume: deleting generated connection after assuming failed");
-				nm_settings_connection_delete (connection, NULL);
+				nm_settings_connection_delete (sett_conn, NULL);
 			} else {
 				if (nm_device_sys_iface_state_get (device) == NM_DEVICE_SYS_IFACE_STATE_ASSUME)
 					nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL);
@@ -2642,7 +2710,14 @@ recheck_assume_connection (NMManager *self,
 
 		/* If the device is a slave or VLAN, find the master ActiveConnection */
 		master_ac = NULL;
-		if (find_master (self, NM_CONNECTION (connection), device, NULL, NULL, &master_ac, NULL) && master_ac)
+		if (   find_master (self,
+		                    nm_settings_connection_get_connection (sett_conn),
+		                    device,
+		                    NULL,
+		                    NULL,
+		                    &master_ac,
+		                    NULL)
+		    && master_ac)
 			nm_active_connection_set_master (active, master_ac);
 
 		active_connection_add (self, active);
@@ -3008,10 +3083,21 @@ platform_link_added (NMManager *self,
 			continue;
 
 		if (nm_device_is_real (candidate)) {
-			/* Ignore the link added event since there's already a realized
-			 * device with the link's name.
+			/* There's already a realized device with the link's name
+			 * and a different ifindex.
 			 */
-			nm_device_update_from_platform_link (candidate, plink);
+			if (nm_device_get_ifindex (candidate) <= 0)
+				nm_device_update_from_platform_link (candidate, plink);
+			else {
+				/* The ifindex of a device can't be changed after
+				 * initialization because it is used as a key by
+				 * the dns-manager.
+				 */
+				_LOGD (LOGD_DEVICE, "(%s): removing old device %p after ifindex change from %d to %d",
+				       plink->name, candidate, nm_device_get_ifindex (candidate), ifindex);
+				remove_device (self, candidate, FALSE, TRUE);
+				goto add;
+			}
 			return;
 		} else if (nm_device_realize_start (candidate,
 		                                    plink,
@@ -3031,6 +3117,7 @@ platform_link_added (NMManager *self,
 		/* Try next unrealized device */
 	}
 
+add:
 	/* Try registered device factories */
 	factory = nm_device_factory_manager_find_factory_for_link_type (plink->type);
 	if (factory) {
@@ -3234,9 +3321,11 @@ nm_manager_get_devices (NMManager *manager)
 
 static NMDevice *
 nm_manager_get_best_device_for_connection (NMManager *self,
+                                           NMSettingsConnection *sett_conn,
                                            NMConnection *connection,
                                            gboolean for_user_request,
-                                           GHashTable *unavailable_devices)
+                                           GHashTable *unavailable_devices,
+                                           GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnectionState ac_state;
@@ -3245,16 +3334,25 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	NMDevice *device;
 	NMDeviceCheckConAvailableFlags flags;
 	gs_unref_ptrarray GPtrArray *all_ac_arr = NULL;
+	gs_free_error GError *local_best = NULL;
+
+	nm_assert (!sett_conn || NM_IS_SETTINGS_CONNECTION (sett_conn));
+	nm_assert (!connection || NM_IS_CONNECTION (connection));
+	nm_assert (sett_conn || connection);
+	nm_assert (!connection || !sett_conn || connection == nm_settings_connection_get_connection (sett_conn));
+
+	if (!connection)
+		connection = nm_settings_connection_get_connection (sett_conn);
 
 	flags = for_user_request ? NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST : NM_DEVICE_CHECK_CON_AVAILABLE_NONE;
 
-	ac = active_connection_find_by_connection (self, connection, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, &all_ac_arr);
+	ac = active_connection_find_by_connection (self, sett_conn, connection, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, &all_ac_arr);
 	if (ac) {
 
 		ac_device = nm_active_connection_get_device (ac);
 		if (   ac_device
 		    && (   (unavailable_devices && g_hash_table_contains (unavailable_devices, ac_device))
-		        || !nm_device_check_connection_available (ac_device, connection, flags, NULL)))
+		        || !nm_device_check_connection_available (ac_device, connection, flags, NULL, NULL)))
 			ac_device = NULL;
 
 		if (all_ac_arr) {
@@ -3271,7 +3369,7 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 
 				if (   !ac_device2
 				    || (unavailable_devices && g_hash_table_contains (unavailable_devices, ac_device2))
-				    || !nm_device_check_connection_available (ac_device2, connection, flags, NULL))
+				    || !nm_device_check_connection_available (ac_device2, connection, flags, NULL, NULL))
 					continue;
 
 				ac_state2 = nm_active_connection_get_state (ac2);
@@ -3319,15 +3417,53 @@ found_better:
 
 	/* Pick the first device that's compatible with the connection. */
 	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		GError *local = NULL;
 
-		if (unavailable_devices && g_hash_table_contains (unavailable_devices, device))
+		if (   unavailable_devices
+		    && g_hash_table_contains (unavailable_devices, device))
 			continue;
 
-		if (nm_device_check_connection_available (device, connection, flags, NULL))
+		if (nm_device_check_connection_available (device,
+		                                          connection,
+		                                          flags,
+		                                          NULL,
+		                                          error ? &local : NULL))
 			return device;
+
+		if (error) {
+			gboolean reset_error;
+
+			if (!local_best)
+				reset_error = TRUE;
+			else if (local_best->domain != NM_UTILS_ERROR)
+				reset_error = (local->domain == NM_UTILS_ERROR);
+			else {
+				reset_error = (   local->domain == NM_UTILS_ERROR
+			                   && local_best->code < local->code);
+			}
+
+			if (reset_error) {
+				g_clear_error (&local_best);
+				g_set_error (&local_best,
+				             local->domain,
+				             local->code,
+				             "device %s not available because %s",
+				             nm_device_get_iface (device),
+				             local->message);
+			}
+			g_error_free (local);
+		}
 	}
 
-	/* No luck. :( */
+	if (error) {
+		if (local_best)
+			g_propagate_error (error, g_steal_pointer (&local_best));
+		else {
+			nm_utils_error_set_literal (error,
+			                            NM_UTILS_ERROR_UNKNOWN,
+			                            "no suitable device found");
+		}
+	}
 	return NULL;
 }
 
@@ -3496,7 +3632,7 @@ find_master (NMManager *self,
 	NMSettingConnection *s_con;
 	const char *master;
 	NMDevice *master_device = NULL;
-	NMSettingsConnection *master_connection = NULL;
+	NMSettingsConnection *master_connection;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
@@ -3515,7 +3651,9 @@ find_master (NMManager *self,
 		}
 
 		master_connection = nm_device_get_settings_connection (master_device);
-		if (master_connection && !is_compatible_with_slave (NM_CONNECTION (master_connection), connection)) {
+		if (   master_connection
+		    && !is_compatible_with_slave (nm_settings_connection_get_connection (master_connection),
+		                                  connection)) {
 			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
 			             "The active connection on %s is not compatible",
 			             nm_device_get_iface (master_device));
@@ -3615,7 +3753,9 @@ ensure_master_active_connection (NMManager *self,
 		 * be already activated on the device, eg returned from find_master().
 		 */
 		g_assert (!master_connection || master_connection == device_connection);
-		if (device_connection && !is_compatible_with_slave (NM_CONNECTION (device_connection), connection)) {
+		if (   device_connection
+		    && !is_compatible_with_slave (nm_settings_connection_get_connection (device_connection),
+		                                  connection)) {
 			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
 			             "The active connection %s is not compatible",
 			             nm_connection_get_id (connection));
@@ -3640,17 +3780,22 @@ ensure_master_active_connection (NMManager *self,
 			g_assert (master_connection == NULL);
 
 			/* Find a compatible connection and activate this device using it */
-			connections = nm_manager_get_activatable_connections (self, NULL, TRUE);
+			connections = nm_manager_get_activatable_connections (self, FALSE, TRUE, NULL);
 			for (i = 0; connections[i]; i++) {
 				NMSettingsConnection *candidate = connections[i];
+				NMConnection *cand_conn = nm_settings_connection_get_connection (candidate);
 
 				/* Ensure eg bond/team slave and the candidate master is a
 				 * bond/team master
 				 */
-				if (!is_compatible_with_slave (NM_CONNECTION (candidate), connection))
+				if (!is_compatible_with_slave (cand_conn, connection))
 					continue;
 
-				if (nm_device_check_connection_available (master_device, NM_CONNECTION (candidate), NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL)) {
+				if (nm_device_check_connection_available (master_device,
+				                                          cand_conn,
+				                                          NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST,
+				                                          NULL,
+				                                          NULL)) {
 					master_ac = nm_manager_activate_connection (self,
 					                                            candidate,
 					                                            NULL,
@@ -3686,7 +3831,11 @@ ensure_master_active_connection (NMManager *self,
 				continue;
 			}
 
-			if (!nm_device_check_connection_available (candidate, NM_CONNECTION (master_connection), NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL))
+			if (!nm_device_check_connection_available (candidate,
+			                                           nm_settings_connection_get_connection (master_connection),
+			                                           NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST,
+			                                           NULL,
+			                                           NULL))
 				continue;
 
 			if (!nm_device_is_software (candidate)) {
@@ -3725,18 +3874,18 @@ typedef struct {
 /**
  * find_slaves:
  * @manager: #NMManager object
- * @connection: the master #NMSettingsConnection to find slave connections for
- * @device: the master #NMDevice for the @connection
+ * @sett_conn: the master #NMSettingsConnection to find slave connections for
+ * @device: the master #NMDevice for the @sett_conn
  * @out_n_slaves: on return, the number of slaves found
  *
- * Given an #NMSettingsConnection, attempts to find its slaves. If @connection is not
+ * Given an #NMSettingsConnection, attempts to find its slaves. If @sett_conn is not
  * master, or has not any slaves, this will return %NULL.
  *
- * Returns: an array of #SlaveConnectionInfo for given master @connection, or %NULL
+ * Returns: an array of #SlaveConnectionInfo for given master @sett_conn, or %NULL
  **/
 static SlaveConnectionInfo *
 find_slaves (NMManager *manager,
-             NMSettingsConnection *connection,
+             NMSettingsConnection *sett_conn,
              NMDevice *device,
              guint *out_n_slaves)
 {
@@ -3751,7 +3900,7 @@ find_slaves (NMManager *manager,
 
 	nm_assert (out_n_slaves);
 
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
+	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (sett_conn));
 	g_return_val_if_fail (s_con, NULL);
 
 	devices = g_hash_table_new (nm_direct_hash, NULL);
@@ -3766,15 +3915,23 @@ find_slaves (NMManager *manager,
 	for (i = 0; i < n_all_connections; i++) {
 		NMSettingsConnection *master_connection = NULL;
 		NMDevice *master_device = NULL, *slave_device;
-		NMConnection *candidate = NM_CONNECTION (all_connections[i]);
-
-		find_master (manager, candidate, NULL, &master_connection, &master_device, NULL, NULL);
-		if (   (master_connection && master_connection == connection)
+		NMSettingsConnection *candidate = all_connections[i];
+
+		find_master (manager,
+		             nm_settings_connection_get_connection (candidate),
+		             NULL,
+		             &master_connection,
+		             &master_device,
+		             NULL,
+		             NULL);
+		if (   (master_connection && master_connection == sett_conn)
 		    || (master_device && master_device == device)) {
 			slave_device = nm_manager_get_best_device_for_connection (manager,
 			                                                          candidate,
+			                                                          NULL,
 			                                                          FALSE,
-			                                                          devices);
+			                                                          devices,
+			                                                          NULL);
 
 			if (!slaves) {
 				/* what we allocate is quite likely much too large. Don't bother, it is only
@@ -3783,7 +3940,7 @@ find_slaves (NMManager *manager,
 			}
 
 			nm_assert (n_slaves < n_all_connections);
-			slaves[n_slaves].connection = NM_SETTINGS_CONNECTION (candidate),
+			slaves[n_slaves].connection = candidate,
 			slaves[n_slaves].device = slave_device,
 			n_slaves++;
 
@@ -3802,32 +3959,29 @@ static gboolean
 should_connect_slaves (NMConnection *connection, NMDevice *device)
 {
 	NMSettingConnection *s_con;
-	NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
-	gs_free char *value = NULL;
+	NMSettingConnectionAutoconnectSlaves val;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 
-	/* Check autoconnect-slaves property */
-	autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con);
-	if (autoconnect_slaves != NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT)
+	val = nm_setting_connection_get_autoconnect_slaves (s_con);
+	if (val != NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT)
 		goto out;
 
-	/* Check configuration default for autoconnect-slaves property */
-	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                               "connection.autoconnect-slaves", device);
-	if (value)
-		autoconnect_slaves = _nm_utils_ascii_str_to_int64 (value, 10, 0, 1, -1);
+	val = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                   "connection.autoconnect-slaves",
+	                                                   device,
+	                                                   0, 1, -1);
 
 out:
-	if (autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO)
+	if (val == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO)
 		return FALSE;
-	if (autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES)
+	if (val == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES)
 		return TRUE;
 	return FALSE;
 }
 
-static gint
+static int
 compare_slaves (gconstpointer a, gconstpointer b, gpointer sort_by_name)
 {
 	const SlaveConnectionInfo *a_info = a;
@@ -3855,7 +4009,8 @@ autoconnect_slaves (NMManager *self,
 {
 	GError *local_err = NULL;
 
-	if (should_connect_slaves (NM_CONNECTION (master_connection), master_device)) {
+	if (should_connect_slaves (nm_settings_connection_get_connection (master_connection),
+	                           master_device)) {
 		gs_free SlaveConnectionInfo *slaves = NULL;
 		guint i, n_slaves = 0;
 
@@ -3996,7 +4151,7 @@ active_connection_parent_active (NMActiveConnection *active,
 {
 	NMDevice *device = nm_active_connection_get_device (active);
 	GError *error = NULL;
-	NMSettingsConnection *connection;
+	NMSettingsConnection *sett_conn;
 	NMDevice *parent;
 
 	g_signal_handlers_disconnect_by_func (active,
@@ -4012,10 +4167,13 @@ active_connection_parent_active (NMActiveConnection *active,
 		return;
 	}
 
-	connection = nm_active_connection_get_settings_connection (active);
+	sett_conn = nm_active_connection_get_settings_connection (active);
 	parent = nm_active_connection_get_device (parent_ac);
 
-	if (!nm_device_create_and_realize (device, (NMConnection *) connection, parent, &error)) {
+	if (!nm_device_create_and_realize (device,
+	                                   nm_settings_connection_get_connection (sett_conn),
+	                                   parent,
+	                                   &error)) {
 		_LOGW (LOGD_CORE, "Could not realize device '%s': %s",
 		       nm_device_get_iface (device), error->message);
 		nm_active_connection_set_state_fail (active,
@@ -4033,11 +4191,13 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 {
 	NMDevice *device, *master_device = NULL;
 	NMConnection *applied;
-	NMSettingsConnection *connection;
+	NMSettingsConnection *sett_conn;
 	NMSettingsConnection *master_connection = NULL;
 	NMConnection *existing_connection = NULL;
 	NMActiveConnection *master_ac = NULL;
 	NMAuthSubject *subject;
+	GError *local = NULL;
+	NMConnectionMultiConnect multi_connect;
 
 	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
 	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE);
@@ -4048,8 +4208,8 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	device = nm_active_connection_get_device (active);
 	g_return_val_if_fail (device != NULL, FALSE);
 
-	connection = nm_active_connection_get_settings_connection (active);
-	nm_assert (connection);
+	sett_conn = nm_active_connection_get_settings_connection (active);
+	nm_assert (sett_conn);
 
 	applied = nm_active_connection_get_applied_connection (active);
 
@@ -4071,10 +4231,13 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	}
 
 	/* Final connection must be available on device */
-	if (!nm_device_check_connection_available (device, applied, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL)) {
+	if (!nm_device_check_connection_available (device, applied, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL, &local)) {
 		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
-		             "Connection '%s' is not available on the device %s at this time.",
-		             nm_settings_connection_get_id (connection), nm_device_get_iface (device));
+		             "Connection '%s' is not available on device %s because %s",
+		             nm_settings_connection_get_id (sett_conn),
+		             nm_device_get_iface (device),
+		             local->message);
+		g_error_free (local);
 		return FALSE;
 	}
 
@@ -4085,7 +4248,9 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	if (!nm_device_is_real (device)) {
 		NMDevice *parent;
 
-		parent = find_parent_device_for_connection (self, (NMConnection *) connection, NULL);
+		parent = find_parent_device_for_connection (self,
+		                                            nm_settings_connection_get_connection (sett_conn),
+		                                            NULL);
 
 		if (parent && !nm_device_is_real (parent)) {
 			NMSettingsConnection *parent_con;
@@ -4097,7 +4262,11 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 				return FALSE;
 			}
 
-			parent_ac = nm_manager_activate_connection (self, parent_con, NULL, NULL, parent,
+			parent_ac = nm_manager_activate_connection (self,
+			                                            parent_con,
+			                                            NULL,
+			                                            NULL,
+			                                            parent,
 			                                            subject,
 			                                            NM_ACTIVATION_TYPE_MANAGED,
 			                                            nm_active_connection_get_activation_reason (active),
@@ -4115,7 +4284,10 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 			nm_active_connection_set_parent (active, parent_ac);
 		} else {
 			/* We can realize now; no need to wait for a parent device. */
-			if (!nm_device_create_and_realize (device, (NMConnection *) connection, parent, error)) {
+			if (!nm_device_create_and_realize (device,
+			                                   nm_settings_connection_get_connection (sett_conn),
+			                                   parent,
+			                                   error)) {
 				g_prefix_error (error, "%s failed to create resources: ", nm_device_get_iface (device));
 				return FALSE;
 			}
@@ -4123,11 +4295,15 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	}
 
 	/* Try to find the master connection/device if the connection has a dependency */
-	if (!find_master (self, applied, device,
-	                  &master_connection, &master_device, &master_ac,
+	if (!find_master (self,
+	                  applied,
+	                  device,
+	                  &master_connection,
+	                  &master_device,
+	                  &master_ac,
 	                  error)) {
 		g_prefix_error (error, "Can not find a master for %s: ",
-		                nm_settings_connection_get_id (connection));
+		                nm_settings_connection_get_id (sett_conn));
 		return FALSE;
 	}
 
@@ -4137,21 +4313,23 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	if (master_connection || master_device) {
 		if (master_connection) {
 			_LOGD (LOGD_CORE, "Activation of '%s' requires master connection '%s'",
-			       nm_settings_connection_get_id (connection),
+			       nm_settings_connection_get_id (sett_conn),
 			       nm_settings_connection_get_id (master_connection));
 		}
 		if (master_device) {
 			_LOGD (LOGD_CORE, "Activation of '%s' requires master device '%s'",
-			       nm_settings_connection_get_id (connection),
+			       nm_settings_connection_get_id (sett_conn),
 			       nm_device_get_ip_iface (master_device));
 		}
 
 		/* Ensure eg bond slave and the candidate master is a bond master */
-		if (master_connection && !is_compatible_with_slave (NM_CONNECTION (master_connection), applied)) {
+		if (   master_connection
+		    && !is_compatible_with_slave (nm_settings_connection_get_connection (master_connection),
+		                                  applied)) {
 			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
 			             "The master connection '%s' is not compatible with '%s'",
 			             nm_settings_connection_get_id (master_connection),
-			             nm_settings_connection_get_id (connection));
+			             nm_settings_connection_get_id (sett_conn));
 			return FALSE;
 		}
 
@@ -4170,7 +4348,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 					                nm_device_get_ip_iface (device));
 				} else {
 					g_prefix_error (error, "Master connection '%s' can't be activated: ",
-					                nm_settings_connection_get_id (connection));
+					                nm_settings_connection_get_id (sett_conn));
 				}
 				return FALSE;
 			}
@@ -4187,22 +4365,30 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 
 		nm_active_connection_set_master (active, master_ac);
 		_LOGD (LOGD_CORE, "Activation of '%s' depends on active connection %p %s",
-		       nm_settings_connection_get_id (connection),
+		       nm_settings_connection_get_id (sett_conn),
 		       master_ac,
 		       nm_dbus_object_get_path (NM_DBUS_OBJECT  (master_ac)) ?: "");
 	}
 
 	/* Check slaves for master connection and possibly activate them */
-	autoconnect_slaves (self, connection, device, nm_active_connection_get_subject (active));
-
-	{
+	autoconnect_slaves (self, sett_conn, device, nm_active_connection_get_subject (active));
+
+	multi_connect = _nm_connection_get_multi_connect (nm_settings_connection_get_connection (sett_conn));
+	if (   multi_connect == NM_CONNECTION_MULTI_CONNECT_MULTIPLE
+	    || (   multi_connect == NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE
+	        && NM_IN_SET (nm_active_connection_get_activation_reason (active),
+	                      NM_ACTIVATION_REASON_ASSUME,
+	                      NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES,
+	                      NM_ACTIVATION_REASON_USER_REQUEST))) {
+		/* the profile can be activated multiple times. Proceed. */
+	} else {
 		gs_unref_ptrarray GPtrArray *all_ac_arr = NULL;
 		NMActiveConnection *ac;
 		guint i, n_all;
 
 		/* Disconnect the connection if already connected or queued for activation.
 		 * The connection cannot be active multiple times (at the same time).  */
-		ac = active_connection_find (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+		ac = active_connection_find (self, sett_conn, NULL, NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
 		                             &all_ac_arr);
 		if (ac) {
 			n_all = all_ac_arr ? all_ac_arr->len : ((guint) 1);
@@ -4265,7 +4451,7 @@ _internal_activate_generic (NMManager *self, NMActiveConnection *active, GError
 		 * is exported, make sure the manager's activating-connection property
 		 * is up-to-date.
 		 */
-		policy_activating_device_changed (G_OBJECT (priv->policy), NULL, self);
+		policy_activating_ac_changed (G_OBJECT (priv->policy), NULL, self);
 	}
 
 	return success;
@@ -4274,7 +4460,8 @@ _internal_activate_generic (NMManager *self, NMActiveConnection *active, GError
 static NMActiveConnection *
 _new_active_connection (NMManager *self,
                         gboolean is_vpn,
-                        NMConnection *connection,
+                        NMSettingsConnection *sett_conn,
+                        NMConnection *incompl_conn,
                         NMConnection *applied,
                         const char *specific_object,
                         NMDevice *device,
@@ -4284,18 +4471,19 @@ _new_active_connection (NMManager *self,
                         GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMSettingsConnection *settings_connection = NULL;
 	NMDevice *parent_device;
 
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
-
-	nm_assert (is_vpn == _connection_is_vpn (connection));
+	nm_assert (!sett_conn || NM_IS_SETTINGS_CONNECTION (sett_conn));
+	nm_assert (!incompl_conn || NM_IS_CONNECTION (incompl_conn));
+	nm_assert ((!incompl_conn) ^ (!sett_conn));
+	nm_assert (NM_IS_AUTH_SUBJECT (subject));
+	nm_assert (is_vpn == _connection_is_vpn (sett_conn
+	                                         ? nm_settings_connection_get_connection (sett_conn)
+	                                         : incompl_conn));
 	nm_assert (is_vpn || NM_IS_DEVICE (device));
 	nm_assert (!nm_streq0 (specific_object, "/"));
-
-	if (NM_IS_SETTINGS_CONNECTION (connection))
-		settings_connection = (NMSettingsConnection *) connection;
+	nm_assert (!applied || NM_IS_CONNECTION (applied));
+	nm_assert (!is_vpn || !applied);
 
 	if (is_vpn) {
 		NMActiveConnection *parent;
@@ -4303,21 +4491,20 @@ _new_active_connection (NMManager *self,
 		/* FIXME: for VPN connections, we don't allow re-activating an
 		 * already active connection. It's a bug, and should be fixed together
 		 * when reworking VPN handling. */
-		if (active_connection_find_by_connection (self, connection, NM_ACTIVE_CONNECTION_STATE_ACTIVATED, NULL)) {
+		if (active_connection_find_by_connection (self,
+		                                          sett_conn,
+		                                          incompl_conn,
+		                                          NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+		                                          NULL)) {
 			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
 			             "Connection '%s' is already active",
-			             nm_connection_get_id (connection));
+			             sett_conn ? nm_settings_connection_get_id (sett_conn) : nm_connection_get_id (incompl_conn));
 			return NULL;
 		}
 
-		/* FIXME: apparently, activation here only works if @connection is
-		 * a settings-connection. Which is not the case during AddAndActivatate.
-		 * Probably, AddAndActivate is broken for VPN. */
 		if (activation_type != NM_ACTIVATION_TYPE_MANAGED)
 			g_return_val_if_reached (NULL);
 
-		g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
-
 		if (specific_object) {
 			/* Find the specific connection the client requested we use */
 			parent = active_connection_get_by_path (self, specific_object);
@@ -4348,14 +4535,14 @@ _new_active_connection (NMManager *self,
 			return NULL;
 		}
 
-		return (NMActiveConnection *) nm_vpn_connection_new (settings_connection,
+		return (NMActiveConnection *) nm_vpn_connection_new (sett_conn,
 		                                                     parent_device,
 		                                                     nm_dbus_object_get_path (NM_DBUS_OBJECT (parent)),
 		                                                     activation_reason,
 		                                                     subject);
 	}
 
-	return (NMActiveConnection *) nm_act_request_new (settings_connection,
+	return (NMActiveConnection *) nm_act_request_new (sett_conn,
 	                                                  applied,
 	                                                  specific_object,
 	                                                  subject,
@@ -4416,17 +4603,17 @@ fail:
 /**
  * nm_manager_activate_connection():
  * @self: the #NMManager
- * @connection: the #NMSettingsConnection to activate on @device
+ * @sett_conn: the #NMSettingsConnection to activate on @device
  * @applied: (allow-none): the applied connection to activate on @device
  * @specific_object: the specific object path, if any, for the activation
- * @device: the #NMDevice to activate @connection on. Can be %NULL for VPNs.
+ * @device: the #NMDevice to activate @sett_conn on. Can be %NULL for VPNs.
  * @subject: the subject which requested activation
  * @activation_type: whether to assume the connection. That is, take over gracefully,
  *   non-destructible.
  * @activation_reason: the reason for activation
  * @error: return location for an error
  *
- * Begins a new internally-initiated activation of @connection on @device.
+ * Begins a new internally-initiated activation of @sett_conn on @device.
  * @subject should be the subject of the activation that triggered this
  * one, or if this is an autoconnect request, a new internal subject.
  * The returned #NMActiveConnection is owned by the Manager and should be
@@ -4434,11 +4621,11 @@ fail:
  * is supplied, it shall not be modified by the caller afterwards.
  *
  * Returns: (transfer none): the new #NMActiveConnection that tracks
- * activation of @connection on @device
+ * activation of @sett_conn on @device
  */
 NMActiveConnection *
 nm_manager_activate_connection (NMManager *self,
-                                NMSettingsConnection *connection,
+                                NMSettingsConnection *sett_conn,
                                 NMConnection *applied,
                                 const char *specific_object,
                                 NMDevice *device,
@@ -4453,15 +4640,15 @@ nm_manager_activate_connection (NMManager *self,
 	gboolean is_vpn;
 
 	g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), NULL);
-	is_vpn = _connection_is_vpn (NM_CONNECTION (connection));
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn), NULL);
+	is_vpn = _connection_is_vpn (nm_settings_connection_get_connection (sett_conn));
 	g_return_val_if_fail (is_vpn || NM_IS_DEVICE (device), NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
 	nm_assert (!nm_streq0 (specific_object, "/"));
 
 	priv = NM_MANAGER_GET_PRIVATE (self);
 
-	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
+	if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (sett_conn),
 	                                          subject,
 	                                          NM_MANAGER_ERROR,
 	                                          NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -4479,7 +4666,7 @@ nm_manager_activate_connection (NMManager *self,
 			continue;
 
 		active = async_op_data->ac_auth.active;
-		if (   connection == nm_active_connection_get_settings_connection (active)
+		if (   sett_conn == nm_active_connection_get_settings_connection (active)
 		    && nm_streq0 (nm_active_connection_get_specific_object (active), specific_object)
 		    && (!device || nm_active_connection_get_device (active) == device)
 		    && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))
@@ -4490,7 +4677,8 @@ nm_manager_activate_connection (NMManager *self,
 
 	active = _new_active_connection (self,
 	                                 is_vpn,
-	                                 NM_CONNECTION (connection),
+	                                 sett_conn,
+	                                 NULL,
 	                                 applied,
 	                                 specific_object,
 	                                 device,
@@ -4513,7 +4701,9 @@ nm_manager_activate_connection (NMManager *self,
  * validate_activation_request:
  * @self: the #NMManager
  * @context: the D-Bus context of the requestor
- * @connection: the partial or complete #NMConnection to be activated
+ * @sett_conn: the #NMSettingsConnection to be activated, or %NULL if there
+ *   is only a partial activation.
+ * @connection: the partial #NMConnection to be activated (if @sett_conn is unspecified)
  * @device_path: the object path of the device to be activated, or NULL
  * @out_device: on successful reutrn, the #NMDevice to be activated with @connection
  *   The caller may pass in a device which shortcuts the lookup by path.
@@ -4532,6 +4722,7 @@ nm_manager_activate_connection (NMManager *self,
 static NMAuthSubject *
 validate_activation_request (NMManager *self,
                              GDBusMethodInvocation *context,
+                             NMSettingsConnection *sett_conn,
                              NMConnection *connection,
                              const char *device_path,
                              NMDevice **out_device,
@@ -4542,10 +4733,16 @@ validate_activation_request (NMManager *self,
 	gboolean is_vpn = FALSE;
 	gs_unref_object NMAuthSubject *subject = NULL;
 
-	nm_assert (NM_IS_CONNECTION (connection));
+	nm_assert (!sett_conn || NM_IS_SETTINGS_CONNECTION (sett_conn));
+	nm_assert (!connection || NM_IS_CONNECTION (connection));
+	nm_assert (sett_conn || connection);
+	nm_assert (!connection || !sett_conn || connection == nm_settings_connection_get_connection (sett_conn));
 	nm_assert (out_device);
 	nm_assert (out_is_vpn);
 
+	if (!connection)
+		connection = nm_settings_connection_get_connection (sett_conn);
+
 	/* Validate the caller */
 	subject = nm_auth_subject_new_unix_process_from_context (context);
 	if (!subject) {
@@ -4581,17 +4778,20 @@ validate_activation_request (NMManager *self,
 			return NULL;
 		}
 	} else if (!is_vpn) {
-		device = nm_manager_get_best_device_for_connection (self, connection, TRUE, NULL);
+		gs_free_error GError *local = NULL;
+
+		device = nm_manager_get_best_device_for_connection (self, sett_conn, connection, TRUE, NULL, &local);
 		if (!device) {
 			gs_free char *iface = NULL;
 
 			/* VPN and software-device connections don't need a device yet,
 			 * but non-virtual connections do ... */
 			if (!nm_connection_is_virtual (connection)) {
-				g_set_error_literal (error,
-				                     NM_MANAGER_ERROR,
-				                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				                     "No suitable device found for this connection.");
+				g_set_error (error,
+				             NM_MANAGER_ERROR,
+				             NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+				             "No suitable device found for this connection (%s).",
+				             local->message);
 				return NULL;
 			}
 
@@ -4677,7 +4877,7 @@ impl_manager_activate_connection (NMDBusObject *obj,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	gs_unref_object NMActiveConnection *active = NULL;
 	gs_unref_object NMAuthSubject *subject = NULL;
-	NMSettingsConnection *connection = NULL;
+	NMSettingsConnection *sett_conn = NULL;
 	NMDevice *device = NULL;
 	gboolean is_vpn = FALSE;
 	GError *error = NULL;
@@ -4697,8 +4897,8 @@ impl_manager_activate_connection (NMDBusObject *obj,
 	 * (since this is an explicit request, not an auto-activation request).
 	 */
 	if (connection_path) {
-		connection = nm_settings_get_connection_by_path (priv->settings, connection_path);
-		if (!connection) {
+		sett_conn = nm_settings_get_connection_by_path (priv->settings, connection_path);
+		if (!sett_conn) {
 			error = g_error_new_literal (NM_MANAGER_ERROR,
 			                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
 			                             "Connection could not be found.");
@@ -4718,14 +4918,15 @@ impl_manager_activate_connection (NMDBusObject *obj,
 			goto error;
 		}
 
-		connection = nm_device_get_best_connection (device, specific_object_path, &error);
-		if (!connection)
+		sett_conn = nm_device_get_best_connection (device, specific_object_path, &error);
+		if (!sett_conn)
 			goto error;
 	}
 
 	subject = validate_activation_request (self,
 	                                       invocation,
-	                                       NM_CONNECTION (connection),
+	                                       sett_conn,
+	                                       NULL,
 	                                       device_path,
 	                                       &device,
 	                                       &is_vpn,
@@ -4735,7 +4936,8 @@ impl_manager_activate_connection (NMDBusObject *obj,
 
 	active = _new_active_connection (self,
 	                                 is_vpn,
-	                                 NM_CONNECTION (connection),
+	                                 sett_conn,
+	                                 NULL,
 	                                 NULL,
 	                                 specific_object_path,
 	                                 device,
@@ -4759,8 +4961,8 @@ impl_manager_activate_connection (NMDBusObject *obj,
 	return;
 
 error:
-	if (connection) {
-		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
+	if (sett_conn) {
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, sett_conn, FALSE, NULL,
 		                            subject, error->message);
 	}
 	g_dbus_method_invocation_take_error (invocation, error);
@@ -4875,7 +5077,7 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 {
 	NMManager *self = NM_MANAGER (obj);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	gs_unref_object NMConnection *connection = NULL;
+	gs_unref_object NMConnection *incompl_conn = NULL;
 	NMActiveConnection *active = NULL;
 	gs_unref_object NMAuthSubject *subject = NULL;
 	GError *error = NULL;
@@ -4884,6 +5086,7 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 	gs_unref_variant GVariant *settings = NULL;
 	const char *device_path;
 	const char *specific_object_path;
+	gs_free NMConnection **conns = NULL;
 
 	g_variant_get (parameters, "(@a{sa{sv}}&o&o)", &settings, &device_path, &specific_object_path);
 
@@ -4897,13 +5100,14 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 	 * specific data being in the connection till then (especially in
 	 * validate_activation_request()).
 	 */
-	connection = nm_simple_connection_new ();
+	incompl_conn = nm_simple_connection_new ();
 	if (settings && g_variant_n_children (settings))
-		_nm_connection_replace_settings (connection, settings, NM_SETTING_PARSE_FLAGS_STRICT, NULL);
+		_nm_connection_replace_settings (incompl_conn, settings, NM_SETTING_PARSE_FLAGS_STRICT, NULL);
 
 	subject = validate_activation_request (self,
 	                                       invocation,
-	                                       connection,
+	                                       NULL,
+	                                       incompl_conn,
 	                                       device_path,
 	                                       &device,
 	                                       &is_vpn,
@@ -4913,7 +5117,7 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 
 	if (is_vpn) {
 		/* Try to fill the VPN's connection setting and name at least */
-		if (!nm_connection_get_setting_vpn (connection)) {
+		if (!nm_connection_get_setting_vpn (incompl_conn)) {
 			error = g_error_new_literal (NM_CONNECTION_ERROR,
 			                             NM_CONNECTION_ERROR_MISSING_SETTING,
 			                             "VPN connections require a 'vpn' setting");
@@ -4921,27 +5125,31 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 			goto error;
 		}
 
+		conns = nm_settings_connections_array_to_connections (nm_settings_get_connections (priv->settings, NULL), -1);
+
 		nm_utils_complete_generic (priv->platform,
-		                           connection,
+		                           incompl_conn,
 		                           NM_SETTING_VPN_SETTING_NAME,
-		                           (NMConnection *const*) nm_settings_get_connections (priv->settings, NULL),
+		                           conns,
 		                           NULL,
 		                           _("VPN connection"),
 		                           NULL,
 		                           FALSE); /* No IPv6 by default for now */
 	} else {
+		conns = nm_settings_connections_array_to_connections (nm_settings_get_connections (priv->settings, NULL), -1);
 		/* Let each device subclass complete the connection */
 		if (!nm_device_complete_connection (device,
-		                                    connection,
+		                                    incompl_conn,
 		                                    specific_object_path,
-		                                    (NMConnection *const*) nm_settings_get_connections (priv->settings, NULL),
+		                                    conns,
 		                                    &error))
 			goto error;
 	}
 
 	active = _new_active_connection (self,
 	                                 is_vpn,
-	                                 connection,
+	                                 NULL,
+	                                 incompl_conn,
 	                                 NULL,
 	                                 specific_object_path,
 	                                 device,
@@ -4952,15 +5160,16 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 	if (!active)
 		goto error;
 
-	nm_active_connection_authorize (active, connection,
+	nm_active_connection_authorize (active,
+	                                incompl_conn,
 	                                _async_op_complete_ac_auth_cb,
 	                                _async_op_data_new_ac_auth_add_and_activate (self,
 	                                                                             active,
 	                                                                             invocation,
-	                                                                             connection));
+	                                                                             incompl_conn));
 
 	/* we passed the pointers on to _async_op_data_new_ac_auth_add_and_activate() */
-	g_steal_pointer (&connection);
+	g_steal_pointer (&incompl_conn);
 	g_steal_pointer (&active);
 	return;
 
@@ -5077,7 +5286,7 @@ impl_manager_deactivate_connection (NMDBusObject *obj,
 	NMManager *self = NM_MANAGER (obj);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *ac;
-	NMSettingsConnection *connection = NULL;
+	NMSettingsConnection *sett_conn = NULL;
 	GError *error = NULL;
 	NMAuthSubject *subject = NULL;
 	NMAuthChain *chain;
@@ -5088,9 +5297,9 @@ impl_manager_deactivate_connection (NMDBusObject *obj,
 	/* Find the connection by its object path */
 	ac = active_connection_get_by_path (self, active_path);
 	if (ac)
-		connection = nm_active_connection_get_settings_connection (ac);
+		sett_conn = nm_active_connection_get_settings_connection (ac);
 
-	if (!connection) {
+	if (!sett_conn) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
 		                             "The connection was not active.");
@@ -5106,7 +5315,7 @@ impl_manager_deactivate_connection (NMDBusObject *obj,
 		goto done;
 	}
 
-	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
+	if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (sett_conn),
 	                                          subject,
 	                                          NM_MANAGER_ERROR,
 	                                          NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -5128,8 +5337,9 @@ impl_manager_deactivate_connection (NMDBusObject *obj,
 
 done:
 	if (error) {
-		if (connection) {
-			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE, NULL,
+		if (sett_conn) {
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE,
+			                            sett_conn, FALSE, NULL,
 			                            subject, error->message);
 		}
 		g_dbus_method_invocation_take_error (invocation, error);
@@ -5878,66 +6088,76 @@ start_factory (NMDeviceFactory *factory, gpointer user_data)
 	nm_device_factory_start (factory);
 }
 
-void
-nm_manager_write_device_state (NMManager *self)
+gboolean
+nm_manager_write_device_state (NMManager *self, NMDevice *device)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
-	gs_unref_hashtable GHashTable *seen_ifindexes = NULL;
-	gint nm_owned;
+	int ifindex;
+	gboolean managed;
+	NMConfigDeviceStateManagedType managed_type;
+	const char *uuid = NULL;
+	const char *perm_hw_addr_fake = NULL;
+	gboolean perm_hw_addr_is_fake;
+	guint32 route_metric_default_aspired;
+	guint32 route_metric_default_effective;
+	int nm_owned;
+
+	ifindex = nm_device_get_ip_ifindex (device);
+	if (ifindex <= 0)
+		return FALSE;
+	if (ifindex == 1) {
+		/* ignore loopback */
+		return FALSE;
+	}
 
-	seen_ifindexes = g_hash_table_new (nm_direct_hash, NULL);
+	if (!nm_platform_link_get (priv->platform, ifindex))
+		return FALSE;
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		int ifindex;
-		gboolean managed;
-		NMConfigDeviceStateManagedType managed_type;
-		NMConnection *settings_connection;
-		const char *uuid = NULL;
-		const char *perm_hw_addr_fake = NULL;
-		gboolean perm_hw_addr_is_fake;
-		guint32 route_metric_default_aspired;
-		guint32 route_metric_default_effective;
-
-		ifindex = nm_device_get_ip_ifindex (device);
-		if (ifindex <= 0)
-			continue;
-		if (ifindex == 1) {
-			/* ignore loopback */
-			continue;
-		}
+	managed = nm_device_get_managed (device, FALSE);
+	if (managed) {
+		NMSettingsConnection *sett_conn;
 
-		if (!nm_platform_link_get (priv->platform, ifindex))
-			continue;
+		sett_conn = nm_device_get_settings_connection (device);
+		if (sett_conn)
+			uuid = nm_settings_connection_get_uuid (sett_conn);
+		managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
+	} else if (nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT))
+		managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
+	else
+		managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN;
 
-		managed = nm_device_get_managed (device, FALSE);
-		if (managed) {
-			settings_connection = NM_CONNECTION (nm_device_get_settings_connection (device));
-			if (settings_connection)
-				uuid = nm_connection_get_uuid (settings_connection);
-			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
-		} else if (nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT))
-			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
-		else
-			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN;
+	perm_hw_addr_fake = nm_device_get_permanent_hw_address_full (device, FALSE, &perm_hw_addr_is_fake);
+	if (perm_hw_addr_fake && !perm_hw_addr_is_fake)
+		perm_hw_addr_fake = NULL;
+
+	nm_owned = nm_device_is_software (device) ? nm_device_is_nm_owned (device) : -1;
+
+	route_metric_default_effective = _device_route_metric_get (self, ifindex, NM_DEVICE_TYPE_UNKNOWN,
+	                                                           TRUE, &route_metric_default_aspired);
 
-		perm_hw_addr_fake = nm_device_get_permanent_hw_address_full (device, FALSE, &perm_hw_addr_is_fake);
-		if (perm_hw_addr_fake && !perm_hw_addr_is_fake)
-			perm_hw_addr_fake = NULL;
+	return nm_config_device_state_write (ifindex,
+	                                     managed_type,
+	                                     perm_hw_addr_fake,
+	                                     uuid,
+	                                     nm_owned,
+	                                     route_metric_default_aspired,
+	                                     route_metric_default_effective);
+}
 
-		nm_owned = nm_device_is_software (device) ? nm_device_is_nm_owned (device) : -1;
+void
+nm_manager_write_device_state_all (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	gs_unref_hashtable GHashTable *seen_ifindexes = NULL;
+	NMDevice *device;
 
-		route_metric_default_effective = _device_route_metric_get (self, ifindex, NM_DEVICE_TYPE_UNKNOWN,
-		                                                           TRUE, &route_metric_default_aspired);
+	seen_ifindexes = g_hash_table_new (nm_direct_hash, NULL);
 
-		if (nm_config_device_state_write (ifindex,
-		                                  managed_type,
-		                                  perm_hw_addr_fake,
-		                                  uuid,
-		                                  nm_owned,
-		                                  route_metric_default_aspired,
-		                                  route_metric_default_effective))
-			g_hash_table_add (seen_ifindexes, GINT_TO_POINTER (ifindex));
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		if (nm_manager_write_device_state (self, device)) {
+			g_hash_table_add (seen_ifindexes,
+			                  GINT_TO_POINTER (nm_device_get_ip_ifindex (device)));
+		}
 	}
 
 	nm_config_device_state_prune_unseen (seen_ifindexes);
@@ -6017,7 +6237,7 @@ nm_manager_start (NMManager *self, GError **error)
 	                                                 NULL, NULL,
 	                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 	for (i = 0; connections[i]; i++)
-		connection_changed (self, NM_CONNECTION (connections[i]));
+		connection_changed (self, connections[i]);
 
 	nm_clear_g_source (&priv->devices_inited_id);
 	priv->devices_inited_id = g_idle_add_full (G_PRIORITY_LOW + 10, devices_inited_cb, self, NULL);
@@ -6117,25 +6337,19 @@ connection_metered_changed (GObject *object,
 }
 
 static void
-policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
+policy_default_ac_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *best;
 	NMActiveConnection *ac;
 
 	/* Note: this assumes that it's not possible for the IP4 default
 	 * route to be going over the default-ip6-device. If that changes,
 	 * we need something more complicated here.
 	 */
-	best = nm_policy_get_default_ip4_device (priv->policy);
-	if (!best)
-		best = nm_policy_get_default_ip6_device (priv->policy);
-
-	if (best)
-		ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (best));
-	else
-		ac = NULL;
+	ac = nm_policy_get_default_ip4_ac (priv->policy);
+	if (!ac)
+		ac = nm_policy_get_default_ip6_ac (priv->policy);
 
 	if (ac != priv->primary_connection) {
 		if (priv->primary_connection) {
@@ -6148,10 +6362,12 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user
 		priv->primary_connection = ac ? g_object_ref (ac) : NULL;
 
 		if (priv->primary_connection) {
-			g_signal_connect (priv->primary_connection, NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED,
+			g_signal_connect (priv->primary_connection,
+			                  NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED,
 			                  G_CALLBACK (connection_metered_changed), self);
 		}
-		_LOGD (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
+		_LOGD (LOGD_CORE, "PrimaryConnection now %s",
+		       ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
 		_notify (self, PROP_PRIMARY_CONNECTION);
 		_notify (self, PROP_PRIMARY_CONNECTION_TYPE);
 		nm_manager_update_metered (self);
@@ -6159,34 +6375,29 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user
 }
 
 static void
-policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
+policy_activating_ac_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *activating, *best;
-	NMActiveConnection *ac;
+	NMActiveConnection *activating, *best;
 
-	/* We only look at activating-ip6-device if activating-ip4-device
-	 * AND default-ip4-device are NULL; if default-ip4-device is
-	 * non-NULL, then activating-ip6-device is irrelevant, since while
-	 * that device might become the new default-ip6-device, it can't
-	 * become primary-connection while default-ip4-device is set to
+	/* We only look at activating-ip6-ac if activating-ip4-ac
+	 * AND default-ip4-ac are NULL; if default-ip4-ac is
+	 * non-NULL, then activating-ip6-ac is irrelevant, since while
+	 * that AC might become the new default-ip6-ac, it can't
+	 * become primary-connection while default-ip4-ac is set to
 	 * something else.
 	 */
-	activating = nm_policy_get_activating_ip4_device (priv->policy);
-	best = nm_policy_get_default_ip4_device (priv->policy);
+	activating = nm_policy_get_activating_ip4_ac (priv->policy);
+	best = nm_policy_get_default_ip4_ac (priv->policy);
 	if (!activating && !best)
-		activating = nm_policy_get_activating_ip6_device (priv->policy);
+		activating = nm_policy_get_activating_ip6_ac (priv->policy);
 
-	if (activating)
-		ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (activating));
-	else
-		ac = NULL;
-
-	if (ac != priv->activating_connection) {
-		g_clear_object (&priv->activating_connection);
-		priv->activating_connection = ac ? g_object_ref (ac) : NULL;
-		_LOGD (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
+	if (nm_g_object_ref_set (&priv->activating_connection, activating)) {
+		_LOGD (LOGD_CORE, "ActivatingConnection now %s",
+		       activating
+		           ? nm_active_connection_get_settings_connection_id (activating)
+		           : "(none)");
 		_notify (self, PROP_ACTIVATING_CONNECTION);
 	}
 }
@@ -6691,12 +6902,12 @@ nm_manager_set_capability (NMManager *self,
 
 	priv = NM_MANAGER_GET_PRIVATE (self);
 
-	idx = _nm_utils_array_find_binary_search (&g_array_index (priv->capabilities, guint32, 0),
-	                                          sizeof (guint32),
-	                                          priv->capabilities->len,
-	                                          &cap_i,
-	                                          nm_cmp_uint32_p_with_data,
-	                                          NULL);
+	idx = nm_utils_array_find_binary_search (&g_array_index (priv->capabilities, guint32, 0),
+	                                         sizeof (guint32),
+	                                         priv->capabilities->len,
+	                                         &cap_i,
+	                                         nm_cmp_uint32_p_with_data,
+	                                         NULL);
 	if (idx >= 0)
 		return;
 
@@ -6777,14 +6988,14 @@ constructed (GObject *object)
 	 */
 
 	priv->policy = nm_policy_new (self, priv->settings);
-	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE,
-	                  G_CALLBACK (policy_default_device_changed), self);
-	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_DEVICE,
-	                  G_CALLBACK (policy_default_device_changed), self);
-	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_DEVICE,
-	                  G_CALLBACK (policy_activating_device_changed), self);
-	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE,
-	                  G_CALLBACK (policy_activating_device_changed), self);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_AC,
+	                  G_CALLBACK (policy_default_ac_changed), self);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_AC,
+	                  G_CALLBACK (policy_default_ac_changed), self);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_AC,
+	                  G_CALLBACK (policy_activating_ac_changed), self);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_AC,
+	                  G_CALLBACK (policy_activating_ac_changed), self);
 
 	priv->config = g_object_ref (nm_config_get ());
 	g_signal_connect (G_OBJECT (priv->config),
@@ -7122,8 +7333,8 @@ dispose (GObject *object)
 	}
 
 	if (priv->policy) {
-		g_signal_handlers_disconnect_by_func (priv->policy, policy_default_device_changed, self);
-		g_signal_handlers_disconnect_by_func (priv->policy, policy_activating_device_changed, self);
+		g_signal_handlers_disconnect_by_func (priv->policy, policy_default_ac_changed, self);
+		g_signal_handlers_disconnect_by_func (priv->policy, policy_activating_ac_changed, self);
 		g_clear_object (&priv->policy);
 	}