about 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.c1053
1 files changed, 510 insertions, 543 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 0fa88d1d..080cdb8b 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -26,8 +26,6 @@
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <dbus/dbus-glib-lowlevel.h>
 #include <dbus/dbus-glib.h>
 #include <gio/gio.h>
@@ -129,8 +127,6 @@ static NMActiveConnection *_new_active_connection (NMManager *self,
 
 static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data);
 
-static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface);
-
 static void rfkill_change (const char *desc, RfKillType rtype, gboolean enabled);
 
 static gboolean find_master (NMManager *self,
@@ -161,25 +157,22 @@ typedef struct {
 	char *state_file;
 
 	GSList *active_connections;
+	GSList *authorizing_connections;
 	guint ac_cleanup_id;
 	NMActiveConnection *primary_connection;
 	NMActiveConnection *activating_connection;
 
 	GSList *devices;
 	NMState state;
+	NMConfig *config;
 	NMConnectivity *connectivity;
 
-	int ignore_link_added_cb;
-
 	NMPolicy *policy;
 
 	NMDBusManager *dbus_mgr;
 	gboolean       prop_filter_added;
 	NMRfkillManager *rfkill_mgr;
 
-	/* List of NMDeviceFactoryFunc pointers sorted in priority order */
-	GSList *factories;
-
 	NMSettings *settings;
 	char *hostname;
 
@@ -285,7 +278,8 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 
 		g_object_unref (active);
 
-		if (connection) {
+		if (   connection
+		    && nm_settings_has_connection (priv->settings, connection)) {
 			nm_log_dbg (LOGD_DEVICE, "Assumed connection disconnected. Deleting generated connection '%s' (%s)",
 			            nm_connection_get_id (connection), nm_connection_get_uuid (connection));
 			nm_settings_connection_delete (NM_SETTINGS_CONNECTION (connection), NULL, NULL);
@@ -463,20 +457,18 @@ active_connection_get_by_path (NMManager *manager, const char *path)
 
 /************************************************************************/
 
-static NMDevice *
-nm_manager_get_device_by_udi (NMManager *manager, const char *udi)
+static void
+_config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, NMManager *self)
 {
-	GSList *iter;
-
-	g_return_val_if_fail (udi != NULL, NULL);
-
-	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
-		if (!strcmp (nm_device_get_udi (NM_DEVICE (iter->data)), udi))
-			return NM_DEVICE (iter->data);
-	}
-	return NULL;
+	g_object_set (NM_MANAGER_GET_PRIVATE (self)->connectivity,
+	              NM_CONNECTIVITY_URI, nm_config_data_get_connectivity_uri (config_data),
+	              NM_CONNECTIVITY_INTERVAL, nm_config_data_get_connectivity_interval (config_data),
+	              NM_CONNECTIVITY_RESPONSE, nm_config_data_get_connectivity_response (config_data),
+	              NULL);
 }
 
+/************************************************************************/
+
 static NMDevice *
 nm_manager_get_device_by_path (NMManager *manager, const char *path)
 {
@@ -506,6 +498,50 @@ nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex)
 	return NULL;
 }
 
+static NMDevice *
+find_device_by_hw_addr (NMManager *manager, const char *hwaddr)
+{
+	GSList *iter;
+	const char *device_addr;
+
+	g_return_val_if_fail (hwaddr != NULL, NULL);
+
+	if (nm_utils_hwaddr_valid (hwaddr, -1)) {
+		for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
+			device_addr = nm_device_get_hw_address (NM_DEVICE (iter->data));
+			if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1))
+				return NM_DEVICE (iter->data);
+		}
+	}
+	return NULL;
+}
+
+static NMDevice *
+find_device_by_ip_iface (NMManager *self, const gchar *iface)
+{
+	GSList *iter;
+
+	g_return_val_if_fail (iface != NULL, NULL);
+
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) {
+		if (g_strcmp0 (nm_device_get_ip_iface (NM_DEVICE (iter->data)), iface) == 0)
+			return NM_DEVICE (iter->data);
+	}
+	return NULL;
+}
+
+static NMDevice *
+find_device_by_iface (NMManager *self, const gchar *iface)
+{
+	GSList *iter;
+
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) {
+		if (g_strcmp0 (nm_device_get_iface (NM_DEVICE (iter->data)), iface) == 0)
+			return NM_DEVICE (iter->data);
+	}
+	return NULL;
+}
+
 static gboolean
 manager_sleeping (NMManager *self)
 {
@@ -834,109 +870,56 @@ nm_manager_get_state (NMManager *manager)
 	return NM_MANAGER_GET_PRIVATE (manager)->state;
 }
 
-/*******************************************************************/
-/* Settings stuff via NMSettings                                   */
-/*******************************************************************/
+/***************************/
 
 static NMDevice *
-get_device_from_hwaddr (NMManager *self, const char *setting_mac)
+find_parent_device_for_connection (NMManager *self, NMConnection *connection)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	const char *device_mac;
+	NMDeviceFactory *factory;
+	const char *parent_name = NULL;
+	NMConnection *parent_connection;
+	NMDevice *parent, *first_compatible = NULL;
 	GSList *iter;
 
-	if (!setting_mac)
+	factory = nm_device_factory_manager_find_factory_for_connection (connection);
+	if (!factory)
 		return NULL;
 
-	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *device = iter->data;
-
-		device_mac = nm_device_get_hw_address (iter->data);
-		if (!device_mac)
-			continue;
-		if (nm_utils_hwaddr_matches (setting_mac, -1, device_mac, -1))
-			return device;
-	}
-	return NULL;
-}
-
-static NMDevice *
-find_vlan_parent (NMManager *self,
-                  NMConnection *connection)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMSettingVlan *s_vlan;
-	NMSettingWired *s_wired;
-	NMConnection *parent_connection;
-	const char *parent_iface;
-	NMDevice *parent = NULL;
-	const char *setting_mac;
-	GSList *iter;
+	parent_name = nm_device_factory_get_connection_parent (factory, connection);
+	if (!parent_name)
+		return NULL;
 
-	/* The 'parent' property could be given by an interface name, a
-	 * connection UUID, or the MAC address of an NMSettingWired.
-	 */
-	s_vlan = nm_connection_get_setting_vlan (connection);
-	g_return_val_if_fail (s_vlan != NULL, NULL);
-
-	s_wired = nm_connection_get_setting_wired (connection);
-	setting_mac = s_wired ? nm_setting_wired_get_mac_address (s_wired) : NULL;
-
-	parent_iface = nm_setting_vlan_get_parent (s_vlan);
-	if (parent_iface) {
-		parent = find_device_by_ip_iface (self, parent_iface);
-		if (parent)
-			return parent;
-
-		if (nm_utils_is_uuid (parent_iface)) {
-			/* Try as a connection UUID */
-			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (priv->settings, parent_iface);
-			if (parent_connection) {
-				/* Check if the parent connection is activated on some device already */
-				for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-					NMActRequest *req;
-					NMConnection *candidate;
-
-					req = nm_device_get_act_request (NM_DEVICE (iter->data));
-					if (req) {
-						candidate = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (req));
-						if (candidate == parent_connection)
-							return NM_DEVICE (iter->data);
-					}
-				}
+	/* Try as an interface name */
+	parent = find_device_by_ip_iface (self, parent_name);
+	if (parent)
+		return parent;
 
-				/* Check the hardware address of the parent connection */
-				return get_device_from_hwaddr (self, setting_mac);
-			}
-			return NULL;
-		}
-	}
+	/* Maybe a hardware address */
+	parent = find_device_by_hw_addr (self, parent_name);
+	if (parent)
+		return parent;
 
-	/* Try the hardware address from the VLAN connection's hardware setting */
-	return get_device_from_hwaddr (self, setting_mac);
-}
+	/* Maybe a connection UUID */
+	parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (priv->settings, parent_name);
+	if (!parent_connection)
+		return NULL;
 
-static NMDevice *
-find_infiniband_parent (NMManager *self,
-                        NMConnection *connection)
-{
-	NMSettingInfiniband *s_infiniband;
-	const char *parent_iface;
-	NMDevice *parent = NULL;
-	const char *setting_mac;
+	/* Check if the parent connection is currently activated or is comaptible
+	 * with some known device.
+	 */
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *candidate = iter->data;
 
-	s_infiniband = nm_connection_get_setting_infiniband (connection);
-	g_return_val_if_fail (s_infiniband != NULL, NULL);
+		if (nm_device_get_connection (candidate) == parent_connection)
+			return candidate;
 
-	parent_iface = nm_setting_infiniband_get_parent (s_infiniband);
-	if (parent_iface) {
-		parent = find_device_by_ip_iface (self, parent_iface);
-		if (parent)
-			return parent;
+		if (   !first_compatible
+		    && nm_device_check_connection_compatible (candidate, parent_connection))
+			first_compatible = candidate;
 	}
 
-	setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband);
-	return get_device_from_hwaddr (self, setting_mac);
+	return first_compatible;
 }
 
 /**
@@ -944,86 +927,69 @@ find_infiniband_parent (NMManager *self,
  * @self: the #NMManager
  * @connection: the #NMConnection representing a virtual interface
  * @out_parent: on success, the parent device if any
+ * @error: an error if determining the virtual interface name failed
  *
  * Given @connection, returns the interface name that the connection
- * would represent.  If the interface name is not given by the connection,
- * this may require constructing it based on information in the connection
- * and existing network interfaces.
+ * would represent if it is a virtual connection.  %NULL is returned and
+ * @error is set if the connection is not virtual, or if the name could
+ * not be determined.
  *
  * Returns: the expected interface name (caller takes ownership), or %NULL
  */
 static char *
 get_virtual_iface_name (NMManager *self,
                         NMConnection *connection,
-                        NMDevice **out_parent)
+                        NMDevice **out_parent,
+                        GError **error)
 {
+	NMDeviceFactory *factory;
+	char *iface = NULL;
 	NMDevice *parent = NULL;
-	const char *ifname;
 
 	if (out_parent)
 		*out_parent = NULL;
 
-	if (!nm_connection_is_virtual (connection))
+	if (!nm_connection_is_virtual (connection)) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "NetworkManager plugin for '%s' unavailable",
+		             nm_connection_get_connection_type (connection));
 		return NULL;
-
-	ifname = nm_connection_get_interface_name (connection);
-
-	if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) {
-		NMSettingVlan *s_vlan;
-		char *vname;
-
-		s_vlan = nm_connection_get_setting_vlan (connection);
-		g_return_val_if_fail (s_vlan != NULL, NULL);
-
-		parent = find_vlan_parent (self, connection);
-		if (!parent)
-			return NULL;
-
-		if (!nm_device_supports_vlans (parent)) {
-			nm_log_warn (LOGD_DEVICE, "(%s): No support for VLANs on interface %s of type %s",
-			             ifname ? ifname : nm_connection_get_id (connection),
-			             nm_device_get_ip_iface (parent),
-			             nm_device_get_type_desc (parent));
-			return NULL;
-		}
-
-		/* If the connection doesn't specify the interface name for the VLAN
-		 * device, we create one for it using the VLAN ID and the parent
-		 * interface's name.
-		 */
-		if (ifname)
-			vname = g_strdup (ifname);
-		else {
-			vname = nm_utils_new_vlan_name (nm_device_get_ip_iface (parent),
-			                                nm_setting_vlan_get_id (s_vlan));
-		}
-		if (out_parent)
-			*out_parent = parent;
-		return vname;
 	}
 
-	if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) {
-		NMSettingInfiniband *s_infiniband;
-
-		parent = find_infiniband_parent (self, connection);
-		if (!parent)
-			return NULL;
+	factory = nm_device_factory_manager_find_factory_for_connection (connection);
+	if (!factory) {
+		nm_log_warn (LOGD_DEVICE, "(%s) NetworkManager plugin for '%s' unavailable",
+		             nm_connection_get_id (connection),
+		             nm_connection_get_connection_type (connection));
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "NetworkManager plugin for '%s' unavailable",
+		             nm_connection_get_connection_type (connection));
+		return NULL;
+	}
 
-		s_infiniband = nm_connection_get_setting_infiniband (connection);
-		if (out_parent)
-			*out_parent = parent;
-		return g_strdup (nm_setting_infiniband_get_virtual_interface_name (s_infiniband));
+	parent = find_parent_device_for_connection (self, connection);
+	iface = nm_device_factory_get_virtual_iface_name (factory,
+	                                                  connection,
+	                                                  parent ? nm_device_get_ip_iface (parent) : NULL);
+	if (!iface) {
+		nm_log_warn (LOGD_DEVICE, "(%s) failed to determine virtual interface name",
+		             nm_connection_get_id (connection));
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+		                     "failed to determine virtual interface name");
+		return NULL;
 	}
 
-	/* For any other virtual connection, NMSettingConnection:interface-name is
-	 * the virtual device name.
-	 */
-	g_return_val_if_fail (ifname != NULL, NULL);
-	return g_strdup (ifname);
+	if (out_parent)
+		*out_parent = parent;
+	return iface;
 }
 
-/***************************/
-
 /**
  * system_create_virtual_device:
  * @self: the #NMManager
@@ -1039,7 +1005,7 @@ static NMDevice *
 system_create_virtual_device (NMManager *self, NMConnection *connection, GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GError *local_err = NULL;
+	NMDeviceFactory *factory;
 	GSList *iter;
 	char *iface = NULL;
 	NMDevice *device = NULL, *parent = NULL;
@@ -1049,16 +1015,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-	iface = get_virtual_iface_name (self, connection, &parent);
-	if (!iface) {
-		nm_log_dbg (LOGD_DEVICE, "(%s) failed to determine virtual interface name",
-		            nm_connection_get_id (connection));
-		g_set_error_literal (error,
-		                     NM_MANAGER_ERROR,
-		                     NM_MANAGER_ERROR_FAILED,
-		                     "failed to determine virtual interface name");
+	iface = get_virtual_iface_name (self, connection, &parent, error);
+	if (!iface)
 		return NULL;
-	}
 
 	/* Make sure we didn't create a device for this connection already */
 	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
@@ -1076,32 +1035,25 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError
 		}
 	}
 
-	/* Block notification of link added since we're creating the device
-	 * explicitly here, otherwise adding the platform/kernel device would
-	 * create it before this function can do the rest of the setup.
-	 */
-	priv->ignore_link_added_cb++;
-
-	nm_owned = !nm_platform_link_exists (iface);
-
-	for (iter = priv->factories; iter; iter = iter->next) {
-		device = nm_device_factory_create_virtual_device_for_connection (NM_DEVICE_FACTORY (iter->data),
-		                                                                 connection,
-		                                                                 parent,
-		                                                                 &local_err);
-		if (device || local_err) {
-			if (device)
-				g_assert_no_error (local_err);
-			else {
-				nm_log_err (LOGD_DEVICE, "(%s) failed to create virtual device: %s",
-				            nm_connection_get_id (connection),
-				            local_err ? local_err->message : "(unknown error)");
-				g_propagate_error (error, local_err);
-			}
-			break;
-		}
+	factory = nm_device_factory_manager_find_factory_for_connection (connection);
+	if (!factory) {
+		nm_log_err (LOGD_DEVICE, "(%s:%s) NetworkManager plugin for '%s' unavailable",
+		            nm_connection_get_id (connection), iface,
+		            nm_connection_get_connection_type (connection));
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "NetworkManager plugin for '%s' unavailable",
+		             nm_connection_get_connection_type (connection));
+		goto out;
 	}
 
+	nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface);
+
+	device = nm_device_factory_create_virtual_device_for_connection (factory,
+	                                                                 connection,
+	                                                                 parent,
+	                                                                 error);
 	if (device) {
 		if (nm_owned)
 			nm_device_set_nm_owned (device);
@@ -1112,20 +1064,8 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError
 		add_device (self, device, !nm_owned);
 
 		g_object_unref (device);
-	} else {
-		if (error && !*error)
-			nm_log_err (LOGD_DEVICE, "(%s:%s) NetworkManager plugin for '%s' unavailable",
-			            nm_connection_get_id (connection), iface,
-			            nm_connection_get_connection_type (connection));
-			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_FAILED,
-			             "NetworkManager plugin for '%s' unavailable",
-			             nm_connection_get_connection_type (connection));
 	}
 
-	priv->ignore_link_added_cb--;
-
 out:
 	g_free (iface);
 	return device;
@@ -1565,13 +1505,13 @@ get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_gen
 	nm_device_capture_initial_config (device);
 
 	if (ifindex) {
-		int master_ifindex = nm_platform_link_get_master (ifindex);
+		int master_ifindex = nm_platform_link_get_master (NM_PLATFORM_GET, ifindex);
 
 		if (master_ifindex) {
 			master = nm_manager_get_device_by_ifindex (manager, master_ifindex);
 			if (!master) {
 				nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before its master (%s/%d)",
-				            nm_device_get_iface (device), nm_platform_link_get_name (master_ifindex), master_ifindex);
+				            nm_device_get_iface (device), nm_platform_link_get_name (NM_PLATFORM_GET, master_ifindex), master_ifindex);
 				return NULL;
 			}
 			if (!nm_device_get_act_request (master)) {
@@ -1694,10 +1634,7 @@ recheck_assume_connection (NMDevice *device, gpointer user_data)
 
 	if (manager_sleeping (self))
 		return FALSE;
-	if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_USER) ||
-	    nm_device_get_unmanaged_flag (device, NM_UNMANAGED_INTERNAL) ||
-	    nm_device_get_unmanaged_flag (device, NM_UNMANAGED_EXTERNAL_DOWN) ||
-	    nm_device_get_unmanaged_flag (device, NM_UNMANAGED_PARENT))
+	if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT))
 		return FALSE;
 
 	state = nm_device_get_state (device);
@@ -1768,6 +1705,18 @@ device_ip_iface_changed (NMDevice *device,
 	}
 }
 
+static gboolean
+notify_component_added (NMManager *self, GObject *component)
+{
+	GSList *iter;
+
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
+		if (nm_device_notify_component_added (NM_DEVICE (iter->data), component))
+			return TRUE;
+	}
+	return FALSE;
+}
+
 /**
  * add_device:
  * @self: the #NMManager
@@ -1788,9 +1737,13 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
 	RfKillType rtype;
 	GSList *iter, *remove = NULL;
 	gboolean connection_assumed = FALSE;
+	int ifindex;
 
 	/* No duplicates */
-	if (nm_manager_get_device_by_udi (self, nm_device_get_udi (device)))
+	ifindex = nm_device_get_ifindex (device);
+	if (ifindex > 0 && nm_manager_get_device_by_ifindex (self, ifindex))
+		return;
+	if (find_device_by_iface (self, nm_device_get_iface (device)))
 		return;
 
 	/* Remove existing devices owned by the new device; eg remove ethernet
@@ -1852,8 +1805,12 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
 	driver = nm_device_get_driver (device);
 	if (!driver)
 		driver = "unknown";
-	nm_log_info (LOGD_HW, "(%s): new %s device (driver: '%s' ifindex: %d)",
-	             iface, type_desc, driver, nm_device_get_ifindex (device));
+	nm_log_info (LOGD_HW, "(%s): new %s device (carrier: %s, driver: '%s', ifindex: %d)",
+	             iface, type_desc,
+	             nm_device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT)
+	                ? (nm_device_has_carrier (device) ? "ON" : "OFF")
+	                : "UNKNOWN",
+	             driver, nm_device_get_ifindex (device));
 
 	unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
 	user_unmanaged = nm_device_spec_match_list (device, unmanaged_specs);
@@ -1881,27 +1838,14 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
 	g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
 	g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES);
 
+	notify_component_added (self, G_OBJECT (device));
+
 	/* New devices might be master interfaces for virtual interfaces; so we may
 	 * need to create new virtual interfaces now.
 	 */
 	system_create_virtual_devices (self);
 }
 
-static NMDevice *
-find_device_by_ip_iface (NMManager *self, const gchar *iface)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
-
-	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *candidate = iter->data;
-
-		if (g_strcmp0 (nm_device_get_ip_iface (candidate), iface) == 0)
-			return candidate;
-	}
-	return NULL;
-}
-
 /*******************************************************************/
 
 static void
@@ -1917,134 +1861,13 @@ factory_component_added_cb (NMDeviceFactory *factory,
                             GObject *component,
                             gpointer user_data)
 {
-	NMManager *self = NM_MANAGER (user_data);
-	GSList *iter;
-
-	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
-		if (nm_device_notify_component_added (NM_DEVICE (iter->data), component))
-			return TRUE;
-	}
-	return FALSE;
-}
-
-#define PLUGIN_PREFIX "libnm-device-plugin-"
-#define PLUGIN_PATH_TAG "NMManager-plugin-path"
-
-struct read_device_factory_paths_data {
-	char *path;
-	struct stat st;
-};
-
-static gint
-read_device_factory_paths_sort_fcn (gconstpointer a, gconstpointer b)
-{
-	const struct read_device_factory_paths_data *da = a;
-	const struct read_device_factory_paths_data *db = b;
-	time_t ta, tb;
-
-	ta = MAX (da->st.st_mtime, da->st.st_ctime);
-	tb = MAX (db->st.st_mtime, db->st.st_ctime);
-
-	if (ta < tb)
-		return 1;
-	if (ta > tb)
-		return -1;
-	return 0;
+	return notify_component_added (NM_MANAGER (user_data), component);
 }
 
-static char**
-read_device_factory_paths (void)
-{
-	GDir *dir;
-	GError *error = NULL;
-	const char *item;
-	GArray *paths;
-	char **result;
-	guint i;
-
-	dir = g_dir_open (NMPLUGINDIR, 0, &error);
-	if (!dir) {
-		nm_log_warn (LOGD_HW, "device plugin: failed to open directory %s: %s",
-		             NMPLUGINDIR,
-		             (error && error->message) ? error->message : "(unknown)");
-		g_clear_error (&error);
-		return NULL;
-	}
-
-	paths = g_array_new (FALSE, FALSE, sizeof (struct read_device_factory_paths_data));
-
-	while ((item = g_dir_read_name (dir))) {
-		int errsv;
-		struct read_device_factory_paths_data data;
-
-		if (!g_str_has_prefix (item, PLUGIN_PREFIX))
-			continue;
-		if (g_str_has_suffix (item, ".la"))
-			continue;
-
-		data.path = g_build_filename (NMPLUGINDIR, item, NULL);
-
-		if (stat (data.path, &data.st) != 0) {
-			errsv = errno;
-			nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (error during stat: %s)", data.path, strerror (errsv));
-			goto NEXT;
-		}
-		if (!S_ISREG (data.st.st_mode))
-			goto NEXT;
-		if (data.st.st_uid != 0) {
-			nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (file must be owned by root)", data.path);
-			goto NEXT;
-		}
-		if (data.st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) {
-			nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (invalid file permissions)", data.path);
-			goto NEXT;
-		}
-
-		g_array_append_val (paths, data);
-		continue;
-NEXT:
-		g_free (data.path);
-	}
-	g_dir_close (dir);
-
-	/* sort filenames by modification time. */
-	g_array_sort (paths, read_device_factory_paths_sort_fcn);
-
-	result = g_new (char *, paths->len + 1);
-	for (i = 0; i < paths->len; i++)
-		result[i] = g_array_index (paths, struct read_device_factory_paths_data, i).path;
-	result[i] = NULL;
-
-	g_array_free (paths, TRUE);
-	return result;
-}
-
-static gboolean
-_register_device_factory (NMManager *self,
-                          NMDeviceFactory *factory,
-                          gboolean duplicate_check,
-                          const char *path,
-                          GError **error)
+static void
+_register_device_factory (NMDeviceFactory *factory, gpointer user_data)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDeviceType ftype;
-	GSList *iter;
-
-	if (duplicate_check) {
-		/* Make sure we don't double-register factories */
-		ftype = nm_device_factory_get_device_type (factory);
-		for (iter = priv->factories; iter; iter = iter->next) {
-			if (ftype == nm_device_factory_get_device_type (iter->data)) {
-				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-				             "multiple plugins for same type (using '%s' instead of '%s')",
-				             (char *) g_object_get_data (G_OBJECT (iter->data), PLUGIN_PATH_TAG),
-				             path);
-				return FALSE;
-			}
-		}
-	}
-
-	priv->factories = g_slist_append (priv->factories, factory);
+	NMManager *self = NM_MANAGER (user_data);
 
 	g_signal_connect (factory,
 	                  NM_DEVICE_FACTORY_DEVICE_ADDED,
@@ -2054,80 +1877,6 @@ _register_device_factory (NMManager *self,
 	                  NM_DEVICE_FACTORY_COMPONENT_ADDED,
 	                  G_CALLBACK (factory_component_added_cb),
 	                  self);
-	g_object_set_data_full (G_OBJECT (factory), PLUGIN_PATH_TAG,
-	                        g_strdup (path), g_free);
-	return TRUE;
-}
-
-static void
-load_device_factories (NMManager *self)
-{
-	NMDeviceFactory *factory;
-	const GSList *iter;
-	GError *error = NULL;
-	char **path, **paths;
-
-	/* Register internal factories first */
-	for (iter = nm_device_factory_get_internal_factory_types (); iter; iter = iter->next) {
-		GType ftype = (GType) GPOINTER_TO_SIZE (iter->data);
-
-		factory = (NMDeviceFactory *) g_object_new (ftype, NULL);
-		g_assert (factory);
-		if (_register_device_factory (self, factory, FALSE, "internal", &error)) {
-			nm_log_dbg (LOGD_HW, "Loaded device plugin: %s", g_type_name (ftype));
-		} else {
-			nm_log_warn (LOGD_HW, "Loading device plugin failed: %s", error->message);
-			g_object_unref (factory);
-			g_clear_error (&error);
-		}
-	}
-
-	paths = read_device_factory_paths ();
-	if (!paths)
-		return;
-
-	for (path = paths; *path; path++) {
-		GModule *plugin;
-		NMDeviceFactoryCreateFunc create_func;
-		const char *item;
-
-		item = strrchr (*path, '/');
-		g_assert (item);
-
-		plugin = g_module_open (*path, G_MODULE_BIND_LOCAL);
-
-		if (!plugin) {
-			nm_log_warn (LOGD_HW, "(%s): failed to load plugin: %s", item, g_module_error ());
-			continue;
-		}
-
-		if (!g_module_symbol (plugin, "nm_device_factory_create", (gpointer) &create_func)) {
-			nm_log_warn (LOGD_HW, "(%s): failed to find device factory creator: %s", item, g_module_error ());
-			g_module_close (plugin);
-			continue;
-		}
-
-		factory = create_func (&error);
-		if (!factory) {
-			nm_log_warn (LOGD_HW, "(%s): failed to initialize device factory: %s",
-			             item, error ? error->message : "unknown");
-			g_clear_error (&error);
-			g_module_close (plugin);
-			continue;
-		}
-		g_clear_error (&error);
-
-		if (_register_device_factory (self, factory, TRUE, g_module_name (plugin), &error)) {
-			nm_log_info (LOGD_HW, "Loaded device plugin: %s", g_module_name (plugin));
-			g_module_make_resident (plugin);
-		} else {
-			nm_log_warn (LOGD_HW, "Loading device plugin failed: %s", error->message);
-			g_object_unref (factory);
-			g_module_close (plugin);
-			g_clear_error (&error);
-		}
-	}
-	g_strfreev (paths);
 }
 
 /*******************************************************************/
@@ -2135,63 +1884,43 @@ load_device_factories (NMManager *self)
 static void
 platform_link_added (NMManager *self,
                      int ifindex,
-                     NMPlatformLink *plink,
-                     NMPlatformReason reason)
+                     NMPlatformLink *plink)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDeviceFactory *factory;
 	NMDevice *device = NULL;
-	GSList *iter;
 	GError *error = NULL;
 
 	g_return_if_fail (ifindex > 0);
 
-	if (priv->ignore_link_added_cb > 0)
-		return;
-
 	if (nm_manager_get_device_by_ifindex (self, ifindex))
 		return;
 
 	/* Try registered device factories */
-	for (iter = priv->factories; iter; iter = iter->next) {
-		NMDeviceFactory *factory = NM_DEVICE_FACTORY (iter->data);
-
-		device = nm_device_factory_new_link (factory, plink, &error);
-		if (device && NM_IS_DEVICE (device)) {
-			g_assert_no_error (error);
-			break;  /* success! */
-		}
+	factory = nm_device_factory_manager_find_factory_for_link_type (plink->type);
+	if (factory) {
+		gboolean ignore = FALSE;
 
-		if (error) {
-			nm_log_warn (LOGD_HW, "%s: factory failed to create device: (%d) %s",
-			             plink->udi,
-			             error ? error->code : -1,
-			             error ? error->message : "(unknown)");
-			g_clear_error (&error);
+		device = nm_device_factory_new_link (factory, plink, &ignore, &error);
+		if (!device) {
+			if (!ignore) {
+				nm_log_warn (LOGD_HW, "%s: factory failed to create device: %s",
+				             plink->name, error->message);
+				g_clear_error (&error);
+			}
 			return;
 		}
 	}
 
-	/* Ignore Bluetooth PAN interfaces; they are handled by their NMDeviceBt
-	 * parent and don't get a separate interface.
-	 */
-	if (!strncmp (plink->name, "bnep", STRLEN ("bnep")))
-		return;
-
 	if (device == NULL) {
 		switch (plink->type) {
-
 		case NM_LINK_TYPE_WWAN_ETHERNET:
-			/* WWAN pseudo-ethernet interfaces are handled automatically by
-			 * their NMDeviceModem and don't get a separate NMDevice object.
-			 */
-			break;
-
+		case NM_LINK_TYPE_BNEP:
 		case NM_LINK_TYPE_OLPC_MESH:
 		case NM_LINK_TYPE_TEAM:
 		case NM_LINK_TYPE_WIFI:
 		case NM_LINK_TYPE_WIMAX:
 			nm_log_info (LOGD_HW, "(%s): '%s' plugin not available; creating generic device",
-			             plink->name, plink->type_name);
+			             plink->name, nm_link_type_to_string (plink->type));
 			/* fall through */
 		default:
 			device = nm_device_generic_new (plink);
@@ -2205,30 +1934,76 @@ platform_link_added (NMManager *self,
 	}
 }
 
+typedef struct {
+	NMManager *self;
+	int ifindex;
+} PlatformLinkCbData;
+
+static gboolean
+_platform_link_cb_idle (PlatformLinkCbData *data)
+{
+	NMManager *self = data->self;
+
+	if (self) {
+		const NMPlatformLink *l;
+
+		l = nm_platform_link_get (NM_PLATFORM_GET, data->ifindex);
+		if (l) {
+			NMPlatformLink pllink;
+
+			pllink = *l; /* make a copy of the link instance */
+			platform_link_added (self, data->ifindex, &pllink);
+		} else {
+			NMDevice *device;
+
+			device = nm_manager_get_device_by_ifindex (self, data->ifindex);
+			if (device)
+				remove_device (self, device, FALSE, TRUE);
+		}
+		g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self);
+	}
+	g_slice_free (PlatformLinkCbData, data);
+	return G_SOURCE_REMOVE;
+}
+
 static void
 platform_link_cb (NMPlatform *platform,
+                  NMPObjectType obj_type,
                   int ifindex,
                   NMPlatformLink *plink,
                   NMPlatformSignalChangeType change_type,
                   NMPlatformReason reason,
                   gpointer user_data)
 {
+	PlatformLinkCbData *data;
+
 	switch (change_type) {
 	case NM_PLATFORM_SIGNAL_ADDED:
-		platform_link_added (NM_MANAGER (user_data), ifindex, plink, reason);
-		break;
-	case NM_PLATFORM_SIGNAL_REMOVED: {
-		NMManager *self = NM_MANAGER (user_data);
-		NMDevice *device;
-
-		device = nm_manager_get_device_by_ifindex (self, ifindex);
-		if (device)
-			remove_device (self, device, FALSE, TRUE);
+	case NM_PLATFORM_SIGNAL_REMOVED:
+		data = g_slice_new (PlatformLinkCbData);
+		data->self = NM_MANAGER (user_data);
+		data->ifindex = ifindex;
+		g_object_add_weak_pointer (G_OBJECT (data->self), (gpointer *) &data->self);
+		g_idle_add ((GSourceFunc) _platform_link_cb_idle, data);
 		break;
-	 }
-	 default:
+	default:
 		break;
-	 }
+	}
+}
+
+static void
+platform_query_devices (NMManager *self)
+{
+	GArray *links_array;
+	NMPlatformLink *links;
+	int i;
+
+	links_array = nm_platform_link_get_all (NM_PLATFORM_GET);
+	links = (NMPlatformLink *) links_array->data;
+	for (i = 0; i < links_array->len; i++)
+		platform_link_added (self, links[i].ifindex, &links[i]);
+
+	g_array_unref (links_array);
 }
 
 static void
@@ -2248,6 +2023,40 @@ nm_manager_get_devices (NMManager *manager)
 	return NM_MANAGER_GET_PRIVATE (manager)->devices;
 }
 
+static NMDevice *
+nm_manager_get_connection_device (NMManager *self,
+                                  NMConnection *connection)
+{
+	NMActiveConnection *ac = find_ac_for_connection (self, connection);
+	if (ac == NULL)
+		return NULL;
+
+	return nm_active_connection_get_device (ac);
+}
+
+static NMDevice *
+nm_manager_get_best_device_for_connection (NMManager *self,
+                                           NMConnection *connection)
+{
+	const GSList *devices, *iter;
+	NMDevice *act_device = nm_manager_get_connection_device (self, connection);
+
+	if (act_device)
+		return act_device;
+
+	/* Pick the first device that's compatible with the connection. */
+	devices = nm_manager_get_devices (self);
+	for (iter = devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *device = NM_DEVICE (iter->data);
+
+		if (nm_device_check_connection_available (device, connection, NM_DEVICE_CHECK_CON_AVAILABLE_NONE, NULL))
+			return device;
+	}
+
+	/* No luck. :( */
+	return NULL;
+}
+
 static gboolean
 impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err)
 {
@@ -2411,13 +2220,10 @@ find_master (NMManager *self,
 				NMConnection *candidate = iter->data;
 				char *vname;
 
-				if (nm_connection_is_virtual (candidate)) {
-					vname = get_virtual_iface_name (self, candidate, NULL);
-					if (   g_strcmp0 (master, vname) == 0
-					    && is_compatible_with_slave (candidate, connection))
-						master_connection = candidate;
-					g_free (vname);
-				}
+				vname = get_virtual_iface_name (self, candidate, NULL, NULL);
+				if (g_strcmp0 (master, vname) == 0 && is_compatible_with_slave (candidate, connection))
+					master_connection = candidate;
+				g_free (vname);
 			}
 			g_slist_free (connections);
 		}
@@ -2615,6 +2421,127 @@ ensure_master_active_connection (NMManager *self,
 	return NULL;
 }
 
+/**
+ * find_slaves:
+ * @manager: #NMManager object
+ * @connection: the master #NMConnection to find slave connections for
+ * @device: the master #NMDevice for the @connection
+ *
+ * Given an #NMConnection, attempts to find its slaves. If @connection is not
+ * master, or has not any slaves, this will return %NULL.
+ *
+ * Returns: list of slave connections for given master @connection, or %NULL
+ **/
+static GSList *
+find_slaves (NMManager *manager,
+             NMConnection *connection,
+             NMDevice *device)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GSList *all_connections, *iter;
+	GSList *slaves = NULL;
+	NMSettingConnection *s_con;
+	const char *master;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	master = nm_setting_connection_get_master (s_con);
+
+	if (master != NULL)
+		return NULL;  /* connection is not master */
+
+	/* Search through all connections, not only inactive ones, because
+	 * even if a slave was already active, it might be deactivated during
+	 * master reactivation.
+	 */
+	all_connections = nm_settings_get_connections (priv->settings);
+	for (iter = all_connections; iter; iter = iter->next) {
+		NMConnection *master_connection = NULL;
+		NMDevice *master_device = NULL;
+		NMConnection *candidate = iter->data;
+
+		find_master (manager, candidate, NULL, &master_connection, &master_device, NULL, NULL);
+		if (   (master_connection && master_connection == connection)
+		    || (master_device && master_device == device)) {
+			slaves = g_slist_prepend (slaves, candidate);
+		}
+	}
+	g_slist_free (all_connections);
+
+	return g_slist_reverse (slaves);
+}
+
+static gboolean
+should_connect_slaves (NMConnection *connection, NMDevice *device)
+{
+	NMSettingConnection *s_con;
+	NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
+	gs_free char *value = NULL;
+
+	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)
+		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);
+
+out:
+	if (autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO)
+		return FALSE;
+	if (autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES)
+		return TRUE;
+	return FALSE;
+}
+
+static gboolean
+autoconnect_slaves (NMManager *manager,
+                    NMConnection *master_connection,
+                    NMDevice *master_device,
+                    NMAuthSubject *subject)
+{
+	GError *local_err = NULL;
+	gboolean ret = FALSE;
+
+	if (should_connect_slaves (master_connection, master_device)) {
+		GSList *slaves, *iter;
+
+		iter = slaves = find_slaves (manager, master_connection, master_device);
+		ret = slaves != NULL;
+
+		while (iter) {
+			NMConnection *slave_connection = iter->data;
+
+			iter = iter->next;
+			nm_log_dbg (LOGD_CORE, "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)",
+			            nm_connection_get_id (slave_connection),
+			            nm_connection_get_uuid (slave_connection),
+			            nm_connection_get_id (master_connection),
+			            nm_connection_get_uuid (master_connection));
+
+			/* Schedule slave activation */
+			nm_manager_activate_connection (manager,
+			                                slave_connection,
+			                                NULL,
+			                                nm_manager_get_best_device_for_connection (manager, slave_connection),
+			                                subject,
+			                                &local_err);
+			if (local_err) {
+				nm_log_warn (LOGD_CORE, "Slave connection activation failed: %s", local_err->message);
+				g_error_free (local_err);
+			}
+		}
+		g_slist_free (slaves);
+	}
+	return ret;
+}
+
 static gboolean
 _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **error)
 {
@@ -2635,7 +2562,7 @@ _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **er
 static gboolean
 _internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error)
 {
-	NMDevice *device, *master_device = NULL;
+	NMDevice *device, *existing, *master_device = NULL;
 	NMConnection *connection;
 	NMConnection *master_connection = NULL;
 	NMActiveConnection *master_ac = NULL;
@@ -2783,11 +2710,19 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		}
 
 		nm_active_connection_set_master (active, master_ac);
-		nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %s",
+		nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %p",
 		            nm_connection_get_id (connection),
-		            nm_active_connection_get_path (master_ac));
+		            master_ac);
 	}
 
+	/* Check slaves for master connection and possibly activate them */
+	autoconnect_slaves (self, connection, device, nm_active_connection_get_subject (active));
+
+	/* Disconnect the connection if connected or queued on another device */
+	existing = nm_manager_get_connection_device (self, connection);
+	if (existing)
+		nm_device_steal_connection (existing, connection);
+
 	/* Export the new ActiveConnection to clients and start it on the device */
 	nm_active_connection_export (active);
 	g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
@@ -2825,6 +2760,7 @@ _internal_activate_generic (NMManager *self, NMActiveConnection *active, GError
 		 * is exported, make sure the manager's activating-connection property
 		 * is up-to-date.
 		 */
+		active_connection_add (self, active);
 		policy_activating_device_changed (G_OBJECT (priv->policy), NULL, self);
 	}
 
@@ -2894,18 +2830,6 @@ _new_active_connection (NMManager *self,
 		return NULL;
 	}
 
-	if (existing_ac) {
-		NMDevice *existing_device = nm_active_connection_get_device (existing_ac);
-
-		if (existing_device != device) {
-			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
-			             "Connection '%s' is already active on %s",
-			             nm_connection_get_id (connection),
-			             nm_device_get_iface (existing_device));
-			return NULL;
-		}
-	}
-
 	/* Normalize the specific object */
 	if (specific_object && g_strcmp0 (specific_object, "/") == 0)
 		specific_object = NULL;
@@ -2929,15 +2853,14 @@ _internal_activation_failed (NMManager *self,
                              NMActiveConnection *active,
                              const char *error_desc)
 {
-	nm_log_warn (LOGD_CORE, "Failed to activate '%s': %s",
-	             nm_connection_get_id (nm_active_connection_get_connection (active)),
-	             error_desc);
+	nm_log_dbg (LOGD_CORE, "Failed to activate '%s': %s",
+	            nm_connection_get_id (nm_active_connection_get_connection (active)),
+	            error_desc);
 
 	if (nm_active_connection_get_state (active) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
 		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
 		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
 	}
-	active_connection_remove (self, active);
 }
 
 static void
@@ -2948,8 +2871,11 @@ _internal_activation_auth_done (NMActiveConnection *active,
                                 gpointer user_data2)
 {
 	NMManager *self = user_data1;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 
+	priv->authorizing_connections = g_slist_remove (priv->authorizing_connections, active);
+
 	if (success) {
 		if (_internal_activate_generic (self, active, &error)) {
 			g_object_unref (active);
@@ -2989,8 +2915,10 @@ nm_manager_activate_connection (NMManager *self,
                                 NMAuthSubject *subject,
                                 GError **error)
 {
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *active;
 	char *error_desc = NULL;
+	GSList *iter;
 
 	g_return_val_if_fail (self != NULL, NULL);
 	g_return_val_if_fail (connection != NULL, NULL);
@@ -3010,6 +2938,22 @@ nm_manager_activate_connection (NMManager *self,
 		return NULL;
 	}
 
+	/* Look for a active connection that's equivalent and is already pending authorization
+	 * and eventual activation. This is used to de-duplicate concurrent activations which would
+	 * otherwise race and cause the device to disconnect and reconnect repeatedly.
+	 * In particular, this allows the master and multiple slaves to concurrently auto-activate
+	 * while all the slaves would use the same active-connection. */
+	for (iter = priv->authorizing_connections; iter; iter = g_slist_next (iter)) {
+		active = iter->data;
+
+		if (   connection == nm_active_connection_get_connection (active)
+		    && g_strcmp0 (nm_active_connection_get_specific_object (active), specific_object) == 0
+		    && nm_active_connection_get_device (active) == device
+		    && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))
+		    && nm_auth_subject_is_internal (subject))
+			return active;
+	}
+
 	active = _new_active_connection (self,
 	                                 connection,
 	                                 specific_object,
@@ -3017,8 +2961,8 @@ nm_manager_activate_connection (NMManager *self,
 	                                 subject,
 	                                 error);
 	if (active) {
+		priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active);
 		nm_active_connection_authorize (active, _internal_activation_auth_done, self, NULL);
-		active_connection_add (self, active);
 	}
 	return active;
 }
@@ -3064,6 +3008,18 @@ validate_activation_request (NMManager *self,
 		goto error;
 	}
 
+	/* Not implemented yet, we want to fail early */
+	if (   nm_connection_get_setting_connection (connection)
+	    && nm_connection_get_setting_ip6_config (connection)
+	    && !strcmp (nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG),
+	                NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE,
+		                     "Sharing IPv6 connections is not supported yet.");
+		return NULL;
+	}
+
 	/* Check whether it's a VPN or not */
 	if (   nm_connection_get_setting_vpn (connection)
 	    || nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME))
@@ -3083,7 +3039,10 @@ validate_activation_request (NMManager *self,
 			                     "Device not found");
 			goto error;
 		}
-	} else {
+	} else
+		device = nm_manager_get_best_device_for_connection (self, connection);
+
+	if (!device) {
 		gboolean is_software = nm_connection_is_virtual (connection);
 
 		/* VPN and software-device connections don't need a device yet */
@@ -3091,22 +3050,17 @@ validate_activation_request (NMManager *self,
 			g_set_error_literal (error,
 			                     NM_MANAGER_ERROR,
 			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-			                     "This connection requires an existing device.");
+			                     "No suitable device found for this connection.");
 			goto error;
 		}
 
 		if (is_software) {
-			/* Look for an existing device with the connection's interface name */
 			char *iface;
 
-			iface = get_virtual_iface_name (self, connection, NULL);
-			if (!iface) {
-				g_set_error_literal (error,
-				                     NM_MANAGER_ERROR,
-				                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				                     "Failed to determine connection's virtual interface name");
+			/* Look for an existing device with the connection's interface name */
+			iface = get_virtual_iface_name (self, connection, NULL, error);
+			if (!iface)
 				goto error;
-			}
 
 			device = find_device_by_ip_iface (self, iface);
 			g_free (iface);
@@ -3252,7 +3206,6 @@ impl_manager_activate_connection (NMManager *self,
 		goto error;
 
 	nm_active_connection_authorize (active, _activation_auth_done, self, context);
-	active_connection_add (self, active);
 	g_clear_object (&subject);
 	return;
 
@@ -3330,8 +3283,6 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 		                                 activation_add_done,
 		                                 info);
 	} else {
-		active_connection_remove (self, active);
-
 		g_assert (error_desc);
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -3438,7 +3389,6 @@ impl_manager_add_and_activate_connection (NMManager *self,
 		goto error;
 
 	nm_active_connection_authorize (active, _add_and_activate_auth_done, self, context);
-	active_connection_add (self, active);
 	g_object_unref (connection);
 	g_object_unref (subject);
 	return;
@@ -3616,7 +3566,7 @@ done:
 static gboolean
 device_is_wake_on_lan (NMDevice *device)
 {
-	return nm_platform_link_get_wake_on_lan (nm_device_get_ip_ifindex (device));
+	return nm_platform_link_get_wake_on_lan (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device));
 }
 
 static void
@@ -4172,11 +4122,16 @@ impl_manager_check_connectivity (NMManager *manager,
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
 }
 
+static void
+start_factory (NMDeviceFactory *factory, gpointer user_data)
+{
+	nm_device_factory_start (factory);
+}
+
 void
 nm_manager_start (NMManager *self)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
 	guint i;
 
 	/* Set initial radio enabled/disabled state */
@@ -4208,10 +4163,9 @@ nm_manager_start (NMManager *self)
 	system_hostname_changed_cb (priv->settings, NULL, self);
 
 	/* Start device factories */
-	for (iter = priv->factories; iter; iter = iter->next)
-		nm_device_factory_start (iter->data);
+	nm_device_factory_manager_for_each_factory (start_factory, NULL);
 
-	nm_platform_query_devices ();
+	platform_query_devices (self);
 
 	/*
 	 * Connections added before the manager is started do not emit
@@ -4270,12 +4224,9 @@ connectivity_changed (NMConnectivity *connectivity,
                       gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
-	NMConnectivityState state;
-	static const char *connectivity_states[] = { "UNKNOWN", "NONE", "PORTAL", "LIMITED", "FULL" };
 
-	state = nm_connectivity_get_state (connectivity);
 	nm_log_dbg (LOGD_CORE, "connectivity checking indicates %s",
-	            connectivity_states[state]);
+	            nm_connectivity_state_to_string (nm_connectivity_get_state (connectivity)));
 
 	nm_manager_update_state (self);
 	g_object_notify (G_OBJECT (self), NM_MANAGER_CONNECTIVITY);
@@ -4718,6 +4669,7 @@ nm_manager_new (NMSettings *settings,
 	NMManagerPrivate *priv;
 	DBusGConnection *bus;
 	DBusConnection *dbus_connection;
+	NMConfigData *config_data;
 
 	g_assert (settings);
 
@@ -4749,7 +4701,16 @@ nm_manager_new (NMSettings *settings,
 	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE,
 	                  G_CALLBACK (policy_activating_device_changed), singleton);
 
-	priv->connectivity = nm_connectivity_new ();
+	priv->config = g_object_ref (nm_config_get ());
+	g_signal_connect (G_OBJECT (priv->config),
+	                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
+	                  G_CALLBACK (_config_changed_cb),
+	                  singleton);
+
+	config_data = nm_config_get_data (priv->config);
+	priv->connectivity = nm_connectivity_new (nm_config_data_get_connectivity_uri (config_data),
+	                                          nm_config_data_get_connectivity_interval (config_data),
+	                                          nm_config_data_get_connectivity_response (config_data));
 	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
 	                  G_CALLBACK (connectivity_changed), singleton);
 
@@ -4807,7 +4768,7 @@ nm_manager_new (NMSettings *settings,
 	rfkill_change (priv->radio_states[RFKILL_TYPE_WLAN].desc, RFKILL_TYPE_WLAN, initial_wifi_enabled);
 	rfkill_change (priv->radio_states[RFKILL_TYPE_WWAN].desc, RFKILL_TYPE_WWAN, initial_wwan_enabled);
 
-	load_device_factories (singleton);
+	nm_device_factory_manager_load_factories (_register_device_factory, singleton);
 
 	return singleton;
 }
@@ -5042,13 +5003,18 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
+_deinit_device_factory (NMDeviceFactory *factory, gpointer user_data)
+{
+	g_signal_handlers_disconnect_matched (factory, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NM_MANAGER (user_data));
+}
+
+static void
 dispose (GObject *object)
 {
 	NMManager *manager = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 	DBusGConnection *bus;
 	DBusConnection *dbus_connection;
-	GSList *iter;
 
 	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
 	priv->auth_chains = NULL;
@@ -5070,7 +5036,14 @@ dispose (GObject *object)
 	g_clear_object (&priv->primary_connection);
 	g_clear_object (&priv->activating_connection);
 
-	g_clear_object (&priv->connectivity);
+	if (priv->config) {
+		g_signal_handlers_disconnect_by_func (priv->config, _config_changed_cb, manager);
+		g_clear_object (&priv->config);
+	}
+	if (priv->connectivity) {
+		g_signal_handlers_disconnect_by_func (priv->connectivity, connectivity_changed, manager);
+		g_clear_object (&priv->connectivity);
+	}
 
 	g_free (priv->hostname);
 
@@ -5113,14 +5086,8 @@ dispose (GObject *object)
 		g_clear_object (&priv->fw_monitor);
 	}
 
-	for (iter = priv->factories; iter; iter = iter->next) {
-		NMDeviceFactory *factory = iter->data;
-
-		g_signal_handlers_disconnect_matched (factory, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager);
-		g_object_unref (factory);
-	}
-	g_clear_pointer (&priv->factories, g_slist_free);
-
+	nm_device_factory_manager_for_each_factory (_deinit_device_factory, manager);
+	
 	if (priv->timestamp_update_id) {
 		g_source_remove (priv->timestamp_update_id);
 		priv->timestamp_update_id = 0;