summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-07-14 19:38:58 +0200
committerMichael Biebl <biebl@debian.org>2015-07-14 19:38:58 +0200
commit50a58f0fabd8a34c1b6108a107e08abe3c1ccd24 (patch)
tree6790165f39daee79e2b6c6617483320613493367 /src/devices
parentf408e27bccfacf347605a8d98649975a68f38a17 (diff)
Imported Upstream version 1.0.4 upstream/1.0.4
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/adsl/Makefile.in2
-rw-r--r--src/devices/adsl/nm-atm-manager.c11
-rw-r--r--src/devices/adsl/nm-device-adsl.c12
-rw-r--r--src/devices/bluetooth/Makefile.in2
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c9
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c22
-rw-r--r--src/devices/bluetooth/nm-device-bt.c66
-rw-r--r--src/devices/nm-device-bond.c89
-rw-r--r--src/devices/nm-device-bridge.c73
-rw-r--r--src/devices/nm-device-ethernet.c256
-rw-r--r--src/devices/nm-device-factory.c532
-rw-r--r--src/devices/nm-device-factory.h124
-rw-r--r--src/devices/nm-device-generic.c15
-rw-r--r--src/devices/nm-device-gre.c22
-rw-r--r--src/devices/nm-device-infiniband.c123
-rw-r--r--src/devices/nm-device-macvlan.c22
-rw-r--r--src/devices/nm-device-private.h3
-rw-r--r--src/devices/nm-device-tun.c17
-rw-r--r--src/devices/nm-device-veth.c35
-rw-r--r--src/devices/nm-device-vlan.c348
-rw-r--r--src/devices/nm-device-vxlan.c24
-rw-r--r--src/devices/nm-device.c1934
-rw-r--r--src/devices/nm-device.h43
-rw-r--r--src/devices/team/Makefile.in2
-rw-r--r--src/devices/team/nm-device-team.c67
-rw-r--r--src/devices/team/nm-team-factory.c21
-rw-r--r--src/devices/wifi/Makefile.in2
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c23
-rw-r--r--src/devices/wifi/nm-device-wifi.c234
-rw-r--r--src/devices/wifi/nm-wifi-ap.c244
-rw-r--r--src/devices/wifi/nm-wifi-ap.h2
-rw-r--r--src/devices/wifi/nm-wifi-factory.c17
-rw-r--r--src/devices/wifi/tests/Makefile.in437
-rw-r--r--src/devices/wimax/Makefile.in2
-rw-r--r--src/devices/wimax/nm-wimax-factory.c14
-rw-r--r--src/devices/wimax/nm-wimax-util.h2
-rw-r--r--src/devices/wwan/Makefile.in2
-rw-r--r--src/devices/wwan/nm-device-modem.c31
-rw-r--r--src/devices/wwan/nm-modem.c9
-rw-r--r--src/devices/wwan/nm-wwan-factory.c19
40 files changed, 3126 insertions, 1786 deletions
diff --git a/src/devices/adsl/Makefile.in b/src/devices/adsl/Makefile.in
index 02da85e7..6f7fe174 100644
--- a/src/devices/adsl/Makefile.in
+++ b/src/devices/adsl/Makefile.in
@@ -223,6 +223,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -336,6 +337,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c
index 6f34c02b..0830f85d 100644
--- a/src/devices/adsl/nm-atm-manager.c
+++ b/src/devices/adsl/nm-atm-manager.c
@@ -25,6 +25,7 @@
 #include <gmodule.h>
 
 #include "nm-atm-manager.h"
+#include "nm-setting-adsl.h"
 #include "nm-device-adsl.h"
 #include "nm-device-factory.h"
 #include "nm-logging.h"
@@ -203,11 +204,9 @@ handle_uevent (GUdevClient *client,
 		adsl_remove (self, device);
 }
 
-static NMDeviceType
-get_device_type (NMDeviceFactory *factory)
-{
-	return NM_DEVICE_TYPE_ADSL;
-}
+NM_DEVICE_FACTORY_DECLARE_TYPES (
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_ADSL_SETTING_NAME)
+)
 
 /*********************************************************************/
 
@@ -224,7 +223,7 @@ nm_atm_manager_init (NMAtmManager *self)
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
-	factory_iface->get_device_type = get_device_type;
+	factory_iface->get_supported_types = get_supported_types;
 	factory_iface->start = start;
 }
 
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index e664ff48..4d2cac5b 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -73,7 +73,7 @@ typedef struct {
 
 /**************************************************************/
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *dev)
 {
 	return (  NM_DEVICE_CAP_CARRIER_DETECT
@@ -144,7 +144,7 @@ set_nas_iface (NMDeviceAdsl *self, int idx, const char *name)
 	g_return_if_fail (name != NULL);
 
 	g_warn_if_fail (priv->nas_ifindex <= 0);
-	priv->nas_ifindex = idx > 0 ? idx : nm_platform_link_get_ifindex (name);
+	priv->nas_ifindex = idx > 0 ? idx : nm_platform_link_get_ifindex (NM_PLATFORM_GET, name);
 	g_warn_if_fail (priv->nas_ifindex > 0);
 
 	g_warn_if_fail (priv->nas_ifname == NULL);
@@ -295,7 +295,7 @@ error:
 }
 
 static void
-link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDeviceAdsl *self)
+link_changed_cb (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDeviceAdsl *self)
 {
 	if (change_type == NM_PLATFORM_SIGNAL_REMOVED) {
 		NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
@@ -352,7 +352,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
 		_LOGD (LOGD_ADSL, "ATM setup successful");
 
 		/* otherwise we're good for stage3 */
-		nm_platform_link_set_up (priv->nas_ifindex);
+		nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex, NULL);
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
 
 	} else if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) == 0) {
@@ -492,7 +492,7 @@ carrier_update_cb (gpointer user_data)
 
 	path  = g_strdup_printf ("/sys/class/atm/%s/carrier",
 	                         ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (NM_DEVICE (self))));
-	carrier = (int) nm_platform_sysctl_get_int_checked (path, 10, 0, 1, -1);
+	carrier = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, path, 10, 0, 1, -1);
 	g_free (path);
 
 	if (carrier != -1)
@@ -526,7 +526,7 @@ get_atm_index (const char *iface)
 
 	path = g_strdup_printf ("/sys/class/atm/%s/atmindex",
 	                        ASSERT_VALID_PATH_COMPONENT (iface));
-	idx = (int) nm_platform_sysctl_get_int_checked (path, 10, 0, G_MAXINT, -1);
+	idx = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, path, 10, 0, G_MAXINT, -1);
 	g_free (path);
 
 	return idx;
diff --git a/src/devices/bluetooth/Makefile.in b/src/devices/bluetooth/Makefile.in
index b8e6dcda..6233a751 100644
--- a/src/devices/bluetooth/Makefile.in
+++ b/src/devices/bluetooth/Makefile.in
@@ -243,6 +243,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -356,6 +357,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index fe1a97bb..d5426052 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -28,6 +28,7 @@
 
 #include "nm-core-internal.h"
 
+#include "nm-bt-error.h"
 #include "nm-bluez-common.h"
 #include "nm-bluez-device.h"
 #include "nm-logging.h"
@@ -436,8 +437,6 @@ nm_bluez_device_disconnect (NMBluezDevice *self)
 		} else if (priv->bluez_version == 5) {
 #if WITH_BLUEZ5_DUN
 			nm_bluez5_dun_cleanup (priv->b5_dun_context);
-#else
-			g_assert_not_reached ();
 #endif
 			priv->connected = FALSE;
 			goto out;
@@ -559,7 +558,11 @@ nm_bluez_device_connect_async (NMBluezDevice *self,
 				priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address);
 			nm_bluez5_dun_connect (priv->b5_dun_context, bluez5_dun_connect_cb, simple);
 #else
-			g_assert_not_reached ();
+			g_simple_async_result_set_error (simple,
+							 NM_BT_ERROR,
+							 NM_BT_ERROR_DUN_CONNECT_FAILED,
+							 "NetworkManager built without support for Bluez 5");
+			g_simple_async_result_complete (simple);
 #endif
 			return;
 		}
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index e176a4b4..df4eb46d 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -29,6 +29,7 @@
 #include "nm-logging.h"
 #include "nm-bluez-manager.h"
 #include "nm-device-factory.h"
+#include "nm-setting-bluetooth.h"
 #include "nm-bluez4-manager.h"
 #include "nm-bluez5-manager.h"
 #include "nm-bluez-device.h"
@@ -37,6 +38,7 @@
 #include "nm-device-bt.h"
 
 #include "nm-dbus-manager.h"
+#include "nm-platform.h"
 
 typedef struct {
 	int bluez_version;
@@ -368,11 +370,10 @@ start (NMDeviceFactory *factory)
 	check_bluez_and_try_setup (NM_BLUEZ_MANAGER (factory));
 }
 
-static NMDeviceType
-get_device_type (NMDeviceFactory *factory)
-{
-	return NM_DEVICE_TYPE_BT;
-}
+NM_DEVICE_FACTORY_DECLARE_TYPES (
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_BNEP)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BLUETOOTH_SETTING_NAME)
+)
 
 /*********************************************************************/
 
@@ -405,10 +406,19 @@ nm_bluez_manager_init (NMBluezManager *self)
 	g_assert (priv->provider);
 }
 
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+{
+	g_warn_if_fail (plink->type == NM_LINK_TYPE_BNEP);
+	*out_ignore = TRUE;
+	return NULL;
+}
+
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
-	factory_iface->get_device_type = get_device_type;
+	factory_iface->get_supported_types = get_supported_types;
+	factory_iface->new_link = new_link;
 	factory_iface->start = start;
 }
 
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index 61371ddd..88033e2e 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -124,7 +124,7 @@ get_connection_bt_type (NMConnection *connection)
 	return NM_BT_CAPABILITY_NONE;
 }
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *device)
 {
 	return NM_DEVICE_CAP_IS_NON_KERNEL;
@@ -479,6 +479,13 @@ device_state_changed (NMDevice *device,
 
 	if (priv->modem)
 		nm_modem_device_state_changed (priv->modem, new_state, old_state, reason);
+
+	/* Need to recheck available connections whenever MM appears or disappears,
+	 * since the device could be both DUN and NAP capable and thus may not
+	 * change state (which rechecks available connections) when MM comes and goes.
+	 */
+	if (priv->mm_running && (priv->capabilities & NM_BT_CAPABILITY_DUN))
+	    nm_device_recheck_available_connections (device);
 }
 
 static void
@@ -944,60 +951,19 @@ is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags)
 }
 
 static void
-handle_availability_change (NMDeviceBt *self,
-                            gboolean old_available,
-                            NMDeviceStateReason unavailable_reason)
-{
-	NMDevice *device = NM_DEVICE (self);
-	NMDeviceState state;
-	gboolean available;
-
-	state = nm_device_get_state (device);
-	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
-		_LOGD (LOGD_BT, "availability blocked by UNMANAGED state");
-		return;
-	}
-
-	available = nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
-	if (available == old_available)
-		return;
-
-	if (available) {
-		if (state != NM_DEVICE_STATE_UNAVAILABLE)
-			_LOGW (LOGD_CORE | LOGD_BT, "not in expected unavailable state!");
-
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_DISCONNECTED,
-		                         NM_DEVICE_STATE_REASON_NONE);
-	} else {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_UNAVAILABLE,
-		                         unavailable_reason);
-	}
-}
-
-static void
 set_mm_running (NMDeviceBt *self, gboolean running)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-	gboolean old_available;
 
-	if (priv->mm_running == running)
-		return;
-
-	_LOGD (LOGD_BT, "ModemManager now %s",
-	       running ? "available" : "unavailable");
+	if (priv->mm_running != running) {
+		_LOGD (LOGD_BT, "ModemManager now %s",
+		       running ? "available" : "unavailable");
 
-	old_available = nm_device_is_available (NM_DEVICE (self), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
-	priv->mm_running = running;
-	handle_availability_change (self, old_available, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
-
-	/* Need to recheck available connections whenever MM appears or disappears,
-	 * since the device could be both DUN and NAP capable and thus may not
-	 * change state (which rechecks available connections) when MM comes and goes.
-	 */
-	if (priv->capabilities & NM_BT_CAPABILITY_DUN)
-	    nm_device_recheck_available_connections (NM_DEVICE (self));
+		priv->mm_running = running;
+		nm_device_queue_recheck_available (NM_DEVICE (self),
+		                                   NM_DEVICE_STATE_REASON_NONE,
+		                                   NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
+	}
 }
 
 static void
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 0b2c97d1..7f139424 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -37,6 +37,7 @@
 #include "nm-enum-types.h"
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
+#include "nm-ip4-config.h"
 
 #include "nm-device-bond-glue.h"
 
@@ -60,18 +61,16 @@ enum {
 
 /******************************************************************/
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *dev)
 {
-	return NM_DEVICE_CAP_CARRIER_DETECT;
+	return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
 }
 
 static gboolean
 is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags)
 {
-	if (NM_DEVICE_GET_CLASS (dev)->is_up)
-		return NM_DEVICE_GET_CLASS (dev)->is_up (dev);
-	return FALSE;
+	return TRUE;
 }
 
 static gboolean
@@ -144,7 +143,7 @@ set_bond_attr (NMDevice *device, const char *attr, const char *value)
 	gboolean ret;
 	int ifindex = nm_device_get_ifindex (device);
 
-	ret = nm_platform_master_set_option (ifindex, attr, value);
+	ret = nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, attr, value);
 	if (!ret)
 		_LOGW (LOGD_HW, "failed to set bonding attribute '%s' to '%s'", attr, value);
 	return ret;
@@ -178,7 +177,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 	/* Read bond options from sysfs and update the Bond setting to match */
 	options = nm_setting_bond_get_valid_options (s_bond);
 	while (options && *options) {
-		gs_free char *value = nm_platform_master_get_option (ifindex, *options);
+		gs_free char *value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, *options);
 		const char *defvalue = nm_setting_bond_get_option_default (s_bond, *options);
 
 		if (value && !ignore_if_zero (*options, value) && (g_strcmp0 (value, defvalue) != 0)) {
@@ -334,7 +333,7 @@ apply_bonding_config (NMDevice *device)
 	}
 
 	/* Clear ARP targets */
-	contents = nm_platform_master_get_option (ifindex, "arp_ip_target");
+	contents = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "arp_ip_target");
 	set_arp_targets (device, contents, " \n", "-");
 	g_free (contents);
 
@@ -377,6 +376,25 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	return ret;
 }
 
+static void
+ip4_config_pre_commit (NMDevice *self, NMIP4Config *config)
+{
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	guint32 mtu;
+
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
+	s_wired = nm_connection_get_setting_wired (connection);
+
+	if (s_wired) {
+		/* MTU override */
+		mtu = nm_setting_wired_get_mtu (s_wired);
+		if (mtu)
+			nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
+	}
+}
+
 static gboolean
 enslave_slave (NMDevice *device,
                NMDevice *slave,
@@ -391,7 +409,8 @@ enslave_slave (NMDevice *device,
 
 	if (configure) {
 		nm_device_take_down (slave, TRUE);
-		success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device),
+		success = nm_platform_link_enslave (NM_PLATFORM_GET,
+		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 		nm_device_bring_up (slave, TRUE, &no_firmware);
 
@@ -415,7 +434,8 @@ release_slave (NMDevice *device,
 	gboolean success = TRUE, no_firmware = FALSE;
 
 	if (configure) {
-		success = nm_platform_link_release (nm_device_get_ip_ifindex (device),
+		success = nm_platform_link_release (NM_PLATFORM_GET,
+		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 
 		if (success) {
@@ -510,6 +530,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 	parent_class->master_update_slave_connection = master_update_slave_connection;
 
 	parent_class->act_stage1_prepare = act_stage1_prepare;
+	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
 	parent_class->enslave_slave = enslave_slave;
 	parent_class->release_slave = release_slave;
 
@@ -532,18 +553,15 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 #define NM_BOND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BOND_FACTORY, NMBondFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_BOND) {
-		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND,
-		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-		                                  NM_DEVICE_DRIVER, "bonding",
-		                                  NM_DEVICE_TYPE_DESC, "Bond",
-		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND,
-		                                  NM_DEVICE_IS_MASTER, TRUE,
-		                                  NULL);
-	}
-	return NULL;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_DRIVER, "bonding",
+	                                  NM_DEVICE_TYPE_DESC, "Bond",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND,
+	                                  NM_DEVICE_IS_MASTER, TRUE,
+	                                  NULL);
 }
 
 static NMDevice *
@@ -552,19 +570,18 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
                                       NMDevice *parent,
                                       GError **error)
 {
-	const char *iface;
-
-	if (!nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME))
-		return NULL;
-
-	iface = nm_connection_get_interface_name (connection);
-	g_return_val_if_fail (iface != NULL, NULL);
-
-	if (   !nm_platform_bond_add (iface)
-		&& nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_BOND, "(%s): failed to create bonding master interface for '%s': %s",
-			         iface, nm_connection_get_id (connection),
-			         nm_platform_get_error_msg ());
+	const char *iface = nm_connection_get_interface_name (connection);
+	NMPlatformError plerr;
+
+	g_assert (iface);
+
+	plerr = nm_platform_bond_add (NM_PLATFORM_GET, iface, NULL);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create bond interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
 		return NULL;
 	}
 
@@ -577,7 +594,9 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 		                              NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL(BOND, Bond, bond,
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (BOND, Bond, bond,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_BOND)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BOND_SETTING_NAME),
 	factory_iface->new_link = new_link;
 	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
 	)
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index a2ed4b41..e1cafd60 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -59,18 +59,16 @@ enum {
 
 /******************************************************************/
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *dev)
 {
-	return NM_DEVICE_CAP_CARRIER_DETECT;
+	return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
 }
 
 static gboolean
 is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags)
 {
-	if (NM_DEVICE_GET_CLASS (dev)->is_up)
-		return NM_DEVICE_GET_CLASS (dev)->is_up (dev);
-	return FALSE;
+	return TRUE;
 }
 
 static gboolean
@@ -213,9 +211,9 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool
 
 	value = g_strdup_printf ("%u", uval);
 	if (slave)
-		nm_platform_slave_set_option (ifindex, option->sysname, value);
+		nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value);
 	else
-		nm_platform_master_set_option (ifindex, option->sysname, value);
+		nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value);
 }
 
 static void
@@ -259,7 +257,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 	}
 
 	for (option = master_options; option->name; option++) {
-		gs_free char *str = nm_platform_master_get_option (ifindex, option->sysname);
+		gs_free char *str = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, option->sysname);
 		int value;
 
 		if (str) {
@@ -298,7 +296,7 @@ master_update_slave_connection (NMDevice *device,
 	}
 
 	for (option = slave_options; option->name; option++) {
-		gs_free char *str = nm_platform_slave_get_option (ifindex_slave, option->sysname);
+		gs_free char *str = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex_slave, option->sysname);
 		int value;
 
 		if (str) {
@@ -346,7 +344,7 @@ enslave_slave (NMDevice *device,
 	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
 
 	if (configure) {
-		if (!nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)))
+		if (!nm_platform_link_enslave (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)))
 			return FALSE;
 
 		commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection));
@@ -372,7 +370,8 @@ release_slave (NMDevice *device,
 	gboolean success = TRUE;
 
 	if (configure) {
-		success = nm_platform_link_release (nm_device_get_ip_ifindex (device),
+		success = nm_platform_link_release (NM_PLATFORM_GET,
+		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 
 		if (success) {
@@ -477,18 +476,15 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 #define NM_BRIDGE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BRIDGE_FACTORY, NMBridgeFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_BRIDGE) {
-		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE,
-		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-		                                  NM_DEVICE_DRIVER, "bridge",
-		                                  NM_DEVICE_TYPE_DESC, "Bridge",
-		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE,
-		                                  NM_DEVICE_IS_MASTER, TRUE,
-		                                  NULL);
-	}
-	return NULL;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_DRIVER, "bridge",
+	                                  NM_DEVICE_TYPE_DESC, "Bridge",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE,
+	                                  NM_DEVICE_IS_MASTER, TRUE,
+	                                  NULL);
 }
 
 static NMDevice *
@@ -497,21 +493,16 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
                                       NMDevice *parent,
                                       GError **error)
 {
-	const char *iface;
+	const char *iface = nm_connection_get_interface_name (connection);
 	NMSettingBridge *s_bridge;
 	const char *mac_address_str;
 	guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX];
+	NMPlatformError plerr;
 
-	if (!nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME))
-		return NULL;
-
-	g_return_val_if_fail (connection != NULL, NULL);
-
-	iface = nm_connection_get_interface_name (connection);
-	g_return_val_if_fail (iface != NULL, NULL);
+	g_assert (iface);
 
 	s_bridge = nm_connection_get_setting_bridge (connection);
-	g_return_val_if_fail (s_bridge, NULL);
+	g_assert (s_bridge);
 
 	mac_address_str = nm_setting_bridge_get_mac_address (s_bridge);
 	if (mac_address_str) {
@@ -519,13 +510,17 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 			mac_address_str = NULL;
 	}
 
-	if (   !nm_platform_bridge_add (iface,
+	plerr = nm_platform_bridge_add (NM_PLATFORM_GET,
+	                                iface,
 	                                mac_address_str ? mac_address : NULL,
-	                                mac_address_str ? ETH_ALEN : 0)
-	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_BRIDGE, "(%s): failed to create bridge master interface for '%s': %s",
-		             iface, nm_connection_get_id (connection),
-		             nm_platform_get_error_msg ());
+	                                mac_address_str ? ETH_ALEN : 0,
+	                                NULL);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create bridge interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
 		return NULL;
 	}
 
@@ -538,7 +533,9 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 	                                  NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL(BRIDGE, Bridge, bridge,
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (BRIDGE, Bridge, bridge,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_BRIDGE)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BRIDGE_SETTING_NAME),
 	factory_iface->new_link = new_link;
 	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
 	)
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index ac408d3d..af2cb8f2 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -26,10 +26,6 @@
 #include <netinet/in.h>
 #include <string.h>
 #include <stdlib.h>
-#include <linux/sockios.h>
-#include <linux/ethtool.h>
-#include <linux/version.h>
-#include <sys/ioctl.h>
 #include <unistd.h>
 #include <errno.h>
 
@@ -48,6 +44,7 @@
 #include "nm-enum-types.h"
 #include "nm-dbus-manager.h"
 #include "nm-platform.h"
+#include "nm-platform-utils.h"
 #include "nm-dcb.h"
 #include "nm-settings-connection.h"
 #include "nm-config.h"
@@ -55,6 +52,7 @@
 #include "nm-connection-provider.h"
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
+#include "NetworkManagerUtils.h"
 
 #include "nm-device-ethernet-glue.h"
 
@@ -99,9 +97,6 @@ typedef enum {
 } DcbWait;
 
 typedef struct {
-	char *              perm_hw_addr;    /* Permanent MAC address */
-	char *              initial_hw_addr; /* Initial MAC address (as seen when NM starts) */
-
 	guint32             speed;
 
 	Supplicant          supplicant;
@@ -156,26 +151,21 @@ static void
 _update_s390_subchannels (NMDeviceEthernet *self)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	GUdevClient *client;
 	GUdevDevice *dev;
 	GUdevDevice *parent = NULL;
 	const char *parent_path, *item, *driver;
-	const char *subsystems[] = { "net", NULL };
+	int ifindex;
 	GDir *dir;
 	GError *error = NULL;
 
-	client = g_udev_client_new (subsystems);
-	if (!client) {
-		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to initialize GUdev client");
-		return;
-	}
-
-	dev = g_udev_client_query_by_subsystem_and_name (client, "net",
-	                                                 nm_device_get_iface (NM_DEVICE (self)));
+	ifindex = nm_device_get_ifindex (NM_DEVICE (self));
+	dev = (GUdevDevice *) nm_platform_link_get_udev_device (NM_PLATFORM_GET, ifindex);
 	if (!dev) {
-		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device with udev");
+		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device %d '%s' with udev",
+		       ifindex, str_if_set (nm_device_get_iface (NM_DEVICE (self)), "(null)"));
 		goto out;
 	}
+	g_object_ref (dev);
 
 	/* Try for the "ccwgroup" parent */
 	parent = g_udev_device_get_parent_with_subsystem (dev, "ccwgroup", NULL);
@@ -210,7 +200,7 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 		           || !strcmp (item, "portno")) {
 			char *path, *value;
 			path = g_strdup_printf ("%s/%s", parent_path, item);
-			value = nm_platform_sysctl_get (path);
+			value = nm_platform_sysctl_get (NM_PLATFORM_GET, path);
 			if (value && *value)
 				g_hash_table_insert (priv->s390_options, g_strdup (item), g_strdup (value));
 			else
@@ -247,7 +237,6 @@ out:
 		g_object_unref (parent);
 	if (dev)
 		g_object_unref (dev);
-	g_object_unref (client);
 }
 
 static GObject*
@@ -263,7 +252,7 @@ constructor (GType type,
 	if (object) {
 #ifndef G_DISABLE_ASSERT
 		int ifindex = nm_device_get_ifindex (NM_DEVICE (object));
-		NMLinkType link_type = nm_platform_link_get_type (ifindex);
+		NMLinkType link_type = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex);
 
 		g_assert (   link_type == NM_LINK_TYPE_ETHERNET
 		          || link_type == NM_LINK_TYPE_VETH
@@ -310,72 +299,12 @@ nm_device_ethernet_init (NMDeviceEthernet *self)
 	priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 }
 
-static void
-update_permanent_hw_address (NMDevice *dev)
-{
-	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	struct ifreq req;
-	struct ethtool_perm_addr *epaddr = NULL;
-	int fd, ret, errsv;
-	const char *mac;
-
-	g_return_if_fail (priv->perm_hw_addr == NULL);
-
-	fd = socket (PF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		_LOGW (LOGD_HW, "couldn't open control socket.");
-		return;
-	}
-
-	/* Get permanent MAC address */
-	memset (&req, 0, sizeof (struct ifreq));
-	strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
-
-	epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN);
-	epaddr->cmd = ETHTOOL_GPERMADDR;
-	epaddr->size = ETH_ALEN;
-	req.ifr_data = (void *) epaddr;
-
-	errno = 0;
-	ret = ioctl (fd, SIOCETHTOOL, &req);
-	errsv = errno;
-	if ((ret < 0) || !nm_ethernet_address_is_valid (epaddr->data, ETH_ALEN)) {
-		_LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address (error %d)", errsv);
-		/* Fall back to current address */
-		mac = nm_device_get_hw_address (dev);
-		if (mac)
-			nm_utils_hwaddr_aton (mac, epaddr->data, ETH_ALEN);
-		else
-			memset (epaddr->data, 0, ETH_ALEN);
-	}
-
-	priv->perm_hw_addr = nm_utils_hwaddr_ntoa (epaddr->data, ETH_ALEN);
-
-	g_free (epaddr);
-	close (fd);
-}
-
-static void
-update_initial_hw_address (NMDevice *dev)
-{
-	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-
-	/* This sets initial MAC address from current MAC address. It should only
-	 * be called from NMDevice constructor() to really get the initial address.
-	 */
-	priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (dev));
-
-	_LOGD (LOGD_DEVICE | LOGD_ETHER, "read initial MAC address %s", priv->initial_hw_addr);
-}
-
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *device)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
 
-	if (nm_platform_link_supports_carrier_detect (nm_device_get_ifindex (device)))
+	if (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, nm_device_get_ifindex (device)))
 	    return NM_DEVICE_CAP_CARRIER_DETECT;
 	else {
 		_LOGI (LOGD_HW, "driver '%s' does not support carrier detection.",
@@ -421,7 +350,6 @@ static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMSettingWired *s_wired;
 
 	if (!NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->check_connection_compatible (device, connection))
@@ -438,7 +366,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	if (s_wired) {
-		const char *mac;
+		const char *mac, *perm_hw_addr;
 		gboolean try_mac = TRUE;
 		const char * const *mac_blacklist;
 		int i;
@@ -446,21 +374,25 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		if (!match_subchans (self, s_wired, &try_mac))
 			return FALSE;
 
+		perm_hw_addr = nm_device_get_permanent_hw_address (device);
 		mac = nm_setting_wired_get_mac_address (s_wired);
-		if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, priv->perm_hw_addr, -1))
-			return FALSE;
-
-		/* Check for MAC address blacklist */
-		mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
-		for (i = 0; mac_blacklist[i]; i++) {
-			if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
-				g_warn_if_reached ();
+		if (perm_hw_addr) {
+			if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
 				return FALSE;
-			}
 
-			if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, priv->perm_hw_addr, -1))
-				return FALSE;
-		}
+			/* Check for MAC address blacklist */
+			mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
+			for (i = 0; mac_blacklist[i]; i++) {
+				if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
+					g_warn_if_reached ();
+					return FALSE;
+				}
+
+				if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1))
+					return FALSE;
+			}
+		} else if (mac)
+			return FALSE;
 	}
 
 	return TRUE;
@@ -1160,7 +1092,7 @@ dcb_state (NMDevice *device, gboolean timeout)
 	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
 
 
-	carrier = nm_platform_link_is_connected (nm_device_get_ifindex (device));
+	carrier = nm_platform_link_is_connected (NM_PLATFORM_GET, nm_device_get_ifindex (device));
 	_LOGD (LOGD_DCB, "dcb_state() wait %d carrier %d timeout %d", priv->dcb_wait, carrier, timeout);
 
 	switch (priv->dcb_wait) {
@@ -1278,7 +1210,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB);
 	if (s_dcb) {
 		/* lldpad really really wants the carrier to be up */
-		if (nm_platform_link_is_connected (nm_device_get_ifindex (device))) {
+		if (nm_platform_link_is_connected (NM_PLATFORM_GET, nm_device_get_ifindex (device))) {
 			if (!dcb_enable (device)) {
 				*reason = NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED;
 				return NM_ACT_STAGE_RETURN_FAILURE;
@@ -1391,8 +1323,8 @@ deactivate (NMDevice *device)
 		NM_DEVICE_ETHERNET_GET_PRIVATE (device)->last_pppoe_time = nm_utils_get_monotonic_timestamp_s ();
 
 	/* Reset MAC address back to initial address */
-	if (priv->initial_hw_addr)
-		nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_ETHER);
+	if (nm_device_get_initial_hw_address (device))
+		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_ETHER);
 }
 
 static gboolean
@@ -1402,10 +1334,10 @@ complete_connection (NMDevice *device,
                      const GSList *existing_connections,
                      GError **error)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 	NMSettingWired *s_wired;
 	NMSettingPppoe *s_pppoe;
 	const char *setting_mac;
+	const char *perm_hw_addr;
 
 	s_pppoe = nm_connection_get_setting_pppoe (connection);
 
@@ -1432,21 +1364,22 @@ complete_connection (NMDevice *device,
 		nm_connection_add_setting (connection, NM_SETTING (s_wired));
 	}
 
-	setting_mac = nm_setting_wired_get_mac_address (s_wired);
-	if (setting_mac) {
-		/* Make sure the setting MAC (if any) matches the device's permanent MAC */
-		if (!nm_utils_hwaddr_matches (setting_mac, -1, priv->perm_hw_addr, -1)) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("connection does not match device"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS);
-			return FALSE;
-		}
-	} else {
-		if (!nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, NULL, ETH_ALEN)) {
+	perm_hw_addr = nm_device_get_permanent_hw_address (device);
+	if (perm_hw_addr) {
+		setting_mac = nm_setting_wired_get_mac_address (s_wired);
+		if (setting_mac) {
+			/* Make sure the setting MAC (if any) matches the device's permanent MAC */
+			if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) {
+				g_set_error_literal (error,
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("connection does not match device"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS);
+				return FALSE;
+			}
+		} else {
 			g_object_set (G_OBJECT (s_wired),
-			              NM_SETTING_WIRED_MAC_ADDRESS, priv->perm_hw_addr,
+			              NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr,
 			              NULL);
 		}
 	}
@@ -1463,7 +1396,7 @@ new_default_connection (NMDevice *self)
 	const char *hw_address;
 	char *defname, *uuid;
 
-	if (!nm_config_get_ethernet_can_auto_default (nm_config_get (), self))
+	if (nm_config_get_no_auto_default_for_device (nm_config_get (), self))
 		return NULL;
 
 	hw_address = nm_device_get_hw_address (self);
@@ -1495,15 +1428,19 @@ new_default_connection (NMDevice *self)
 	return connection;
 }
 
-static gboolean
+static NMMatchSpecMatchType
 spec_match_list (NMDevice *device, const GSList *specs)
 {
+	NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m;
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 
-	if (priv->subchannels && nm_match_spec_s390_subchannels (specs, priv->subchannels))
-		return TRUE;
-
-	return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->spec_match_list (device, specs);
+	if (priv->subchannels)
+		matched = nm_match_spec_s390_subchannels (specs, priv->subchannels);
+	if (matched != NM_MATCH_SPEC_NEG_MATCH) {
+		m = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->spec_match_list (device, specs);
+		matched = MAX (matched, m);
+	}
+	return matched;
 }
 
 static void
@@ -1511,6 +1448,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 	NMSettingWired *s_wired = nm_connection_get_setting_wired (connection);
+	const char *perm_hw_addr = nm_device_get_permanent_hw_address (device);
 	const char *mac = nm_device_get_hw_address (device);
 	const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
 	GHashTableIter iter;
@@ -1524,11 +1462,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 	/* If the device reports a permanent address, use that for the MAC address
 	 * and the current MAC, if different, is the cloned MAC.
 	 */
-	if (!nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, NULL, ETH_ALEN)) {
-		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, priv->perm_hw_addr, NULL);
+	if (perm_hw_addr) {
+		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL);
 
 		mac_prop = NULL;
-		if (mac && !nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, mac, -1))
+		if (mac && !nm_utils_hwaddr_matches (perm_hw_addr, -1, mac, -1))
 			mac_prop = NM_SETTING_WIRED_CLONED_MAC_ADDRESS;
 	}
 
@@ -1562,37 +1500,10 @@ get_link_speed (NMDevice *device)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	struct ifreq ifr;
-	struct ethtool_cmd edata = {
-		.cmd = ETHTOOL_GSET,
-	};
 	guint32 speed;
-	int fd;
 
-	fd = socket (PF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		_LOGW (LOGD_HW | LOGD_ETHER, "couldn't open ethtool control socket.");
+	if (!nmp_utils_ethtool_get_link_speed (nm_device_get_iface (device), &speed))
 		return;
-	}
-
-	memset (&ifr, 0, sizeof (struct ifreq));
-	strncpy (ifr.ifr_name, nm_device_get_iface (device), IFNAMSIZ);
-	ifr.ifr_data = (char *) &edata;
-
-	if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) {
-		close (fd);
-		return;
-	}
-	close (fd);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
-	speed = edata.speed;
-#else
-	speed = ethtool_cmd_speed (&edata);
-#endif
-	if (speed == G_MAXUINT16 || speed == G_MAXUINT32)
-		speed = 0;
-
 	if (priv->speed == speed)
 		return;
 
@@ -1612,6 +1523,17 @@ carrier_changed (NMDevice *device, gboolean carrier)
 }
 
 static void
+link_changed (NMDevice *device, NMPlatformLink *info)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->link_changed (device, info);
+	if (!priv->subchan1 && info->initialized)
+		_update_s390_subchannels (self);
+}
+
+static void
 dispose (GObject *object)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
@@ -1634,8 +1556,6 @@ finalize (GObject *object)
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
-	g_free (priv->perm_hw_addr);
-	g_free (priv->initial_hw_addr);
 	g_clear_object (&priv->supplicant.mgr);
 	g_free (priv->subchan1);
 	g_free (priv->subchan2);
@@ -1656,7 +1576,7 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_PERM_HW_ADDRESS:
-		g_value_set_string (value, priv->perm_hw_addr);
+		g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (object)));
 		break;
 	case PROP_SPEED:
 		g_value_set_uint (value, priv->speed);
@@ -1696,8 +1616,6 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	object_class->set_property = set_property;
 
 	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->update_permanent_hw_address = update_permanent_hw_address;
-	parent_class->update_initial_hw_address = update_initial_hw_address;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
 	parent_class->new_default_connection = new_default_connection;
@@ -1710,6 +1628,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	parent_class->spec_match_list = spec_match_list;
 	parent_class->update_connection = update_connection;
 	parent_class->carrier_changed = carrier_changed;
+	parent_class->link_changed = link_changed;
 
 	parent_class->state_changed = device_state_changed;
 
@@ -1739,19 +1658,18 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 #define NM_ETHERNET_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ETHERNET_FACTORY, NMEthernetFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_ETHERNET) {
-		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET,
-		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-		                                  NM_DEVICE_TYPE_DESC, "Ethernet",
-		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
-		                                  NULL);
-	}
-	return NULL;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_TYPE_DESC, "Ethernet",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
+	                                  NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL(ETHERNET, Ethernet, ethernet, \
-	factory_iface->new_link = new_link; \
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (ETHERNET, Ethernet, ethernet,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_ETHERNET)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_PPPOE_SETTING_NAME),
+	factory_iface->new_link = new_link;
 	)
 
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index abfb8d4a..13034808 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -19,8 +19,19 @@
  */
 
 #include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+
+#include <gmodule.h>
 
 #include "nm-device-factory.h"
+#include "nm-logging.h"
+#include "nm-platform.h"
+
+const NMLinkType _nm_device_factory_no_default_links[] = { NM_LINK_TYPE_NONE };
+const char *_nm_device_factory_no_default_settings[] = { NULL };
 
 enum {
 	DEVICE_ADDED,
@@ -29,130 +40,509 @@ enum {
 };
 static guint signals[LAST_SIGNAL] = { 0 };
 
-static GSList *internal_types = NULL;
+gboolean
+nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component)
+{
+	gboolean consumed = FALSE;
+
+	g_signal_emit (factory, signals[COMPONENT_ADDED], 0, component, &consumed);
+	return consumed;
+}
 
 void
-_nm_device_factory_internal_register_type (GType factory_type)
+nm_device_factory_get_supported_types (NMDeviceFactory *factory,
+                                       const NMLinkType **out_link_types,
+                                       const char ***out_setting_types)
 {
-	g_return_if_fail (g_slist_find (internal_types, GUINT_TO_POINTER (factory_type)) == NULL);
-	internal_types = g_slist_prepend (internal_types, GUINT_TO_POINTER (factory_type));
+	const NMLinkType *link_types_fallback;
+	const char **setting_types_fallback;
+
+	g_return_if_fail (factory != NULL);
+
+	if (!out_link_types)
+		out_link_types = &link_types_fallback;
+	if (!out_setting_types)
+		out_setting_types = &setting_types_fallback;
+
+	NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_supported_types (factory,
+	                                                                out_link_types,
+	                                                                out_setting_types);
 }
 
-const GSList *
-nm_device_factory_get_internal_factory_types (void)
+void
+nm_device_factory_start (NMDeviceFactory *factory)
 {
-	return internal_types;
+	g_return_if_fail (factory != NULL);
+
+	if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start)
+		NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start (factory);
 }
 
-gboolean
-nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component)
+NMDevice *
+nm_device_factory_new_link (NMDeviceFactory *factory,
+                            NMPlatformLink *plink,
+                            gboolean *out_ignore,
+                            GError **error)
 {
-	gboolean consumed = FALSE;
+	NMDeviceFactory *interface;
+	const NMLinkType *link_types = NULL;
+	const char **setting_types = NULL;
+	int i;
 
-	g_signal_emit (factory, signals[COMPONENT_ADDED], 0, component, &consumed);
-	return consumed;
+	g_return_val_if_fail (factory != NULL, NULL);
+	g_return_val_if_fail (plink != NULL, NULL);
+
+	/* Ensure the factory can create interfaces for this connection */
+	nm_device_factory_get_supported_types (factory, &link_types, &setting_types);
+	for (i = 0; link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) {
+		if (plink->type == link_types[i])
+			break;
+	}
+
+	if (link_types[i] == NM_LINK_TYPE_UNKNOWN) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Device factory %s does not support link type %s (%d)",
+		             G_OBJECT_TYPE_NAME (factory),
+		             plink->kind, plink->type);
+		return NULL;
+	}
+
+	interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory);
+	if (!interface->new_link) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+		             "Device factory %s cannot manage new devices",
+		             G_OBJECT_TYPE_NAME (factory));
+		return NULL;
+	}
+
+	return interface->new_link (factory, plink, out_ignore, error);
 }
 
-static void
-interface_init (gpointer g_iface)
+NMDevice *
+nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory,
+                                                        NMConnection *connection,
+                                                        NMDevice *parent,
+                                                        GError **error)
 {
-	GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
-	static gboolean initialized = FALSE;
+	NMDeviceFactory *interface;
+	const char **setting_types = NULL;
+	gboolean found = FALSE;
+	int i;
 
-	if (G_LIKELY (initialized))
-		return;
+	g_return_val_if_fail (factory, NULL);
+	g_return_val_if_fail (connection, NULL);
+	g_return_val_if_fail (!error || !*error, NULL);
+
+	/* Ensure the factory can create interfaces for this connection */
+	nm_device_factory_get_supported_types (factory, NULL, &setting_types);
+	for (i = 0; setting_types && setting_types[i]; i++) {
+		if (nm_connection_is_type (connection, setting_types[i])) {
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (!found) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+		             "Device factory %s does not support connection type %s",
+		             G_OBJECT_TYPE_NAME (factory),
+		             nm_connection_get_connection_type (connection));
+		return NULL;
+	}
+
+	interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory);
+	if (!interface->create_virtual_device_for_connection) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+		             "Device factory %s cannot create virtual devices",
+		             G_OBJECT_TYPE_NAME (factory));
+		return NULL;
+	}
+
+	return interface->create_virtual_device_for_connection (factory, connection, parent, error);
+}
+
+const char *
+nm_device_factory_get_connection_parent (NMDeviceFactory *factory,
+                                         NMConnection *connection)
+{
+	g_return_val_if_fail (factory != NULL, NULL);
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	if (!nm_connection_is_virtual (connection))
+		return NULL;
+
+	if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_connection_parent)
+		return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_connection_parent (factory, connection);
+	return NULL;
+}
+
+static char *
+get_virtual_iface_name (NMDeviceFactory *factory,
+                        NMConnection *connection,
+                        const char *parent_iface)
+{
+	const char *iface;
+
+	/* For any other virtual connection, NMSettingConnection:interface-name is
+	 * the virtual device name.
+	 */
+	iface = nm_connection_get_interface_name (connection);
+	g_return_val_if_fail (iface != NULL, NULL);
+	return g_strdup (iface);
+}
+
+char *
+nm_device_factory_get_virtual_iface_name (NMDeviceFactory *factory,
+                                          NMConnection *connection,
+                                          const char *parent_iface)
+{
+	g_return_val_if_fail (factory != NULL, NULL);
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	if (!nm_connection_is_virtual (connection))
+		return NULL;
+
+	if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_virtual_iface_name)
+		return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_virtual_iface_name (factory, connection, parent_iface);
+	return NULL;
+}
+
+/*******************************************************************/
+
+static void
+default_init (NMDeviceFactory *factory_iface)
+{
+	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
 
 	/* Signals */
 	signals[DEVICE_ADDED] = g_signal_new (NM_DEVICE_FACTORY_DEVICE_ADDED,
-	                                      iface_type,
+	                                      NM_TYPE_DEVICE_FACTORY,
 	                                      G_SIGNAL_RUN_FIRST,
 	                                      G_STRUCT_OFFSET (NMDeviceFactory, device_added),
 	                                      NULL, NULL, NULL,
 	                                      G_TYPE_NONE, 1, NM_TYPE_DEVICE);
 
 	signals[COMPONENT_ADDED] = g_signal_new (NM_DEVICE_FACTORY_COMPONENT_ADDED,
-	                                         iface_type,
+	                                         NM_TYPE_DEVICE_FACTORY,
 	                                         G_SIGNAL_RUN_LAST,
 	                                         G_STRUCT_OFFSET (NMDeviceFactory, component_added),
 	                                         g_signal_accumulator_true_handled, NULL, NULL,
 	                                         G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
-
-	initialized = TRUE;
 }
 
 GType
 nm_device_factory_get_type (void)
 {
-	static GType device_factory_type = 0;
+	static volatile gsize g_define_type_id__volatile = 0;
+	if (g_once_init_enter (&g_define_type_id__volatile)) {
+		GType g_define_type_id =
+			g_type_register_static_simple (G_TYPE_INTERFACE,
+			                               g_intern_static_string ("NMDeviceFactory"),
+			                               sizeof (NMDeviceFactory),
+			                               (GClassInitFunc) default_init,
+			                               0,
+			                               (GInstanceInitFunc) NULL,
+			                               (GTypeFlags) 0);
+		g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+		g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+	}
+	return g_define_type_id__volatile;
+}
+
+/*******************************************************************/
+
+static GSList *internal_types = NULL;
+static GHashTable *factories_by_link = NULL;
+static GHashTable *factories_by_setting = NULL;
+
+void
+_nm_device_factory_internal_register_type (GType factory_type)
+{
+	g_return_if_fail (g_slist_find (internal_types, GUINT_TO_POINTER (factory_type)) == NULL);
+	internal_types = g_slist_prepend (internal_types, GUINT_TO_POINTER (factory_type));
+}
+
+static void __attribute__((destructor))
+_cleanup (void)
+{
+	g_clear_pointer (&internal_types, g_slist_free);
+	g_clear_pointer (&factories_by_link, g_hash_table_unref);
+	g_clear_pointer (&factories_by_setting, g_hash_table_unref);
+}
 
-	if (!device_factory_type) {
-		const GTypeInfo device_factory_info = {
-			sizeof (NMDeviceFactory), /* class_size */
-			interface_init,           /* base_init */
-			NULL,                     /* base_finalize */
-			NULL,
-			NULL,                     /* class_finalize */
-			NULL,                     /* class_data */
-			0,
-			0,                        /* n_preallocs */
-			NULL
-		};
+static NMDeviceFactory *
+find_factory (const NMLinkType *needle_link_types,
+              const char **needle_setting_types)
+{
+	NMDeviceFactory *found;
+	guint i;
 
-		device_factory_type = g_type_register_static (G_TYPE_INTERFACE,
-		                                              "NMDeviceFactory",
-		                                              &device_factory_info,
-		                                              0);
-		g_type_interface_add_prerequisite (device_factory_type, G_TYPE_OBJECT);
+	g_return_val_if_fail (factories_by_link, NULL);
+	g_return_val_if_fail (factories_by_setting, NULL);
+
+	/* NMLinkType search */
+	for (i = 0; needle_link_types && needle_link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) {
+		found = g_hash_table_lookup (factories_by_link, GUINT_TO_POINTER (needle_link_types[i]));
+		if (found)
+			return found;
+	}
+
+	/* NMSetting name search */
+	for (i = 0; needle_setting_types && needle_setting_types[i]; i++) {
+		found = g_hash_table_lookup (factories_by_setting, needle_setting_types[i]);
+		if (found)
+			return found;
 	}
 
-	return device_factory_type;
+	return NULL;
 }
 
-NMDeviceType
-nm_device_factory_get_device_type (NMDeviceFactory *factory)
+NMDeviceFactory *
+nm_device_factory_manager_find_factory_for_link_type (NMLinkType link_type)
 {
-	g_return_val_if_fail (factory != NULL, NM_DEVICE_TYPE_UNKNOWN);
+	const NMLinkType ltypes[2] = { link_type, NM_LINK_TYPE_NONE };
 
-	return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_device_type (factory);
+	if (link_type == NM_LINK_TYPE_UNKNOWN)
+		return NULL;
+	g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, NULL);
+	return find_factory (ltypes, NULL);
+}
+
+NMDeviceFactory *
+nm_device_factory_manager_find_factory_for_connection (NMConnection *connection)
+{
+	const char *stypes[2] = { nm_connection_get_connection_type (connection), NULL };
+
+	g_assert (stypes[0]);
+	return find_factory (NULL, stypes);
 }
 
 void
-nm_device_factory_start (NMDeviceFactory *factory)
+nm_device_factory_manager_for_each_factory (NMDeviceFactoryManagerFactoryFunc callback,
+                                            gpointer user_data)
 {
-	g_return_if_fail (factory != NULL);
+	GHashTableIter iter;
+	NMDeviceFactory *factory;
+	GSList *list_iter, *list = NULL;
 
-	if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start)
-		NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start (factory);
+	g_return_if_fail (factories_by_link);
+	g_return_if_fail (factories_by_setting);
+
+	g_hash_table_iter_init (&iter, factories_by_link);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) {
+		if (!g_slist_find (list, factory))
+			list = g_slist_prepend (list, factory);
+	}
+
+	g_hash_table_iter_init (&iter, factories_by_setting);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) {
+		if (!g_slist_find (list, factory))
+			list = g_slist_prepend (list, factory);
+	}
+
+	for (list_iter = list; list_iter; list_iter = list_iter->next)
+		callback (list_iter->data, user_data);
+
+	g_slist_free (list);
 }
 
-NMDevice *
-nm_device_factory_new_link (NMDeviceFactory *factory,
-                            NMPlatformLink *plink,
-                            GError **error)
+#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)
 {
-	g_return_val_if_fail (factory != NULL, NULL);
-	g_return_val_if_fail (plink != NULL, NULL);
+	const struct read_device_factory_paths_data *da = a;
+	const struct read_device_factory_paths_data *db = b;
+	time_t ta, tb;
 
-	if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->new_link)
-		return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->new_link (factory, plink, error);
-	return NULL;
+	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;
 }
 
-NMDevice *
-nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory,
-                                                        NMConnection *connection,
-                                                        NMDevice *parent,
-                                                        GError **error)
+static char**
+read_device_factory_paths (void)
 {
-	NMDeviceFactory *interface;
+	GDir *dir;
+	GError *error = NULL;
+	const char *item;
+	GArray *paths;
+	char **result;
+	guint i;
 
-	g_return_val_if_fail (factory, NULL);
-	g_return_val_if_fail (connection, NULL);
-	g_return_val_if_fail (!error || !*error, NULL);
+	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;
+	}
 
-	interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory);
-	if (interface->create_virtual_device_for_connection)
-		return interface->create_virtual_device_for_connection (factory, connection, parent, 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
+_add_factory (NMDeviceFactory *factory,
+              gboolean check_duplicates,
+              const char *path,
+              NMDeviceFactoryManagerFactoryFunc callback,
+              gpointer user_data)
+{
+	NMDeviceFactory *found = NULL;
+	const NMLinkType *link_types = NULL;
+	const char **setting_types = NULL;
+	int i;
+
+	g_return_val_if_fail (factories_by_link, FALSE);
+	g_return_val_if_fail (factories_by_setting, FALSE);
+
+	nm_device_factory_get_supported_types (factory, &link_types, &setting_types);
+	if (check_duplicates) {
+		found = find_factory (link_types, setting_types);
+		if (found) {
+			nm_log_warn (LOGD_HW, "Loading device plugin failed: multiple plugins "
+			             "for same type (using '%s' instead of '%s')",
+			             (char *) g_object_get_data (G_OBJECT (found), PLUGIN_PATH_TAG),
+			             path);
+			return FALSE;
+		}
+	}
+
+	g_object_set_data_full (G_OBJECT (factory), PLUGIN_PATH_TAG, g_strdup (path), g_free);
+	for (i = 0; link_types && link_types[i] > NM_LINK_TYPE_UNKNOWN; i++)
+		g_hash_table_insert (factories_by_link, GUINT_TO_POINTER (link_types[i]), g_object_ref (factory));
+	for (i = 0; setting_types && setting_types[i]; i++)
+		g_hash_table_insert (factories_by_setting, (char *) setting_types[i], g_object_ref (factory));
+
+	callback (factory, user_data);
+
+	nm_log_info (LOGD_HW, "Loaded device plugin: %s (%s)", G_OBJECT_TYPE_NAME (factory), path);
+	return TRUE;
+}
+
+void
+nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc callback,
+                                          gpointer user_data)
+{
+	NMDeviceFactory *factory;
+	const GSList *iter;
+	GError *error = NULL;
+	char **path, **paths;
+
+	g_return_if_fail (factories_by_link == NULL);
+	g_return_if_fail (factories_by_setting == NULL);
+
+	factories_by_link = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+	factories_by_setting = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+
+	/* Register internal factories first */
+	for (iter = internal_types; iter; iter = iter->next) {
+		GType ftype = (GType) GPOINTER_TO_SIZE (iter->data);
+
+		factory = (NMDeviceFactory *) g_object_new (ftype, NULL);
+		g_assert (factory);
+		_add_factory (factory, FALSE, "internal", callback, user_data);
+	}
+
+	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 (_add_factory (factory, TRUE, g_module_name (plugin), callback, user_data))
+			g_module_make_resident (plugin);
+		else
+			g_module_close (plugin);
+
+		g_object_unref (factory);
+	}
+	g_strfreev (paths);
 }
 
diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h
index ce672b8b..34c056d2 100644
--- a/src/devices/nm-device-factory.h
+++ b/src/devices/nm-device-factory.h
@@ -66,14 +66,19 @@ struct _NMDeviceFactory {
 	GTypeInterface g_iface;
 
 	/**
-	 * get_device_type:
+	 * get_supported_types:
 	 * @factory: the #NMDeviceFactory
+	 * @out_link_types: on return, a %NM_LINK_TYPE_NONE terminated
+	 *  list of #NMLinkType that the plugin supports
+	 * @out_setting_types: on return, a %NULL terminated list of
+	 *  base-type #NMSetting names that the plugin can create devices for
 	 *
-	 * This function MUST be implemented.
-	 *
-	 * Returns: the #NMDeviceType that this plugin creates
+	 * Returns the #NMLinkType and #NMSetting names that this plugin
+	 * supports.  This function MUST be implemented.
 	 */
-	NMDeviceType (*get_device_type) (NMDeviceFactory *factory);
+	void (*get_supported_types) (NMDeviceFactory *factory,
+	                             const NMLinkType **out_link_types,
+	                             const char ***out_setting_types);
 
 	/**
 	 * start:
@@ -87,20 +92,27 @@ struct _NMDeviceFactory {
 	/**
 	 * new_link:
 	 * @factory: the #NMDeviceFactory
-	 * @link: the new link
+	 * @plink: the new link
+	 * @out_ignore: on return, %TRUE if the link should be ignored
 	 * @error: error if the link could be claimed but an error occurred
 	 *
 	 * The NetworkManager core was notified of a new link which the plugin
 	 * may want to claim and create a #NMDevice subclass for.  If the link
-	 * represents a device the factory is capable of claiming, but the device
-	 * could not be created, %NULL should be returned and @error should be set.
-	 * %NULL should always be returned and @error should never be set if the
-	 * factory cannot create devices for the type which @link represents.
+	 * represents a device which the factory does not support, or the link
+	 * is supported but the device could not be created, %NULL should be
+	 * returned and @error should be set.
+	 *
+	 * If the plugin cannot create a #NMDevice for the link and wants the
+	 * core to ignore it, set @out_ignore to %TRUE and return no error.
+	 *
+	 * @plink is guaranteed to be one of the types the factory returns in
+	 * get_supported_types().
 	 *
 	 * Returns: the #NMDevice if the link was claimed and created, %NULL if not
 	 */
 	NMDevice * (*new_link)        (NMDeviceFactory *factory,
 	                               NMPlatformLink *plink,
+	                               gboolean *out_ignore,
 	                               GError **error);
 
 	/**
@@ -121,6 +133,34 @@ struct _NMDeviceFactory {
 	                                                    NMDevice *parent,
 	                                                    GError **error);
 
+	/**
+	 * get_connection_parent:
+	 * @factory: the #NMDeviceFactory
+	 * @connection: the #NMConnection to return the parent name for, if supported
+	 *
+	 * Given a connection, returns the a parent interface name, parent connection
+	 * UUID, or parent device hardware address for @connection.
+	 *
+	 * Returns: the parent interface name, parent connection UUID, parent
+	 *   device hardware address, or %NULL
+	 */
+	const char * (*get_connection_parent) (NMDeviceFactory *factory,
+	                                       NMConnection *connection);
+
+	/**
+	 * get_virtual_iface_name:
+	 * @factory: the #NMDeviceFactory
+	 * @connection: the #NMConnection to return the virtual interface name for
+	 * @parent_iface: parent interface name
+	 *
+	 * Given a connection, returns the interface name that a device activating
+	 * that connection would have.
+	 *
+	 * Returns: the interface name, or %NULL
+	 */
+	char * (*get_virtual_iface_name) (NMDeviceFactory *factory,
+	                                  NMConnection *connection,
+	                                  const char *parent_iface);
 
 	/* Signals */
 
@@ -151,12 +191,22 @@ struct _NMDeviceFactory {
 
 GType      nm_device_factory_get_type    (void);
 
-NMDeviceType nm_device_factory_get_device_type (NMDeviceFactory *factory);
+void       nm_device_factory_get_supported_types (NMDeviceFactory *factory,
+                                                  const NMLinkType **out_link_types,
+                                                  const char ***out_setting_types);
+
+const char *nm_device_factory_get_connection_parent (NMDeviceFactory *factory,
+                                                     NMConnection *connection);
+
+char *     nm_device_factory_get_virtual_iface_name (NMDeviceFactory *factory,
+                                                     NMConnection *connection,
+                                                     const char *parent_iface);
 
 void       nm_device_factory_start       (NMDeviceFactory *factory);
 
 NMDevice * nm_device_factory_new_link    (NMDeviceFactory *factory,
                                           NMPlatformLink *plink,
+                                          gboolean *out_ignore,
                                           GError **error);
 
 NMDevice * nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory,
@@ -168,15 +218,33 @@ NMDevice * nm_device_factory_create_virtual_device_for_connection (NMDeviceFacto
 gboolean   nm_device_factory_emit_component_added (NMDeviceFactory *factory,
                                                    GObject *component);
 
+#define NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(...) \
+	{ static const NMLinkType _df_links[] = { __VA_ARGS__, NM_LINK_TYPE_NONE }; *out_link_types = _df_links; }
+#define NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(...) \
+	{ static const char *_df_settings[] = { __VA_ARGS__, NULL }; *out_setting_types = _df_settings; }
+
+extern const NMLinkType _nm_device_factory_no_default_links[];
+extern const char *_nm_device_factory_no_default_settings[];
+
+#define NM_DEVICE_FACTORY_DECLARE_TYPES(...) \
+	static void \
+	get_supported_types (NMDeviceFactory *factory, \
+	                     const NMLinkType **out_link_types, \
+	                     const char ***out_setting_types) \
+	{ \
+		*out_link_types = _nm_device_factory_no_default_links; \
+		*out_setting_types = _nm_device_factory_no_default_settings; \
+ \
+		{ __VA_ARGS__; } \
+	} \
+ \
+
 /**************************************************************************
  * INTERNAL DEVICE FACTORY FUNCTIONS - devices provided by plugins should
  * not use these functions.
  **************************************************************************/
 
-#define DEFINE_DEVICE_FACTORY_INTERNAL(upper, mixed, lower, dfi_code) \
-	DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(upper, mixed, lower, upper, dfi_code)
-
-#define DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(upper, mixed, lower, devtype, dfi_code) \
+#define NM_DEVICE_FACTORY_DEFINE_INTERNAL(upper, mixed, lower, st_code, dfi_code) \
 	typedef GObject NM##mixed##Factory; \
 	typedef GObjectClass NM##mixed##FactoryClass; \
  \
@@ -198,16 +266,12 @@ gboolean   nm_device_factory_emit_component_added (NMDeviceFactory *factory,
 		g_type_ensure (NM_TYPE_##upper##_FACTORY); \
 	} \
  \
-	static NMDeviceType \
-	get_device_type (NMDeviceFactory *factory) \
-	{ \
-		return NM_DEVICE_TYPE_##devtype; \
-	} \
+	NM_DEVICE_FACTORY_DECLARE_TYPES(st_code) \
  \
 	static void \
 	device_factory_interface_init (NMDeviceFactory *factory_iface) \
 	{ \
-		factory_iface->get_device_type = get_device_type; \
+		factory_iface->get_supported_types = get_supported_types; \
 		dfi_code \
 	} \
  \
@@ -222,6 +286,22 @@ gboolean   nm_device_factory_emit_component_added (NMDeviceFactory *factory,
 	}
 
 void _nm_device_factory_internal_register_type (GType factory_type);
-const GSList *nm_device_factory_get_internal_factory_types (void);
+
+/**************************************************************************
+ * PRIVATE FACTORY FUNCTIONS - for factory consumers (eg, NMManager).
+ **************************************************************************/
+
+typedef void (*NMDeviceFactoryManagerFactoryFunc)    (NMDeviceFactory *factory,
+                                                      gpointer user_data);
+
+void              nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc callback,
+                                                            gpointer user_data);
+
+NMDeviceFactory * nm_device_factory_manager_find_factory_for_link_type  (NMLinkType link_type);
+
+NMDeviceFactory * nm_device_factory_manager_find_factory_for_connection (NMConnection *connection);
+
+void              nm_device_factory_manager_for_each_factory (NMDeviceFactoryManagerFactoryFunc callback,
+                                                              gpointer user_data);
 
 #endif /* __NETWORKMANAGER_DEVICE_FACTORY_H__ */
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index f103ef83..5841d834 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -45,15 +45,23 @@ enum {
 	LAST_PROP
 };
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *dev)
 {
-	if (nm_platform_link_supports_carrier_detect (nm_device_get_ifindex (dev)))
+	if (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, nm_device_get_ifindex (dev)))
 		return NM_DEVICE_CAP_CARRIER_DETECT;
 	else
 		return NM_DEVICE_CAP_NONE;
 }
 
+static const char *
+get_type_description (NMDevice *device)
+{
+	if (NM_DEVICE_GENERIC_GET_PRIVATE (device)->type_description)
+		return NM_DEVICE_GENERIC_GET_PRIVATE (device)->type_description;
+	return NM_DEVICE_CLASS (nm_device_generic_parent_class)->get_type_description (device);
+}
+
 static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
@@ -117,7 +125,7 @@ constructed (GObject *object)
 		int ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self));
 
 		if (ifindex != 0)
-			priv->type_description = g_strdup (nm_platform_link_get_type_name (ifindex));
+			priv->type_description = g_strdup (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex));
 	}
 
 	G_OBJECT_CLASS (nm_device_generic_parent_class)->constructed (object);
@@ -184,6 +192,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
 	object_class->set_property = set_property;
 
 	parent_class->get_generic_capabilities = get_generic_capabilities;
+	parent_class->get_type_description = get_type_description;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->update_connection = update_connection;
 
diff --git a/src/devices/nm-device-gre.c b/src/devices/nm-device-gre.c
index 9c25b398..b3510d2e 100644
--- a/src/devices/nm-device-gre.c
+++ b/src/devices/nm-device-gre.c
@@ -71,7 +71,7 @@ update_properties (NMDevice *device)
 	GObject *object = G_OBJECT (device);
 	NMPlatformGreProperties props;
 
-	if (!nm_platform_gre_get_properties (nm_device_get_ifindex (device), &props)) {
+	if (!nm_platform_gre_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
 		_LOGW (LOGD_HW, "could not read gre properties");
 		return;
 	}
@@ -267,19 +267,17 @@ nm_device_gre_class_init (NMDeviceGreClass *klass)
 #define NM_GRE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_GRE_FACTORY, NMGreFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_GRE || plink->type == NM_LINK_TYPE_GRETAP) {
-		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GRE,
-		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-		                                  NM_DEVICE_TYPE_DESC, "Gre",
-		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
-		                                  NULL);
-	}
-	return NULL;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GRE,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_TYPE_DESC, "Gre",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+	                                  NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(GRE, Gre, gre, ETHERNET, \
-	factory_iface->new_link = new_link; \
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (GRE, Gre, gre,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_GRE, NM_LINK_TYPE_GRETAP),
+	factory_iface->new_link = new_link;
 	)
 
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index e1af8995..2e2483c0 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -44,12 +44,15 @@ G_DEFINE_TYPE (NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandPrivate))
 
+#define NM_DEVICE_INFINIBAND_IS_PARTITION "is-partition"
+
 typedef struct {
-	int dummy;
+	gboolean is_partition;
 } NMDeviceInfinibandPrivate;
 
 enum {
 	PROP_0,
+	PROP_IS_PARTITION,
 
 	LAST_PROP
 };
@@ -59,10 +62,15 @@ nm_device_infiniband_init (NMDeviceInfiniband * self)
 {
 }
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *dev)
 {
-	return NM_DEVICE_CAP_CARRIER_DETECT;
+	guint32 caps = NM_DEVICE_CAP_CARRIER_DETECT;
+
+	if (NM_DEVICE_INFINIBAND_GET_PRIVATE (dev)->is_partition)
+		caps |= NM_DEVICE_CAP_IS_SOFTWARE;
+
+	return caps;
 }
 
 static NMActStageReturn
@@ -105,7 +113,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 		}
 	}
 
-	ok = nm_platform_sysctl_set (mode_path, transport_mode);
+	ok = nm_platform_sysctl_set (NM_PLATFORM_GET, mode_path, transport_mode);
 	g_free (mode_path);
 
 	if (!ok) {
@@ -213,8 +221,8 @@ update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMSettingInfiniband *s_infiniband = nm_connection_get_setting_infiniband (connection);
 	const char *mac = nm_device_get_hw_address (device);
-	char *mode_path, *contents = NULL;
 	const char *transport_mode = "datagram";
+	int ifindex;
 
 	if (!s_infiniband) {
 		s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
@@ -224,16 +232,10 @@ update_connection (NMDevice *device, NMConnection *connection)
 	if (mac && !nm_utils_hwaddr_matches (mac, -1, NULL, INFINIBAND_ALEN))
 		g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
 
-	mode_path = g_strdup_printf ("/sys/class/net/%s/mode",
-	                             ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (device)));
-	contents = nm_platform_sysctl_get (mode_path);
-	g_free (mode_path);
-	if (contents) {
-		if (strstr (contents, "datagram"))
+	ifindex = nm_device_get_ifindex (device);
+	if (ifindex > 0) {
+		if (!nm_platform_infiniband_get_info (NM_PLATFORM_GET, ifindex, NULL, NULL, &transport_mode))
 			transport_mode = "datagram";
-		else if (strstr (contents, "connected"))
-			transport_mode = "connected";
-		g_free (contents);
 	}
 	g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, transport_mode, NULL);
 }
@@ -243,6 +245,9 @@ get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
 	switch (prop_id) {
+	case PROP_IS_PARTITION:
+		g_value_set_boolean (value, NM_DEVICE_INFINIBAND_GET_PRIVATE (object)->is_partition);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -254,6 +259,9 @@ set_property (GObject *object, guint prop_id,
 			  const GValue *value, GParamSpec *pspec)
 {
 	switch (prop_id) {
+	case PROP_IS_PARTITION:
+		NM_DEVICE_INFINIBAND_GET_PRIVATE (object)->is_partition = g_value_get_boolean (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -281,6 +289,12 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
 
 	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_IS_PARTITION,
+		 g_param_spec_boolean (NM_DEVICE_INFINIBAND_IS_PARTITION, "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
@@ -293,16 +307,14 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 #define NM_INFINIBAND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INFINIBAND_FACTORY, NMInfinibandFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_INFINIBAND) {
-		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
-		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-		                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
-		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
-		                                  NULL);
-	}
-	return NULL;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
+	                                  NM_DEVICE_INFINIBAND_IS_PARTITION, (plink->parent > 0),
+	                                  NULL);
 }
 
 static NMDevice *
@@ -314,25 +326,30 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 	NMSettingInfiniband *s_infiniband;
 	int p_key, parent_ifindex;
 	const char *iface;
+	NMPlatformError plerr;
 
-	if (!nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME))
+	if (!NM_IS_DEVICE_INFINIBAND (parent)) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Parent interface %s must be an InfiniBand interface",
+		             nm_device_get_iface (parent));
 		return NULL;
-
-	g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (parent), NULL);
+	}
 
 	s_infiniband = nm_connection_get_setting_infiniband (connection);
 
 	iface = nm_setting_infiniband_get_virtual_interface_name (s_infiniband);
-	g_return_val_if_fail (iface != NULL, NULL);
+	g_assert (iface);
 
 	parent_ifindex = nm_device_get_ifindex (parent);
 	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
 
-	if (   !nm_platform_infiniband_partition_add (parent_ifindex, p_key)
-	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_INFINIBAND, "(%s): failed to add InfiniBand P_Key interface for '%s': %s",
-		             iface, nm_connection_get_id (connection),
-		             nm_platform_get_error_msg ());
+	plerr = nm_platform_infiniband_partition_add (NM_PLATFORM_GET, parent_ifindex, p_key, NULL);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create InfiniBand P_Key interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
 		return NULL;
 	}
 
@@ -341,11 +358,49 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 	                                  NM_DEVICE_DRIVER, nm_device_get_driver (parent),
 	                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
+	                                  NM_DEVICE_INFINIBAND_IS_PARTITION, TRUE,
 	                                  NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL(INFINIBAND, Infiniband, infiniband, \
-	factory_iface->new_link = new_link; \
+static const char *
+get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
+{
+	NMSettingInfiniband *s_infiniband;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME), NULL);
+
+	s_infiniband = nm_connection_get_setting_infiniband (connection);
+	g_assert (s_infiniband);
+
+	return nm_setting_infiniband_get_parent (s_infiniband);
+}
+
+static char *
+get_virtual_iface_name (NMDeviceFactory *factory,
+                        NMConnection *connection,
+                        const char *parent_iface)
+{
+	NMSettingInfiniband *s_infiniband;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME), NULL);
+
+	s_infiniband = nm_connection_get_setting_infiniband (connection);
+	g_assert (s_infiniband);
+
+	if (!parent_iface)
+		return NULL;
+
+	g_return_val_if_fail (g_strcmp0 (parent_iface, nm_setting_infiniband_get_parent (s_infiniband)) == 0, NULL);
+
+	return g_strdup (nm_setting_infiniband_get_virtual_interface_name (s_infiniband));
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (INFINIBAND, Infiniband, infiniband,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_INFINIBAND)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_INFINIBAND_SETTING_NAME),
+	factory_iface->new_link = new_link;
 	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	factory_iface->get_connection_parent = get_connection_parent;
+	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
 	)
 
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index bcd38617..0bfe3fe9 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -64,7 +64,7 @@ update_properties (NMDevice *device)
 	GObject *object = G_OBJECT (device);
 	NMPlatformMacvlanProperties props;
 
-	if (!nm_platform_macvlan_get_properties (nm_device_get_ifindex (device), &props)) {
+	if (!nm_platform_macvlan_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
 		_LOGW (LOGD_HW, "could not read macvlan properties");
 		return;
 	}
@@ -175,19 +175,17 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 #define NM_MACVLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MACVLAN_FACTORY, NMMacvlanFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_MACVLAN || plink->type == NM_LINK_TYPE_MACVTAP) {
-		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_MACVLAN,
-		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-		                                  NM_DEVICE_TYPE_DESC, "Macvlan",
-		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
-		                                  NULL);
-	}
-	return NULL;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_MACVLAN,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_TYPE_DESC, "Macvlan",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+	                                  NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(MACVLAN, Macvlan, macvlan, ETHERNET, \
-	factory_iface->new_link = new_link; \
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (MACVLAN, Macvlan, macvlan,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP),
+	factory_iface->new_link = new_link;
 	)
 
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 1f781bbc..f34969ac 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -97,6 +97,9 @@ void nm_device_set_carrier (NMDevice *self, gboolean carrier);
 
 void nm_device_emit_recheck_auto_activate (NMDevice *device);
 void nm_device_queue_recheck_assume (NMDevice *device);
+void nm_device_queue_recheck_available (NMDevice *device,
+                                        NMDeviceStateReason available_reason,
+                                        NMDeviceStateReason unavailable_reason);
 
 void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config);
 void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config);
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index 9a3fab5e..5aaff8b1 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -65,7 +65,7 @@ reload_tun_properties (NMDeviceTun *self)
 	GObject *object = G_OBJECT (self);
 	NMPlatformTunProperties props;
 
-	if (!nm_platform_tun_get_properties (nm_device_get_ifindex (NM_DEVICE (self)), &props)) {
+	if (!nm_platform_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &props)) {
 		_LOGD (LOGD_HW, "could not read tun properties");
 		return;
 	}
@@ -123,7 +123,7 @@ constructed (GObject *object)
 	gboolean properties_read;
 	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
 
-	properties_read = nm_platform_tun_get_properties (nm_device_get_ifindex (NM_DEVICE (self)), &priv->props);
+	properties_read = nm_platform_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &priv->props);
 
 	G_OBJECT_CLASS (nm_device_tun_parent_class)->constructed (object);
 
@@ -270,7 +270,7 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 #define NM_TUN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TUN_FACTORY, NMTunFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
 	const char *mode = NULL;
 
@@ -278,8 +278,10 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
 		mode = "tun";
 	else if (plink->type == NM_LINK_TYPE_TAP)
 		mode = "tap";
-	else
-		return NULL;
+	else {
+		g_warn_if_reached ();
+		mode = "unknown";
+	}
 
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TUN,
 	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
@@ -289,7 +291,8 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
 	                                  NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(TUN, Tun, tun, GENERIC, \
-	factory_iface->new_link = new_link; \
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (TUN, Tun, tun,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP),
+	factory_iface->new_link = new_link;
 	)
 
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index db29c00d..04f03fbb 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -82,7 +82,7 @@ get_peer (NMDeviceVeth *self)
 	if (priv->ever_had_peer)
 		return priv->peer;
 
-	if (!nm_platform_veth_get_properties (nm_device_get_ifindex (device), &props)) {
+	if (!nm_platform_veth_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
 		_LOGW (LOGD_HW, "could not read veth properties");
 		return NULL;
 	}
@@ -96,13 +96,21 @@ get_peer (NMDeviceVeth *self)
 	return priv->peer;
 }
 
+static gboolean
+can_unmanaged_external_down (NMDevice *self)
+{
+	/* Unless running in a container, an udev rule causes these to be
+	 * unmanaged. If there's no udev then we're probably in a container
+	 * and should IFF_UP and configure the veth ourselves even if we
+	 * didn't create it. */
+	return FALSE;
+}
 
 /**************************************************************/
 
 static void
 nm_device_veth_init (NMDeviceVeth *self)
 {
-	nm_device_set_initial_unmanaged_flag (NM_DEVICE (self), NM_UNMANAGED_DEFAULT, TRUE);
 }
 
 static void
@@ -141,12 +149,15 @@ static void
 nm_device_veth_class_init (NMDeviceVethClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	g_type_class_add_private (klass, sizeof (NMDeviceVethPrivate));
 
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 
+	device_class->can_unmanaged_external_down = can_unmanaged_external_down;
+
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PEER,
@@ -166,19 +177,17 @@ nm_device_veth_class_init (NMDeviceVethClass *klass)
 #define NM_VETH_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VETH_FACTORY, NMVethFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_VETH) {
-		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VETH,
-		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-		                                  NM_DEVICE_TYPE_DESC, "Veth",
-		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
-		                                  NULL);
-	}
-	return NULL;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VETH,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_TYPE_DESC, "Veth",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
+	                                  NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(VETH, Veth, veth, ETHERNET, \
-	factory_iface->new_link = new_link; \
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (VETH, Veth, veth,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_VETH),
+	factory_iface->new_link = new_link;
 	)
 
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index c69bcded..158e7de5 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -40,6 +40,7 @@
 #include "nm-device-factory.h"
 #include "nm-manager.h"
 #include "nm-core-internal.h"
+#include "gsystem-local-alloc.h"
 
 #include "nm-device-vlan-glue.h"
 
@@ -51,14 +52,10 @@ G_DEFINE_TYPE (NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE)
 #define NM_DEVICE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VLAN, NMDeviceVlanPrivate))
 
 typedef struct {
-	char *initial_hw_addr;
-
-	gboolean disposed;
 	gboolean invalid;
 
 	NMDevice *parent;
 	guint parent_state_id;
-
 	int vlan_id;
 } NMDeviceVlanPrivate;
 
@@ -75,20 +72,68 @@ enum {
 /******************************************************************/
 
 static void
-update_initial_hw_address (NMDevice *dev)
+parent_state_changed (NMDevice *parent,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
+
+	/* We'll react to our own carrier state notifications. Ignore the parent's. */
+	if (reason == NM_DEVICE_STATE_REASON_CARRIER)
+		return;
+
+	nm_device_set_unmanaged (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason);
+}
+
+static void
+nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent, gboolean construct)
 {
-	NMDeviceVlan *self = NM_DEVICE_VLAN (dev);
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+
+	if (parent == priv->parent)
+		return;
+
+	if (priv->parent_state_id) {
+		g_signal_handler_disconnect (priv->parent, priv->parent_state_id);
+		priv->parent_state_id = 0;
+	}
+	g_clear_object (&priv->parent);
 
-	priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (dev));
-	_LOGD (LOGD_DEVICE | LOGD_VLAN, "read initial MAC address %s", priv->initial_hw_addr);
+	if (parent) {
+		priv->parent = g_object_ref (parent);
+		priv->parent_state_id = g_signal_connect (priv->parent,
+		                                          "state-changed",
+		                                          G_CALLBACK (parent_state_changed),
+		                                          device);
+
+		/* Set parent-dependent unmanaged flag */
+		if (construct) {
+			nm_device_set_initial_unmanaged_flag (device,
+			                                      NM_UNMANAGED_PARENT,
+			                                      !nm_device_get_managed (parent));
+		} else {
+			nm_device_set_unmanaged (device,
+			                         NM_UNMANAGED_PARENT,
+			                         !nm_device_get_managed (parent),
+			                         NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED);
+		}
+	}
+
+	/* Recheck availability now that the parent has changed */
+	nm_device_queue_recheck_available (self,
+	                                   NM_DEVICE_STATE_REASON_PARENT_CHANGED,
+	                                   NM_DEVICE_STATE_REASON_PARENT_CHANGED);
+	g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_PARENT);
 }
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *dev)
 {
 	/* We assume VLAN interfaces always support carrier detect */
-	return NM_DEVICE_CAP_CARRIER_DETECT;
+	return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
 }
 
 static gboolean
@@ -108,12 +153,55 @@ bring_up (NMDevice *dev, gboolean *no_firmware)
 /******************************************************************/
 
 static gboolean
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+{
+	if (!NM_DEVICE_VLAN_GET_PRIVATE (device)->parent)
+		return FALSE;
+
+	return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->is_available (device, flags);
+}
+
+static gboolean
+component_added (NMDevice *device, GObject *component)
+{
+	NMDeviceVlan *self = NM_DEVICE_VLAN (device);
+	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
+	NMDevice *added_device;
+	int parent_ifindex = -1;
+
+	if (priv->parent)
+		return FALSE;
+
+	if (!NM_IS_DEVICE (component))
+		return FALSE;
+	added_device = NM_DEVICE (component);
+
+	if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, nm_device_get_ifindex (device), &parent_ifindex, NULL)) {
+		_LOGW (LOGD_VLAN, "failed to get VLAN interface info while checking added component.");
+		return FALSE;
+	}
+
+	if (nm_device_get_ifindex (added_device) != parent_ifindex)
+		return FALSE;
+
+	nm_device_vlan_set_parent (self, added_device, FALSE);
+
+	/* Don't claim parent exclusively */
+	return FALSE;
+}
+
+/******************************************************************/
+
+static gboolean
 match_parent (NMDeviceVlan *self, const char *parent)
 {
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
 
 	g_return_val_if_fail (parent != NULL, FALSE);
 
+	if (!priv->parent)
+		return FALSE;
+
 	if (nm_utils_is_uuid (parent)) {
 		NMActRequest *parent_req;
 		NMConnection *parent_connection;
@@ -239,32 +327,6 @@ complete_connection (NMDevice *device,
 	return TRUE;
 }
 
-static void parent_state_changed (NMDevice *parent, NMDeviceState new_state,
-                                  NMDeviceState old_state,
-                                  NMDeviceStateReason reason,
-                                  gpointer user_data);
-
-static void
-nm_device_vlan_set_parent (NMDeviceVlan *device, NMDevice *parent)
-{
-	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
-
-	if (priv->parent_state_id) {
-		g_signal_handler_disconnect (priv->parent, priv->parent_state_id);
-		priv->parent_state_id = 0;
-	}
-	g_clear_object (&priv->parent);
-
-	if (parent) {
-		priv->parent = g_object_ref (parent);
-		priv->parent_state_id = g_signal_connect (priv->parent,
-		                                          "state-changed",
-		                                          G_CALLBACK (parent_state_changed),
-		                                          device);
-	}
-	g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_PARENT);
-}
-
 static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
@@ -281,7 +343,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 		nm_connection_add_setting (connection, (NMSetting *) s_vlan);
 	}
 
-	if (!nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id)) {
+	if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent_ifindex, &vlan_id)) {
 		_LOGW (LOGD_VLAN, "failed to get VLAN interface info while updating connection.");
 		return;
 	}
@@ -296,8 +358,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 
 	parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
 	g_assert (parent);
-	if (priv->parent != parent)
-		nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent);
+	nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent, FALSE);
 
 	/* Update parent in the connection; default to parent's interface name */
 	new_parent = nm_device_get_iface (parent);
@@ -353,12 +414,12 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 		num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_INGRESS_MAP);
 		for (i = 0; i < num; i++) {
 			if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_INGRESS_MAP, i, &from, &to))
-				nm_platform_vlan_set_ingress_map (ifindex, from, to);
+				nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, from, to);
 		}
 		num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_EGRESS_MAP);
 		for (i = 0; i < num; i++) {
 			if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_EGRESS_MAP, i, &from, &to))
-				nm_platform_vlan_set_egress_map (ifindex, from, to);
+				nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, from, to);
 		}
 	}
 
@@ -386,30 +447,9 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 static void
 deactivate (NMDevice *device)
 {
-	NMDeviceVlan *self = NM_DEVICE_VLAN (device);
-	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
-
 	/* Reset MAC address back to initial address */
-	if (priv->initial_hw_addr)
-		nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_VLAN);
-}
-
-/******************************************************************/
-
-static void
-parent_state_changed (NMDevice *parent,
-                      NMDeviceState new_state,
-                      NMDeviceState old_state,
-                      NMDeviceStateReason reason,
-                      gpointer user_data)
-{
-	NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
-
-	/* We'll react to our own carrier state notifications. Ignore the parent's. */
-	if (reason == NM_DEVICE_STATE_REASON_CARRIER)
-		return;
-
-	nm_device_set_unmanaged (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason);
+	if (nm_device_get_initial_hw_address (device))
+		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_VLAN);
 }
 
 /******************************************************************/
@@ -431,37 +471,40 @@ constructed (GObject *object)
 	if (G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed)
 		G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed (object);
 
-	if (!priv->parent) {
-		_LOGE (LOGD_VLAN, "no parent specified.");
-		priv->invalid = TRUE;
-		return;
-	}
-
-	itype = nm_platform_link_get_type (ifindex);
+	itype = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex);
 	if (itype != NM_LINK_TYPE_VLAN) {
 		_LOGE (LOGD_VLAN, "failed to get VLAN interface type.");
 		priv->invalid = TRUE;
 		return;
 	}
 
-	if (!nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id)) {
+	if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent_ifindex, &vlan_id)) {
 		_LOGW (LOGD_VLAN, "failed to get VLAN interface info.");
 		priv->invalid = TRUE;
 		return;
 	}
 
-	if (   parent_ifindex < 0
-	    || parent_ifindex != nm_device_get_ip_ifindex (priv->parent)
-	    || vlan_id < 0) {
+	if (parent_ifindex < 0 || vlan_id < 0) {
 		_LOGW (LOGD_VLAN, "VLAN parent ifindex (%d) or VLAN ID (%d) invalid.",
 		       parent_ifindex, priv->vlan_id);
 		priv->invalid = TRUE;
 		return;
 	}
 
+	if (priv->parent && parent_ifindex != nm_device_get_ip_ifindex (priv->parent)) {
+		_LOGW (LOGD_VLAN, "VLAN parent %s (%d) and parent ifindex %d don't match.",
+		       nm_device_get_iface (priv->parent),
+		       nm_device_get_ifindex (priv->parent),
+		       parent_ifindex);
+		priv->invalid = TRUE;
+		return;
+	}
+
 	priv->vlan_id = vlan_id;
-	_LOGI (LOGD_HW | LOGD_VLAN, "VLAN ID %d with parent %s",
-	       priv->vlan_id, nm_device_get_iface (priv->parent));
+	_LOGI (LOGD_HW | LOGD_VLAN, "VLAN ID %d with parent %s (%d)",
+	       priv->vlan_id,
+	       priv->parent ? nm_device_get_iface (priv->parent) : "unknown",
+	       parent_ifindex);
 }
 
 static void
@@ -494,7 +537,7 @@ set_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_INT_PARENT_DEVICE:
-		nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value));
+		nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value), TRUE);
 		break;
 	case PROP_VLAN_ID:
 		priv->vlan_id = g_value_get_uint (value);
@@ -508,32 +551,12 @@ set_property (GObject *object, guint prop_id,
 static void
 dispose (GObject *object)
 {
-	NMDeviceVlan *self = NM_DEVICE_VLAN (object);
-	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
-
-	if (priv->disposed) {
-		G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object);
-		return;
-	}
-	priv->disposed = TRUE;
-
-	nm_device_vlan_set_parent (self, NULL);
+	nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), NULL, FALSE);
 
 	G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object);
 }
 
 static void
-finalize (GObject *object)
-{
-	NMDeviceVlan *self = NM_DEVICE_VLAN (object);
-	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
-
-	g_free (priv->initial_hw_addr);
-
-	G_OBJECT_CLASS (nm_device_vlan_parent_class)->finalize (object);
-}
-
-static void
 nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -548,14 +571,14 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
-	object_class->finalize = finalize;
 
-	parent_class->update_initial_hw_address = update_initial_hw_address;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->bring_up = bring_up;
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
 	parent_class->deactivate = deactivate;
+	parent_class->is_available = is_available;
+	parent_class->component_added = component_added;
 
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
@@ -594,30 +617,18 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 #define NM_VLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VLAN_FACTORY, NMVlanFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
 	int parent_ifindex = -1;
 	NMDevice *parent, *device;
 
-	if (plink->type != NM_LINK_TYPE_VLAN)
-		return NULL;
-
-	/* Have to find the parent device */
-	if (!nm_platform_vlan_get_info (plink->ifindex, &parent_ifindex, NULL)) {
-		nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", plink->name);
+	/* Find the parent device */
+	if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, plink->ifindex, &parent_ifindex, NULL)) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		                     "VLAN parent ifindex unknown");
 		return NULL;
 	}
-
 	parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
-	if (!parent) {
-		/* If udev signaled the VLAN interface before it signaled
-		 * the VLAN's parent at startup we may not know about the
-		 * parent device yet.  But we'll find it on the second pass
-		 * from nm_manager_start().
-		 */
-		nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", plink->name);
-		return NULL;
-	}
 
 	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
 	                                    NM_DEVICE_PLATFORM_DEVICE, plink,
@@ -627,14 +638,12 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
 	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
 	                                    NULL);
 	if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		                     "VLAN initialization failed");
 		g_object_unref (device);
 		device = NULL;
 	}
 
-	/* Set initial parent-dependent unmanaged flag */
-	if (device)
-		nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_PARENT, !nm_device_get_managed (parent));
-
 	return device;
 }
 
@@ -646,15 +655,17 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 {
 	NMDevice *device;
 	NMSettingVlan *s_vlan;
-	char *iface;
+	gs_free char *iface = NULL;
+	NMPlatformError plerr;
 
-	if (!nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME))
+	if (!NM_IS_DEVICE (parent)) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		                     "VLAN interfaces must have parents");
 		return NULL;
-
-	g_return_val_if_fail (NM_IS_DEVICE (parent), NULL);
+	}
 
 	s_vlan = nm_connection_get_setting_vlan (connection);
-	g_return_val_if_fail (s_vlan != NULL, NULL);
+	g_assert (s_vlan);
 
 	iface = g_strdup (nm_connection_get_interface_name (connection));
 	if (!iface) {
@@ -662,14 +673,18 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 		                                nm_setting_vlan_get_id (s_vlan));
 	}
 
-	if (   !nm_platform_vlan_add (iface,
+	plerr = nm_platform_vlan_add (NM_PLATFORM_GET,
+	                              iface,
 	                              nm_device_get_ifindex (parent),
 	                              nm_setting_vlan_get_id (s_vlan),
-	                              nm_setting_vlan_get_flags (s_vlan))
-	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_VLAN, "(%s) failed to add VLAN interface for '%s'",
-		             iface, nm_connection_get_id (connection));
-		g_free (iface);
+	                              nm_setting_vlan_get_flags (s_vlan),
+	                              NULL);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create VLAN interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
 		return NULL;
 	}
 
@@ -680,21 +695,74 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 	                                    NM_DEVICE_TYPE_DESC, "VLAN",
 	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
 	                                    NULL);
-	g_free (iface);
 	if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create VLAN interface '%s' for '%s': initialization failed",
+		             iface, nm_connection_get_id (connection));
 		g_object_unref (device);
 		device = NULL;
 	}
 
-	/* Set initial parent-dependent unmanaged flag */
-	if (device)
-		nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_PARENT, !nm_device_get_managed (parent));
-
 	return device;
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL(VLAN, Vlan, vlan, \
-	factory_iface->new_link = new_link; \
+static const char *
+get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
+{
+	NMSettingVlan *s_vlan;
+	NMSettingWired *s_wired;
+	const char *parent = NULL;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME), NULL);
+
+	s_vlan = nm_connection_get_setting_vlan (connection);
+	g_assert (s_vlan);
+
+	parent = nm_setting_vlan_get_parent (s_vlan);
+	if (parent)
+		return parent;
+
+	/* Try the hardware address from the VLAN connection's hardware setting */
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (s_wired)
+		return nm_setting_wired_get_mac_address (s_wired);
+
+	return NULL;
+}
+
+static char *
+get_virtual_iface_name (NMDeviceFactory *factory,
+                        NMConnection *connection,
+                        const char *parent_iface)
+{
+	const char *ifname;
+	NMSettingVlan *s_vlan;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME), NULL);
+
+	s_vlan = nm_connection_get_setting_vlan (connection);
+	g_assert (s_vlan);
+
+	if (!parent_iface)
+		return NULL;
+
+	ifname = nm_connection_get_interface_name (connection);
+	if (ifname)
+		return g_strdup (ifname);
+
+	/* 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.
+	 */
+	return nm_utils_new_vlan_name (parent_iface, nm_setting_vlan_get_id (s_vlan));
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (VLAN, Vlan, vlan,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_VLAN)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_VLAN_SETTING_NAME),
+	factory_iface->new_link = new_link;
 	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	factory_iface->get_connection_parent = get_connection_parent;
+	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
 	)
 
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index ed6b8fa0..186e00de 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -76,7 +76,7 @@ update_properties (NMDevice *device)
 	GObject *object = G_OBJECT (device);
 	NMPlatformVxlanProperties props;
 
-	if (!nm_platform_vxlan_get_properties (nm_device_get_ifindex (device), &props)) {
+	if (!nm_platform_vxlan_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
 		_LOGW (LOGD_HW, "could not read vxlan properties");
 		return;
 	}
@@ -199,7 +199,7 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->props.src_port_max);
 		break;
 	case PROP_PROXY:
-		g_value_set_uint (value, priv->props.proxy);
+		g_value_set_boolean (value, priv->props.proxy);
 		break;
 	case PROP_RSC:
 		g_value_set_boolean (value, priv->props.rsc);
@@ -353,19 +353,17 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 #define NM_VXLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VXLAN_FACTORY, NMVxlanFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_VXLAN) {
-		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VXLAN,
-		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-		                                  NM_DEVICE_TYPE_DESC, "Vxlan",
-		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
-		                                  NULL);
-	}
-	return NULL;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VXLAN,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_TYPE_DESC, "Vxlan",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+	                                  NULL);
 }
 
-DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(VXLAN, Vxlan, vxlan, GENERIC, \
-	factory_iface->new_link = new_link; \
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (VXLAN, Vxlan, vxlan,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_VXLAN),
+	factory_iface->new_link = new_link;
 	)
 
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 47526281..c5d4c203 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -28,8 +28,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
-#include <linux/sockios.h>
-#include <linux/ethtool.h>
 #include <sys/ioctl.h>
 #include <signal.h>
 #include <sys/types.h>
@@ -62,7 +60,6 @@
 #include "nm-enum-types.h"
 #include "nm-settings-connection.h"
 #include "nm-connection-provider.h"
-#include "nm-posix-signals.h"
 #include "nm-auth-utils.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-dispatcher.h"
@@ -70,12 +67,14 @@
 #include "nm-dns-manager.h"
 #include "nm-core-internal.h"
 #include "nm-default-route-manager.h"
+#include "nm-route-manager.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDevice);
 
 static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context);
 static void impl_device_delete     (NMDevice *self, DBusGMethodInvocation *context);
+static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data);
 
 #include "nm-device-glue.h"
 
@@ -139,6 +138,12 @@ enum {
 #define PENDING_ACTION_AUTOCONF6 "autoconf6"
 
 typedef enum {
+	CLEANUP_TYPE_DECONFIGURE,
+	CLEANUP_TYPE_KEEP,
+	CLEANUP_TYPE_REMOVED,
+} CleanupType;
+
+typedef enum {
 	IP_NONE = 0,
 	IP_WAIT,
 	IP_CONF,
@@ -164,6 +169,9 @@ typedef struct {
 	guint timeout;
 	guint watch;
 	GPid pid;
+	const char *binary;
+	const char *address;
+	guint deadline;
 } PingInfo;
 
 typedef struct {
@@ -175,23 +183,28 @@ typedef struct {
 typedef struct {
 	gboolean in_state_changed;
 	gboolean initialized;
+	gboolean platform_link_initialized;
+
+	guint device_link_changed_id;
+	guint device_ip_link_changed_id;
 
 	NMDeviceState state;
 	NMDeviceStateReason state_reason;
 	QueuedState   queued_state;
-	guint queued_ip_config_id;
+	guint queued_ip4_config_id;
+	guint queued_ip6_config_id;
 	GSList *pending_actions;
 
 	char *        udi;
 	char *        path;
 	char *        iface;   /* may change, could be renamed by user */
 	int           ifindex;
-	gboolean      is_software;
 	char *        ip_iface;
 	int           ip_ifindex;
 	NMDeviceType  type;
 	char *        type_desc;
-	guint32       capabilities;
+	char *        type_description;
+	NMDeviceCapabilities capabilities;
 	char *        driver;
 	char *        driver_version;
 	char *        firmware_version;
@@ -200,6 +213,8 @@ typedef struct {
 	GHashTable *  available_connections;
 	char *        hw_addr;
 	guint         hw_addr_len;
+	char *        perm_hw_addr;
+	char *        initial_hw_addr;
 	char *        physical_port_id;
 	guint         dev_id;
 
@@ -220,6 +235,11 @@ typedef struct {
 	gpointer        act_source6_func;
 	guint           recheck_assume_id;
 	struct {
+		guint       		call_id;
+		NMDeviceStateReason available_reason;
+		NMDeviceStateReason unavailable_reason;
+	}               recheck_available;
+	struct {
 		guint               call_id;
 		NMDeviceState       post_state;
 		NMDeviceStateReason post_state_reason;
@@ -242,16 +262,18 @@ typedef struct {
 	/* IP4 configuration info */
 	NMIP4Config *   ip4_config;     /* Combined config from VPN, settings, and device */
 	IpState         ip4_state;
+	NMIP4Config *   con_ip4_config; /* config from the setting */
 	NMIP4Config *   dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */
 	NMIP4Config *   ext_ip4_config; /* Stuff added outside NM */
-	gboolean        ext_ip4_config_had_any_addresses;
 	NMIP4Config *   wwan_ip4_config; /* WWAN configuration */
 	struct {
 		gboolean v4_has;
 		gboolean v4_is_assumed;
+		gboolean v4_configure_first_time;
 		NMPlatformIP4Route v4;
 		gboolean v6_has;
 		gboolean v6_is_assumed;
+		gboolean v6_configure_first_time;
 		NMPlatformIP6Route v6;
 	} default_route;
 
@@ -279,11 +301,12 @@ typedef struct {
 	/* IP6 configuration info */
 	NMIP6Config *  ip6_config;
 	IpState        ip6_state;
+	NMIP6Config *  con_ip6_config; /* config from the setting */
 	NMIP6Config *  vpn6_config;  /* routes added by a VPN which uses this device */
 	NMIP6Config *  wwan_ip6_config;
 	NMIP6Config *  ext_ip6_config; /* Stuff added outside NM */
-	gboolean       ext_ip6_config_had_any_addresses;
 	gboolean       nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
+	guint32        ip6_mtu;
 
 	NMRDisc *      rdisc;
 	gulong         rdisc_changed_id;
@@ -322,6 +345,7 @@ static gboolean nm_device_set_ip4_config (NMDevice *self,
                                           NMIP4Config *config,
                                           guint32 default_route_metric,
                                           gboolean commit,
+                                          gboolean routes_full_sync,
                                           NMDeviceStateReason *reason);
 static gboolean ip4_config_merge_and_apply (NMDevice *self,
                                             NMIP4Config *config,
@@ -331,6 +355,7 @@ static gboolean ip4_config_merge_and_apply (NMDevice *self,
 static gboolean nm_device_set_ip6_config (NMDevice *self,
                                           NMIP6Config *config,
                                           gboolean commit,
+                                          gboolean routes_full_sync,
                                           NMDeviceStateReason *reason);
 
 static gboolean nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure);
@@ -448,6 +473,8 @@ static const char *reason_table[] = {
 	[NM_DEVICE_STATE_REASON_MODEM_AVAILABLE]          = "modem-available",
 	[NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT]        = "sim-pin-incorrect",
 	[NM_DEVICE_STATE_REASON_NEW_ACTIVATION]           = "new-activation",
+	[NM_DEVICE_STATE_REASON_PARENT_CHANGED]           = "parent-changed",
+	[NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED]   = "parent-managed-changed",
 };
 
 static const char *
@@ -463,13 +490,19 @@ reason_to_string (NMDeviceStateReason reason)
 gboolean
 nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value)
 {
-	return nm_platform_sysctl_set (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value);
+	return nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value);
 }
 
-static gboolean
-device_has_capability (NMDevice *self, NMDeviceCapabilities caps)
+static guint32
+nm_device_ipv6_sysctl_get_int32 (NMDevice *self, const char *property, gint32 fallback)
 {
-	return !!(NM_DEVICE_GET_PRIVATE (self)->capabilities & caps);
+	return nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), fallback);
+}
+
+gboolean
+nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps)
+{
+	return NM_FLAGS_ANY (NM_DEVICE_GET_PRIVATE (self)->capabilities, caps);
 }
 
 /***********************************************************/
@@ -486,7 +519,7 @@ nm_device_dbus_export (NMDevice *self)
 	g_return_if_fail (priv->path == NULL);
 
 	priv->path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++);
-	_LOGI (LOGD_DEVICE, "exported as %s", priv->path);
+	_LOGD (LOGD_DEVICE, "exported as %s", priv->path);
 	nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, self);
 }
 
@@ -509,7 +542,7 @@ nm_device_get_udi (NMDevice *self)
 const char *
 nm_device_get_iface (NMDevice *self)
 {
-	g_return_val_if_fail (NM_IS_DEVICE (self), 0);
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
 	return NM_DEVICE_GET_PRIVATE (self)->iface;
 }
@@ -525,9 +558,7 @@ nm_device_get_ifindex (NMDevice *self)
 gboolean
 nm_device_is_software (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	return priv->is_software;
+	return NM_FLAGS_HAS (NM_DEVICE_GET_PRIVATE (self)->capabilities, NM_DEVICE_CAP_IS_SOFTWARE);
 }
 
 const char *
@@ -550,7 +581,7 @@ nm_device_get_ip_ifindex (NMDevice *self)
 	g_return_val_if_fail (self != NULL, 0);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	/* If it's not set, default to iface */
+	/* If it's not set, default to ifindex */
 	return priv->ip_iface ? priv->ip_ifindex : priv->ifindex;
 }
 
@@ -571,13 +602,13 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 
 	priv->ip_iface = g_strdup (iface);
 	if (priv->ip_iface) {
-		priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
+		priv->ip_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface);
 		if (priv->ip_ifindex > 0) {
-			if (nm_platform_check_support_user_ipv6ll ())
-				nm_platform_link_set_user_ipv6ll_enabled (priv->ip_ifindex, TRUE);
+			if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET))
+				nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, priv->ip_ifindex, TRUE);
 
-			if (!nm_platform_link_is_up (priv->ip_ifindex))
-				nm_platform_link_set_up (priv->ip_ifindex);
+			if (!nm_platform_link_is_up (NM_PLATFORM_GET, priv->ip_ifindex))
+				nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex, NULL);
 		} else {
 			/* Device IP interface must always be a kernel network interface */
 			_LOGW (LOGD_HW, "failed to look up interface index");
@@ -607,10 +638,10 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 	ifindex = nm_device_get_ip_ifindex (self);
 	g_assert (ifindex);
 
-	link_type = nm_platform_link_get_type (ifindex);
+	link_type = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex);
 	g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, 0);
 
-	hwaddr = nm_platform_link_get_address (ifindex, &hwaddr_len);
+	hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddr_len);
 	if (!hwaddr_len)
 		return FALSE;
 
@@ -724,50 +755,63 @@ nm_device_get_priority (NMDevice *self)
 	return 11000;
 }
 
-guint32
-nm_device_get_ip4_route_metric (NMDevice *self)
+static guint32
+_get_ipx_route_metric (NMDevice *self,
+                       gboolean is_v4)
 {
+	char *value;
+	gint64 route_metric;
+	NMSettingIPConfig *s_ip;
 	NMConnection *connection;
-	NMSettingIPConfig *s_ip = NULL;
-	gint64 route_metric = -1;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
 
 	connection = nm_device_get_connection (self);
-	if (connection)
-		s_ip = nm_connection_get_setting_ip4_config (connection);
+	if (connection) {
+		s_ip = is_v4
+		       ? nm_connection_get_setting_ip4_config (connection)
+		       : nm_connection_get_setting_ip6_config (connection);
 
-	/* Slave interfaces don't have IP settings, but we may get here when
-	 * external changes are made or when noticing IP changes when starting
-	 * the slave connection.
-	 */
-	if (s_ip)
-		route_metric = nm_setting_ip_config_get_route_metric (s_ip);
+		/* Slave interfaces don't have IP settings, but we may get here when
+		 * external changes are made or when noticing IP changes when starting
+		 * the slave connection.
+		 */
+		if (s_ip) {
+			route_metric = nm_setting_ip_config_get_route_metric (s_ip);
+			if (route_metric >= 0)
+				goto out;
+		}
+	}
 
-	return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
+	/* use the current NMConfigData, which makes this configuration reloadable.
+	 * Note that that means that the route-metric might change between SIGHUP.
+	 * You must cache the returned value if that is a problem. */
+	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+	                                               is_v4 ? "ipv4.route-metric" : "ipv6.route-metric", self);
+	if (value) {
+		route_metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
+		g_free (value);
+
+		if (route_metric >= 0)
+			goto out;
+	}
+	route_metric = nm_device_get_priority (self);
+out:
+	if (!is_v4)
+		route_metric = nm_utils_ip6_route_metric_normalize (route_metric);
+	return route_metric;
 }
 
 guint32
-nm_device_get_ip6_route_metric (NMDevice *self)
+nm_device_get_ip4_route_metric (NMDevice *self)
 {
-	NMConnection *connection;
-	NMSettingIPConfig *s_ip = NULL;
-	gint64 route_metric = -1;
-
-	g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
-
-	connection = nm_device_get_connection (self);
-	if (connection)
-		s_ip = nm_connection_get_setting_ip6_config (connection);
-
-	/* Slave interfaces don't have IP settings, but we may get here when
-	 * external changes are made or when noticing IP changes when starting
-	 * the slave connection.
-	 */
-	if (s_ip)
-		route_metric = nm_setting_ip_config_get_route_metric (s_ip);
+	return _get_ipx_route_metric (self, TRUE);
+}
 
-	return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
+guint32
+nm_device_get_ip6_route_metric (NMDevice *self)
+{
+	return _get_ipx_route_metric (self, FALSE);
 }
 
 const NMPlatformIP4Route *
@@ -808,6 +852,34 @@ nm_device_get_type_desc (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->type_desc;
 }
 
+const char *
+nm_device_get_type_description (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+
+	/* Beware: this function should return the same
+	 * value as nm_device_get_type_description() in libnm. */
+
+	return NM_DEVICE_GET_CLASS (self)->get_type_description (self);
+}
+
+static const char *
+get_type_description (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->type_description) {
+		const char *typename;
+
+		typename = G_OBJECT_TYPE_NAME (self);
+		if (g_str_has_prefix (typename, "NMDevice"))
+			typename += 8;
+		priv->type_description = g_ascii_strdown (typename, -1);
+	}
+
+	return priv->type_description;
+}
+
 gboolean
 nm_device_has_carrier (NMDevice *self)
 {
@@ -1012,8 +1084,15 @@ nm_device_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure
 	return success;
 }
 
+/**
+ * can_unmanaged_external_down:
+ * @self: the device
+ *
+ * Check whether the device should stay NM_UNMANAGED_EXTERNAL_DOWN unless
+ * IFF_UP-ed externally.
+ */
 static gboolean
-is_software_external (NMDevice *self)
+can_unmanaged_external_down (NMDevice *self)
 {
 	return   nm_device_is_software (self)
 	      && !nm_device_get_is_nm_owned (self);
@@ -1030,22 +1109,75 @@ void
 nm_device_finish_init (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean platform_unmanaged = FALSE;
 
 	g_assert (priv->initialized == FALSE);
 
 	/* Do not manage externally created software devices until they are IFF_UP */
-	if (   is_software_external (self)
-	    && !nm_platform_link_is_up (priv->ifindex)
+	if (   NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)
+	    && !nm_platform_link_is_up (NM_PLATFORM_GET, priv->ifindex)
 	    && priv->ifindex > 0)
 		nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE);
 
 	if (priv->master)
 		nm_device_enslave_slave (priv->master, self, NULL);
 
+	if (priv->ifindex > 0) {
+		if (priv->ifindex == 1) {
+			/* keep 'lo' as default-unmanaged. */
+
+			/* FIXME: either find a better way to unmange 'lo' that cannot be changed
+			 * by user configuration (NM_UNMANGED_LOOPBACK?) or fix managing 'lo'.
+			 * Currently it can happen that NM deletes 127.0.0.1 address. */
+			nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, TRUE);
+		} else if (priv->platform_link_initialized || (priv->is_nm_owned && nm_device_is_software (self))) {
+			nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged);
+			nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, platform_unmanaged);
+		} else {
+			/* Hardware and externally-created software links stay unmanaged
+			 * until they are fully initialized by the platform. NM created
+			 * links must be available for activation immediately and thus
+			 * do not get the PLATFORM_INIT unmanaged flag set.
+			 */
+			nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_PLATFORM_INIT, TRUE);
+		}
+	}
+
 	priv->initialized = TRUE;
 }
 
 static void
+update_dynamic_ip_setup (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	g_hash_table_remove_all (priv->ip6_saved_properties);
+
+	if (priv->dhcp4_client) {
+		if (!nm_device_dhcp4_renew (self, FALSE)) {
+			nm_device_state_changed (self,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_DHCP_FAILED);
+			return;
+		}
+	}
+	if (priv->dhcp6_client) {
+		if (!nm_device_dhcp6_renew (self, FALSE)) {
+			nm_device_state_changed (self,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_DHCP_FAILED);
+			return;
+		}
+	}
+	if (priv->rdisc) {
+		/* FIXME: todo */
+	}
+	if (priv->dnsmasq_manager) {
+		/* FIXME: todo */
+	}
+}
+
+static void
 carrier_changed (NMDevice *self, gboolean carrier)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -1094,6 +1226,12 @@ carrier_changed (NMDevice *self, gboolean carrier)
 			 * the device.
 			 */
 			nm_device_emit_recheck_auto_activate (self);
+		} else if (priv->state == NM_DEVICE_STATE_ACTIVATED) {
+			/* If the device is active without a carrier (probably because it is
+			 * tagged for carrier ignore) ensure that when the carrier appears we
+			 * renew DHCP leases and such.
+			 */
+			update_dynamic_ip_setup (self);
 		}
 	} else {
 		g_return_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE);
@@ -1176,37 +1314,6 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier)
 }
 
 static void
-update_for_ip_ifname_change (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	g_hash_table_remove_all (priv->ip6_saved_properties);
-
-	if (priv->dhcp4_client) {
-		if (!nm_device_dhcp4_renew (self, FALSE)) {
-			nm_device_state_changed (self,
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_DHCP_FAILED);
-			return;
-		}
-	}
-	if (priv->dhcp6_client) {
-		if (!nm_device_dhcp6_renew (self, FALSE)) {
-			nm_device_state_changed (self,
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_DHCP_FAILED);
-			return;
-		}
-	}
-	if (priv->rdisc) {
-		/* FIXME: todo */
-	}
-	if (priv->dnsmasq_manager) {
-		/* FIXME: todo */
-	}
-}
-
-static void
 device_set_master (NMDevice *self, int ifindex)
 {
 	NMDevice *master;
@@ -1223,35 +1330,58 @@ device_set_master (NMDevice *self, int ifindex)
 	} else {
 		_LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s",
 		       ifindex,
-		       nm_platform_link_get_name (ifindex));
+		       nm_platform_link_get_name (NM_PLATFORM_GET, ifindex));
 	}
 }
 
-static void
-device_link_changed (NMDevice *self, NMPlatformLink *info)
+static gboolean
+device_link_changed (NMDevice *self)
 {
 	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMUtilsIPv6IfaceId token_iid;
 	gboolean ip_ifname_changed = FALSE;
+	gboolean platform_unmanaged = FALSE;
+	const char *udi;
+	NMPlatformLink info;
+	const NMPlatformLink *pllink;
+	int ifindex;
+
+	priv->device_link_changed_id = 0;
+
+	ifindex = nm_device_get_ifindex (self);
+	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
+	if (!pllink)
+		return G_SOURCE_REMOVE;
 
-	if (info->udi && g_strcmp0 (info->udi, priv->udi)) {
+	info = *pllink;
+
+	udi = nm_platform_link_get_udi (NM_PLATFORM_GET, info.ifindex);
+	if (udi && g_strcmp0 (udi, priv->udi)) {
 		/* Update UDI to what udev gives us */
 		g_free (priv->udi);
-		priv->udi = g_strdup (info->udi);
+		priv->udi = g_strdup (udi);
 		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
 	}
 
+	if (g_strcmp0 (info.driver, priv->driver)) {
+		/* Update driver to what udev gives us */
+		g_free (priv->driver);
+		priv->driver = g_strdup (info.driver);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER);
+	}
+
 	/* Update MTU if it has changed. */
-	if (priv->mtu != info->mtu) {
-		priv->mtu = info->mtu;
+	if (priv->mtu != info.mtu) {
+		priv->mtu = info.mtu;
 		g_object_notify (G_OBJECT (self), NM_DEVICE_MTU);
 	}
 
-	if (info->name[0] && strcmp (priv->iface, info->name) != 0) {
+	if (info.name[0] && strcmp (priv->iface, info.name) != 0) {
 		_LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'",
-		       priv->ifindex, priv->iface, info->name);
+		       priv->ifindex, priv->iface, info.name);
 		g_free (priv->iface);
-		priv->iface = g_strdup (info->name);
+		priv->iface = g_strdup (info.name);
 
 		/* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */
 		ip_ifname_changed = !priv->ip_iface;
@@ -1270,30 +1400,36 @@ device_link_changed (NMDevice *self, NMPlatformLink *info)
 	}
 
 	/* Update slave status for external changes */
-	if (priv->enslaved && info->master != nm_device_get_ifindex (priv->master))
+	if (priv->enslaved && info.master != nm_device_get_ifindex (priv->master))
 		nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_NONE);
-	if (info->master && !priv->enslaved) {
-		device_set_master (self, info->master);
+	if (info.master && !priv->enslaved) {
+		device_set_master (self, info.master);
 		if (priv->master)
 			nm_device_enslave_slave (priv->master, self, NULL);
 	}
 
+	if (priv->rdisc && nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &token_iid)) {
+		_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
+		if (nm_rdisc_set_iid (priv->rdisc, token_iid))
+			nm_rdisc_start (priv->rdisc);
+	}
+
 	if (klass->link_changed)
-		klass->link_changed (self, info);
+		klass->link_changed (self, &info);
 
 	/* Update DHCP, etc, if needed */
 	if (ip_ifname_changed)
-		update_for_ip_ifname_change (self);
+		update_dynamic_ip_setup (self);
 
-	if (priv->up != info->up) {
-		priv->up = info->up;
+	if (priv->up != NM_FLAGS_HAS (info.flags, IFF_UP)) {
+		priv->up = NM_FLAGS_HAS (info.flags, IFF_UP);
 
 		/* Manage externally-created software interfaces only when they are IFF_UP */
 		g_assert (priv->ifindex > 0);
-		if (is_software_external (self)) {
+		if (NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)) {
 			gboolean external_down = nm_device_get_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN);
 
-			if (external_down && info->up) {
+			if (external_down && NM_FLAGS_HAS (info.flags, IFF_UP)) {
 				if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) {
 					/* Ensure the assume check is queued before any queued state changes
 					 * from the transition to UNAVAILABLE.
@@ -1316,7 +1452,7 @@ device_link_changed (NMDevice *self, NMPlatformLink *info)
 					 */
 					priv->unmanaged_flags &= ~NM_UNMANAGED_EXTERNAL_DOWN;
 				}
-			} else if (!external_down && !info->up && nm_device_get_state (self) <= NM_DEVICE_STATE_DISCONNECTED) {
+			} else if (!external_down && !NM_FLAGS_HAS (info.flags, IFF_UP) && nm_device_get_state (self) <= NM_DEVICE_STATE_DISCONNECTED) {
 				/* If the device is already disconnected and is set !IFF_UP,
 				 * unmanage it.
 				 */
@@ -1327,57 +1463,120 @@ device_link_changed (NMDevice *self, NMPlatformLink *info)
 			}
 		}
 	}
+
+	if (priv->ifindex > 0 && !priv->platform_link_initialized && info.initialized) {
+		priv->platform_link_initialized = TRUE;
+
+		if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged)) {
+			nm_device_set_unmanaged (self,
+			                         NM_UNMANAGED_DEFAULT,
+			                         platform_unmanaged,
+			                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+		}
+
+		nm_device_set_unmanaged (self,
+		                         NM_UNMANAGED_PLATFORM_INIT,
+		                         FALSE,
+		                         NM_DEVICE_STATE_REASON_NOW_MANAGED);
+	}
+
+	return G_SOURCE_REMOVE;
 }
 
-static void
-device_ip_link_changed (NMDevice *self, NMPlatformLink *info)
+static gboolean
+device_ip_link_changed (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const NMPlatformLink *pllink;
+	int ip_ifindex;
+
+	priv->device_ip_link_changed_id = 0;
 
-	if (info->name[0] && g_strcmp0 (priv->ip_iface, info->name)) {
+	ip_ifindex = nm_device_get_ip_ifindex (self);
+	pllink = nm_platform_link_get (NM_PLATFORM_GET, ip_ifindex);
+	if (!pllink)
+		return G_SOURCE_REMOVE;
+
+	if (pllink->name[0] && g_strcmp0 (priv->ip_iface, pllink->name)) {
 		_LOGI (LOGD_DEVICE, "interface index %d renamed ip_iface (%d) from '%s' to '%s'",
 		       priv->ifindex, nm_device_get_ip_ifindex (self),
-		       priv->ip_iface, info->name);
+		       priv->ip_iface, pllink->name);
 		g_free (priv->ip_iface);
-		priv->ip_iface = g_strdup (info->name);
+		priv->ip_iface = g_strdup (pllink->name);
 
 		g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
-		update_for_ip_ifname_change (self);
+		update_dynamic_ip_setup (self);
 	}
+	return G_SOURCE_REMOVE;
 }
 
 static void
 link_changed_cb (NMPlatform *platform,
+                 NMPObjectType obj_type,
                  int ifindex,
                  NMPlatformLink *info,
                  NMPlatformSignalChangeType change_type,
                  NMPlatformReason reason,
                  NMDevice *self)
 {
+	NMDevicePrivate *priv;
+
 	if (change_type != NM_PLATFORM_SIGNAL_CHANGED)
 		return;
 
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
 	/* We don't filter by 'reason' because we are interested in *all* link
 	 * changes. For example a call to nm_platform_link_set_up() may result
 	 * in an internal carrier change (i.e. we ask the kernel to set IFF_UP
 	 * and it results in also setting IFF_LOWER_UP.
 	 */
 
-	if (ifindex == nm_device_get_ifindex (self))
-		device_link_changed (self, info);
-	else if (ifindex == nm_device_get_ip_ifindex (self))
-		device_ip_link_changed (self, info);
+	if (ifindex == nm_device_get_ifindex (self)) {
+		if (!priv->device_link_changed_id) {
+			priv->device_link_changed_id = g_idle_add ((GSourceFunc) device_link_changed, self);
+			_LOGD (LOGD_DEVICE, "queued link change for ifindex %d", ifindex);
+		}
+	} else if (ifindex == nm_device_get_ip_ifindex (self)) {
+		if (!priv->device_ip_link_changed_id) {
+			priv->device_ip_link_changed_id = g_idle_add ((GSourceFunc) device_ip_link_changed, self);
+			_LOGD (LOGD_DEVICE, "queued link change for ip-ifindex %d", ifindex);
+		}
+	}
 }
 
 static void
 link_changed (NMDevice *self, NMPlatformLink *info)
 {
 	/* Update carrier from link event if applicable. */
-	if (   device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)
-	    && !device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
+	if (   nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)
+	    && !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
 		nm_device_set_carrier (self, info->connected);
 }
 
+static void
+config_changed_update_ignore_carrier (NMConfig *config,
+                                      NMConfigData *config_data,
+                                      NMConfigChangeFlags changes,
+                                      NMConfigData *old_data,
+                                      NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (   priv->state <= NM_DEVICE_STATE_DISCONNECTED
+	    || priv->state > NM_DEVICE_STATE_ACTIVATED)
+		priv->ignore_carrier = nm_config_data_get_ignore_carrier (config_data, self);
+}
+
+static void
+check_carrier (NMDevice *self)
+{
+	int ifindex = nm_device_get_ip_ifindex (self);
+
+	if (!nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
+		nm_device_set_carrier (self, nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex));
+}
+
 /**
  * nm_device_notify_component_added():
  * @self: the #NMDevice
@@ -1965,7 +2164,7 @@ device_has_config (NMDevice *self)
 		return TRUE;
 
 	/* Slaves are also configured by definition */
-	if (nm_platform_link_get_master (priv->ifindex) > 0)
+	if (nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) > 0)
 		return TRUE;
 
 	return FALSE;
@@ -2103,6 +2302,20 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
 		connection = NULL;
 	}
 
+	/* Ignore any IPv6LL-only, not master connections without slaves,
+	 * unless they are in the assume-ipv6ll-only list.
+	 */
+	if (   connection
+	    && g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0
+	    && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
+	    && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))
+	    && !priv->slaves
+	    && !nm_config_data_get_assume_ipv6ll_only (NM_CONFIG_GET_DATA, self)) {
+		_LOGD (LOGD_DEVICE, "ignoring generated connection (IPv6LL-only and not in master-slave relationship)");
+		g_object_unref (connection);
+		connection = NULL;
+	}
+
 	return connection;
 }
 
@@ -2274,6 +2487,47 @@ nm_device_queue_recheck_assume (NMDevice *self)
 		priv->recheck_assume_id = g_idle_add (nm_device_emit_recheck_assume, self);
 }
 
+static gboolean
+recheck_available (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean now_available = nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
+	NMDeviceState state = nm_device_get_state (self);
+	NMDeviceState new_state = NM_DEVICE_STATE_UNKNOWN;
+
+	priv->recheck_available.call_id = 0;
+
+	if (state == NM_DEVICE_STATE_UNAVAILABLE && now_available) {
+		new_state = NM_DEVICE_STATE_DISCONNECTED;
+		nm_device_queue_state (self, new_state, priv->recheck_available.available_reason);
+	} else if (state >= NM_DEVICE_STATE_DISCONNECTED && !now_available) {
+		new_state = NM_DEVICE_STATE_UNAVAILABLE;
+		nm_device_queue_state (self, new_state, priv->recheck_available.unavailable_reason);
+	}
+	_LOGD (LOGD_DEVICE, "device is %savailable, %s %s",
+	       now_available ? "" : "not ",
+	       new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to",
+	       state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state));
+
+	priv->recheck_available.available_reason = NM_DEVICE_STATE_REASON_NONE;
+	priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE;
+	return G_SOURCE_REMOVE;
+}
+
+void
+nm_device_queue_recheck_available (NMDevice *self,
+                                   NMDeviceStateReason available_reason,
+                                   NMDeviceStateReason unavailable_reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	priv->recheck_available.available_reason = available_reason;
+	priv->recheck_available.unavailable_reason = unavailable_reason;
+	if (!priv->recheck_available.call_id)
+		priv->recheck_available.call_id = g_idle_add (recheck_available, self);
+}
+
 void
 nm_device_emit_recheck_auto_activate (NMDevice *self)
 {
@@ -2432,7 +2686,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
 	/* Notify the new ActiveConnection along with the state change */
 	g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION);
 
-	_LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) started...");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) started...");
 	nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Assumed connections were already set up outside NetworkManager */
@@ -2466,7 +2720,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
 		nm_device_activate_schedule_stage2_device_config (self);
 
 out:
-	_LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) complete.");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) complete.");
 	return FALSE;
 }
 
@@ -2489,7 +2743,7 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_stage1_device_prepare, 0);
 
-	_LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) scheduled...");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) scheduled...");
 }
 
 static NMActStageReturn
@@ -2520,7 +2774,7 @@ nm_device_activate_stage2_device_config (gpointer user_data)
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, 0);
 
-	_LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) starting...");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) starting...");
 	nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Assumed connections were already set up outside NetworkManager */
@@ -2555,12 +2809,12 @@ nm_device_activate_stage2_device_config (gpointer user_data)
 			nm_device_queue_recheck_assume (info->slave);
 	}
 
-	_LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful.");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful.");
 
 	nm_device_activate_schedule_stage3_ip_config_start (self);
 
 out:
-	_LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete.");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete.");
 	return FALSE;
 }
 
@@ -2583,7 +2837,7 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_stage2_device_config, 0);
 
-	_LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) scheduled...");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) scheduled...");
 }
 
 /*********************************************/
@@ -2759,23 +3013,6 @@ aipd_timeout_cb (gpointer user_data)
 	return FALSE;
 }
 
-static void
-aipd_child_setup (gpointer user_data G_GNUC_UNUSED)
-{
-	/* We are in the child process at this point.
-	 * Give child it's own program group for signal
-	 * separation.
-	 */
-	pid_t pid = getpid ();
-	setpgid (pid, pid);
-
-	/*
-	 * We blocked signals in main(). We need to restore original signal
-	 * mask for avahi-autoipd here so that it can receive signals.
-	 */
-	nm_unblock_posix_signals (NULL);
-}
-
 /* default to installed helper, but can be modified for testing */
 const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action";
 
@@ -2815,7 +3052,7 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason)
 	g_free (cmdline);
 
 	if (!g_spawn_async ("/", (char **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                    &aipd_child_setup, NULL, &(priv->aipd_pid), &error)) {
+	                    nm_utils_setpgid, NULL, &(priv->aipd_pid), &error)) {
 		_LOGW (LOGD_DEVICE | LOGD_AUTOIP4,
 		       "Activation: Stage 3 of 5 (IP Configure Start) failed"
 		       " to start avahi-autoipd: %s",
@@ -2825,7 +3062,7 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	_LOGI (LOGD_DEVICE | LOGD_AUTOIP4,
+	_LOGD (LOGD_DEVICE | LOGD_AUTOIP4,
 	       "Activation: Stage 3 of 5 (IP Configure Start) started"
 	       " avahi-autoipd...");
 
@@ -2848,9 +3085,9 @@ _device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlat
 	GArray *routes;
 
 	if (addr_family == AF_INET)
-		routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT);
+		routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
 	else
-		routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT);
+		routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
 
 	if (routes) {
 		guint route_metric = G_MAXUINT32, m;
@@ -2885,10 +3122,62 @@ _device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlat
 }
 
 /*********************************************/
+
+static void
+ensure_con_ip4_config (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+
+	if (priv->con_ip4_config)
+		return;
+
+	connection = nm_device_get_connection (self);
+	if (!connection)
+		return;
+
+	priv->con_ip4_config = nm_ip4_config_new ();
+	nm_ip4_config_merge_setting (priv->con_ip4_config,
+	                             nm_connection_get_setting_ip4_config (connection),
+	                             nm_device_get_ip4_route_metric (self));
+
+	if (nm_device_uses_assumed_connection (self)) {
+		/* For assumed connections ignore all addresses and routes. */
+		nm_ip4_config_reset_addresses (priv->con_ip4_config);
+		nm_ip4_config_reset_routes (priv->con_ip4_config);
+	}
+}
+
+static void
+ensure_con_ip6_config (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+
+	if (priv->con_ip6_config)
+		return;
+
+	connection = nm_device_get_connection (self);
+	if (!connection)
+		return;
+
+	priv->con_ip6_config = nm_ip6_config_new ();
+	nm_ip6_config_merge_setting (priv->con_ip6_config,
+	                             nm_connection_get_setting_ip6_config (connection),
+	                             nm_device_get_ip6_route_metric (self));
+
+	if (nm_device_uses_assumed_connection (self)) {
+		/* For assumed connections ignore all addresses and routes. */
+		nm_ip6_config_reset_addresses (priv->con_ip6_config);
+		nm_ip6_config_reset_routes (priv->con_ip6_config);
+	}
+}
+
+/*********************************************/
 /* DHCPv4 stuff */
 
 static void
-dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
+dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
@@ -2901,7 +3190,8 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
 
 		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
 
-		if (stop)
+		if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
+		    || cleanup_type == CLEANUP_TYPE_REMOVED)
 			nm_dhcp_client_stop (priv->dhcp4_client, release);
 
 		g_clear_object (&priv->dhcp4_client);
@@ -2923,7 +3213,11 @@ ip4_config_merge_and_apply (NMDevice *self,
 	NMConnection *connection;
 	gboolean success;
 	NMIP4Config *composite;
+	gboolean has_direct_route;
 	const guint32 default_route_metric = nm_device_get_ip4_route_metric (self);
+	guint32 gateway;
+	gboolean connection_has_default_route, connection_is_never_default;
+	gboolean routes_full_sync;
 
 	/* Merge all the configs into the composite config */
 	if (config) {
@@ -2932,6 +3226,10 @@ ip4_config_merge_and_apply (NMDevice *self,
 	}
 
 	composite = nm_ip4_config_new ();
+
+	if (commit)
+		ensure_con_ip4_config (self);
+
 	if (priv->dev_ip4_config)
 		nm_ip4_config_merge (composite, priv->dev_ip4_config);
 	if (priv->vpn4_config)
@@ -2945,73 +3243,120 @@ ip4_config_merge_and_apply (NMDevice *self,
 	if (priv->wwan_ip4_config)
 		nm_ip4_config_merge (composite, priv->wwan_ip4_config);
 
-	/* Merge user overrides into the composite config.  Generated+assumed
-	 * connections come from the system not the user and merging them would
-	 * be redundant, so don't bother.
-	 */
+	/* Apply ignore-auto-routes and ignore-auto-dns settings */
 	connection = nm_device_get_connection (self);
+	if (connection) {
+		NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection);
+
+		if (s_ip4) {
+			if (nm_setting_ip_config_get_ignore_auto_routes (s_ip4))
+				nm_ip4_config_reset_routes (composite);
+			if (nm_setting_ip_config_get_ignore_auto_dns (s_ip4)) {
+				nm_ip4_config_reset_nameservers (composite);
+				nm_ip4_config_reset_domains (composite);
+				nm_ip4_config_reset_searches (composite);
+			}
+		}
+	}
+
+	/* Merge user overrides into the composite config. For assumed connections,
+	 * con_ip4_config is empty. */
+	if (priv->con_ip4_config)
+		nm_ip4_config_merge (composite, priv->con_ip4_config);
+
+
+	/* Add the default route.
+	 *
+	 * We keep track of the default route of a device in a private field.
+	 * NMDevice needs to know the default route at this point, because the gateway
+	 * might require a direct route (see below).
+	 *
+	 * But also, we don't want to add the default route to priv->ip4_config,
+	 * because the default route from the setting might not be the same that
+	 * NMDefaultRouteManager eventually configures (because the it might
+	 * tweak the effective metric).
+	 */
+
+	/* unless we come to a different conclusion below, we have no default route and
+	 * the route is assumed. */
 	priv->default_route.v4_has = FALSE;
 	priv->default_route.v4_is_assumed = TRUE;
-	if (connection) {
-		gboolean assumed = nm_device_uses_assumed_connection (self);
-		NMPlatformIP4Route *route = &priv->default_route.v4;
 
-		if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) {
-			nm_ip4_config_merge_setting (composite,
-			                             nm_connection_get_setting_ip4_config (connection),
-			                             default_route_metric);
-		}
+	routes_full_sync =    commit
+	                   && priv->default_route.v4_configure_first_time
+	                   && !nm_device_uses_assumed_connection (self);
 
-		/* Add the default route.
-		 *
-		 * We keep track of the default route of a device in a private field.
-		 * NMDevice needs to know the default route at this point, because the gateway
-		 * might require a direct route (see below).
-		 *
-		 * But also, we don't want to add the default route to priv->ip4_config,
-		 * because the default route from the setting might not be the same that
-		 * NMDefaultRouteManager eventually configures (because the it might
-		 * tweak the effective metric).
+	if (!commit) {
+		/* during a non-commit event, we always pickup whatever is configured. */
+		goto END_ADD_DEFAULT_ROUTE;
+	}
+
+	connection_has_default_route
+	    = nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (),
+	                                                                 connection, &connection_is_never_default);
+
+	if (   !priv->default_route.v4_configure_first_time
+	    && !nm_device_uses_assumed_connection (self)
+	    && connection_is_never_default) {
+		/* If the connection is explicitly configured as never-default, we enforce the (absense of the)
+		 * default-route only once. That allows the user to configure a connection as never-default,
+		 * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */
+		goto END_ADD_DEFAULT_ROUTE;
+	}
+
+	/* At this point, we treat assumed and non-assumed connections alike.
+	 * For assumed connections we do that because we still manage RA and DHCP
+	 * leases for them, so we must extend/update the default route on commits.
+	 */
+
+	/* we are about to commit (for a non-assumed connection). Enforce whatever we have
+	 * configured. */
+	priv->default_route.v4_configure_first_time = FALSE;
+	priv->default_route.v4_is_assumed = FALSE;
+
+	if (!connection_has_default_route)
+		goto END_ADD_DEFAULT_ROUTE;
+
+	if (!nm_ip4_config_get_num_addresses (composite)) {
+		/* without addresses we can have no default route. */
+		goto END_ADD_DEFAULT_ROUTE;
+	}
+
+	gateway = nm_ip4_config_get_gateway (composite);
+	if (   !gateway
+	    && nm_device_get_device_type (self) != NM_DEVICE_TYPE_MODEM)
+		goto END_ADD_DEFAULT_ROUTE;
+
+	has_direct_route = (   gateway == 0
+	                    || nm_ip4_config_get_subnet_for_host (composite, gateway)
+	                    || nm_ip4_config_get_direct_route_for_host (composite, gateway));
+
+	priv->default_route.v4_has = TRUE;
+	memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4));
+	priv->default_route.v4.source = NM_IP_CONFIG_SOURCE_USER;
+	priv->default_route.v4.gateway = gateway;
+	priv->default_route.v4.metric = default_route_metric;
+	priv->default_route.v4.mss = nm_ip4_config_get_mss (composite);
+
+	if (!has_direct_route) {
+		NMPlatformIP4Route r = priv->default_route.v4;
+
+		/* add a direct route to the gateway */
+		r.network = gateway;
+		r.plen = 32;
+		r.gateway = 0;
+		nm_ip4_config_add_route (composite, &r);
+	}
+
+END_ADD_DEFAULT_ROUTE:
+
+	if (priv->default_route.v4_is_assumed) {
+		/* If above does not explicitly assign a default route, we always pick up the
+		 * default route based on what is currently configured.
+		 * That means that even managed connections with never-default, can
+		 * get a default route (if configured externally).
 		 */
-		if (   !assumed
-		    && nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), connection)) {
-			guint32 gateway = 0;
-
-			priv->default_route.v4_is_assumed = FALSE;
-			if (   (!commit && priv->ext_ip4_config_had_any_addresses)
-			    || ( commit && nm_ip4_config_get_num_addresses (composite))) {
-				/* For managed interfaces, we can only configure a gateway, if either the external config indicates
-				 * that we already have addresses, or if we are about to commit any addresses.
-				 * Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any
-				 * addresses for the route. */
-				gateway = nm_ip4_config_get_gateway (composite);
-				if (   gateway
-				    || nm_device_get_device_type (self) == NM_DEVICE_TYPE_MODEM) {
-					memset (route, 0, sizeof (*route));
-					route->source = NM_IP_CONFIG_SOURCE_USER;
-					route->gateway = gateway;
-					route->metric = default_route_metric;
-					route->mss = nm_ip4_config_get_mss (composite);
-					priv->default_route.v4_has = TRUE;
-
-					if (   gateway
-					    && !nm_ip4_config_get_subnet_for_host (composite, gateway)
-					    && !nm_ip4_config_get_direct_route_for_host (composite, gateway)) {
-						/* add a direct route to the gateway */
-						NMPlatformIP4Route r = *route;
-
-						r.network = gateway;
-						r.plen = 32;
-						r.gateway = 0;
-						nm_ip4_config_add_route (composite, &r);
-					}
-				}
-			}
-		} else {
-			/* For interfaces that are assumed and that have no default-route by configuration, we assume
-			 * the default connection and pick up whatever is configured. */
-			priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route);
-		}
+		priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) &priv->default_route.v4);
 	}
 
 	/* Allow setting MTU etc */
@@ -3020,7 +3365,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 			NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite);
 	}
 
-	success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, out_reason);
+	success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync, out_reason);
 	g_object_unref (composite);
 	return success;
 }
@@ -3051,7 +3396,7 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	dhcp4_cleanup (self, TRUE, FALSE);
+	dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 	if (timeout || (priv->ip4_state == IP_CONF))
 		nm_device_activate_schedule_ip4_config_timeout (self);
 	else if (priv->ip4_state == IP_DONE)
@@ -3143,7 +3488,7 @@ dhcp4_start (NMDevice *self,
 		g_object_unref (priv->dhcp4_config);
 	priv->dhcp4_config = nm_dhcp4_config_new ();
 
-	hw_addr = nm_platform_link_get_address (nm_device_get_ip_ifindex (self), &hw_addr_len);
+	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len);
 	if (hw_addr_len) {
 		tmp = g_byte_array_sized_new (hw_addr_len);
 		g_byte_array_append (tmp, hw_addr, hw_addr_len);
@@ -3196,7 +3541,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 	_LOGI (LOGD_DHCP4, "DHCPv4 lease renewal requested");
 
 	/* Terminate old DHCP instance and release the old lease */
-	dhcp4_cleanup (self, TRUE, release);
+	dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release);
 
 	connection = nm_device_get_connection (self);
 	g_assert (connection);
@@ -3451,7 +3796,7 @@ act_stage3_ip4_config_start (NMDevice *self,
 /* DHCPv6 stuff */
 
 static void
-dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release)
+dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
@@ -3464,7 +3809,8 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release)
 			priv->dhcp6_state_sigid = 0;
 		}
 
-		if (stop)
+		if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
+		    || cleanup_type == CLEANUP_TYPE_REMOVED)
 			nm_dhcp_client_stop (priv->dhcp6_client, release);
 
 		g_clear_object (&priv->dhcp6_client);
@@ -3487,10 +3833,16 @@ ip6_config_merge_and_apply (NMDevice *self,
 	NMConnection *connection;
 	gboolean success;
 	NMIP6Config *composite;
+	gboolean has_direct_route;
+	const struct in6_addr *gateway;
+	gboolean connection_has_default_route, connection_is_never_default;
+	gboolean routes_full_sync;
 
 	/* If no config was passed in, create a new one */
 	composite = nm_ip6_config_new ();
-	g_assert (composite);
+
+	if (commit)
+		ensure_con_ip6_config (self);
 
 	/* Merge all the IP configs into the composite config */
 	if (priv->ac_ip6_config)
@@ -3508,72 +3860,119 @@ ip6_config_merge_and_apply (NMDevice *self,
 	if (priv->wwan_ip6_config)
 		nm_ip6_config_merge (composite, priv->wwan_ip6_config);
 
-	/* Merge user overrides into the composite config.  Generated+assumed
-	 * connections come from the system not the user and merging them would
-	 * be redundant, so don't bother.
-	 */
+	/* Apply ignore-auto-routes and ignore-auto-dns settings */
 	connection = nm_device_get_connection (self);
-	priv->default_route.v6_has = FALSE;
-	priv->default_route.v6_is_assumed = TRUE;
 	if (connection) {
-		gboolean assumed = nm_device_uses_assumed_connection (self);
-		NMPlatformIP6Route *route = &priv->default_route.v6;
+		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
 
-		if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) {
-			nm_ip6_config_merge_setting (composite,
-			                             nm_connection_get_setting_ip6_config (connection),
-			                             nm_device_get_ip6_route_metric (self));
+		if (s_ip6) {
+			if (nm_setting_ip_config_get_ignore_auto_routes (s_ip6))
+				nm_ip6_config_reset_routes (composite);
+			if (nm_setting_ip_config_get_ignore_auto_dns (s_ip6)) {
+				nm_ip6_config_reset_nameservers (composite);
+				nm_ip6_config_reset_domains (composite);
+				nm_ip6_config_reset_searches (composite);
+			}
 		}
+	}
 
-		/* Add the default route.
-		 *
-		 * We keep track of the default route of a device in a private field.
-		 * NMDevice needs to know the default route at this point, because the gateway
-		 * might require a direct route (see below).
-		 *
-		 * But also, we don't want to add the default route to priv->ip4_config,
-		 * because the default route from the setting might not be the same that
-		 * NMDefaultRouteManager eventually configures (because the it might
-		 * tweak the effective metric).
+	/* Merge user overrides into the composite config. For assumed connections,
+	 * con_ip6_config is empty. */
+	if (priv->con_ip6_config)
+		nm_ip6_config_merge (composite, priv->con_ip6_config);
+
+	/* Add the default route.
+	 *
+	 * We keep track of the default route of a device in a private field.
+	 * NMDevice needs to know the default route at this point, because the gateway
+	 * might require a direct route (see below).
+	 *
+	 * But also, we don't want to add the default route to priv->ip6_config,
+	 * because the default route from the setting might not be the same that
+	 * NMDefaultRouteManager eventually configures (because the it might
+	 * tweak the effective metric).
+	 */
+
+	/* unless we come to a different conclusion below, we have no default route and
+	 * the route is assumed. */
+	priv->default_route.v6_has = FALSE;
+	priv->default_route.v6_is_assumed = TRUE;
+
+	routes_full_sync =    commit
+	                   && priv->default_route.v6_configure_first_time
+	                   && !nm_device_uses_assumed_connection (self);
+
+	if (!commit) {
+		/* during a non-commit event, we always pickup whatever is configured. */
+		goto END_ADD_DEFAULT_ROUTE;
+	}
+
+	connection_has_default_route
+	    = nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (),
+	                                                                 connection, &connection_is_never_default);
+
+	if (   !priv->default_route.v6_configure_first_time
+	    && !nm_device_uses_assumed_connection (self)
+	    && connection_is_never_default) {
+		/* If the connection is explicitly configured as never-default, we enforce the (absence of the)
+		 * default-route only once. That allows the user to configure a connection as never-default,
+		 * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */
+		goto END_ADD_DEFAULT_ROUTE;
+	}
+
+	/* At this point, we treat assumed and non-assumed connections alike.
+	 * For assumed connections we do that because we still manage RA and DHCP
+	 * leases for them, so we must extend/update the default route on commits.
+	 */
+
+	/* we are about to commit (for a non-assumed connection). Enforce whatever we have
+	 * configured. */
+	priv->default_route.v6_configure_first_time = FALSE;
+	priv->default_route.v6_is_assumed = FALSE;
+
+	if (!connection_has_default_route)
+		goto END_ADD_DEFAULT_ROUTE;
+
+	if (!nm_ip6_config_get_num_addresses (composite)) {
+		/* without addresses we can have no default route. */
+		goto END_ADD_DEFAULT_ROUTE;
+	}
+
+	gateway = nm_ip6_config_get_gateway (composite);
+	if (!gateway)
+		goto END_ADD_DEFAULT_ROUTE;
+
+
+	has_direct_route = nm_ip6_config_get_direct_route_for_host (composite, gateway) != NULL;
+
+
+
+	priv->default_route.v6_has = TRUE;
+	memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6));
+	priv->default_route.v6.source = NM_IP_CONFIG_SOURCE_USER;
+	priv->default_route.v6.gateway = *gateway;
+	priv->default_route.v6.metric = nm_device_get_ip6_route_metric (self);
+	priv->default_route.v6.mss = nm_ip6_config_get_mss (composite);
+
+	if (!has_direct_route) {
+		NMPlatformIP6Route r = priv->default_route.v6;
+
+		/* add a direct route to the gateway */
+		r.network = *gateway;
+		r.plen = 128;
+		r.gateway = in6addr_any;
+		nm_ip6_config_add_route (composite, &r);
+	}
+
+END_ADD_DEFAULT_ROUTE:
+
+	if (priv->default_route.v6_is_assumed) {
+		/* If above does not explicitly assign a default route, we always pick up the
+		 * default route based on what is currently configured.
+		 * That means that even managed connections with never-default, can
+		 * get a default route (if configured externally).
 		 */
-		if (   !assumed
-		    && nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), connection)) {
-			const struct in6_addr *gateway = NULL;
-
-			priv->default_route.v6_is_assumed = FALSE;
-			if (   (!commit && priv->ext_ip6_config_had_any_addresses)
-			    || ( commit && nm_ip6_config_get_num_addresses (composite))) {
-				/* For managed interfaces, we can only configure a gateway, if either the external config indicates
-				 * that we already have addresses, or if we are about to commit any addresses.
-				 * Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any
-				 * addresses for the route. */
-				gateway = nm_ip6_config_get_gateway (composite);
-				if (gateway) {
-					memset (route, 0, sizeof (*route));
-					route->source = NM_IP_CONFIG_SOURCE_USER;
-					route->gateway = *gateway;
-					route->metric = nm_device_get_ip6_route_metric (self);
-					route->mss = nm_ip6_config_get_mss (composite);
-					priv->default_route.v6_has = TRUE;
-
-					if (   gateway
-					    && !nm_ip6_config_get_subnet_for_host (composite, gateway)
-					    && !nm_ip6_config_get_direct_route_for_host (composite, gateway)) {
-						/* add a direct route to the gateway */
-						NMPlatformIP6Route r = *route;
-
-						r.network = *gateway;
-						r.plen = 128;
-						r.gateway = in6addr_any;
-						nm_ip6_config_add_route (composite, &r);
-					}
-				}
-			}
-		} else {
-			/* For interfaces that are assumed and that have no default-route by configuration, we assume
-			 * the default connection and pick up whatever is configured. */
-			priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) route);
-		}
+		priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) &priv->default_route.v6);
 	}
 
 	nm_ip6_config_addresses_sort (composite,
@@ -3585,7 +3984,7 @@ ip6_config_merge_and_apply (NMDevice *self,
 			NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, composite);
 	}
 
-	success = nm_device_set_ip6_config (self, composite, commit, out_reason);
+	success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync, out_reason);
 	g_object_unref (composite);
 	return success;
 }
@@ -3623,7 +4022,7 @@ dhcp6_fail (NMDevice *self, gboolean timeout)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	dhcp6_cleanup (self, TRUE, FALSE);
+	dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 
 	if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) {
 		if (timeout || (priv->ip6_state == IP_CONF))
@@ -3648,7 +4047,7 @@ dhcp6_timeout (NMDevice *self, NMDhcpClient *client)
 		dhcp6_fail (self, TRUE);
 	else {
 		/* not a hard failure; just live with the RA info */
-		dhcp6_cleanup (self, TRUE, FALSE);
+		dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 		if (priv->ip6_state == IP_CONF)
 			nm_device_activate_schedule_ip6_config_result (self);
 	}
@@ -3740,7 +4139,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
 
-	hw_addr = nm_platform_link_get_address (nm_device_get_ip_ifindex (self), &hw_addr_len);
+	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len);
 	if (hw_addr_len) {
 		tmp = g_byte_array_sized_new (hw_addr_len);
 		g_byte_array_append (tmp, hw_addr, hw_addr_len);
@@ -3823,7 +4222,7 @@ nm_device_dhcp6_renew (NMDevice *self, gboolean release)
 	_LOGI (LOGD_DHCP6, "DHCPv6 lease renewal requested");
 
 	/* Terminate old DHCP instance and release the old lease */
-	dhcp6_cleanup (self, TRUE, release);
+	dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release);
 
 	/* Start DHCP again on the interface */
 	return dhcp6_start (self, FALSE, NULL);
@@ -3945,7 +4344,8 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 	lladdr.s6_addr16[0] = htons (0xfe80);
 	nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid);
 	_LOGD (LOGD_IP6, "adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL));
-	if (!nm_platform_ip6_address_add (ip_ifindex,
+	if (!nm_platform_ip6_address_add (NM_PLATFORM_GET,
+	                                  ip_ifindex,
 	                                  lladdr,
 	                                  in6addr_any,
 	                                  64,
@@ -3995,7 +4395,7 @@ print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr)
 
 	if (s_libnl == -1) {
 		s_libnl = !!nm_platform_check_support_libnl_extended_ifa_flags ();
-		s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags ();
+		s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
 
 		if (s_libnl && s_kernel) {
 			nm_log_dbg (LOGD_IP6, "kernel and libnl support extended IFA_FLAGS (needed by NM for IPv6 private addresses)");
@@ -4031,6 +4431,58 @@ print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr)
 	warn = 2;
 }
 
+static void nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu);
+
+static void
+nm_device_set_mtu (NMDevice *self, guint32 mtu)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ifindex = nm_device_get_ifindex (self);
+
+	if (mtu)
+		priv->mtu = mtu;
+
+	/* Ensure the IPv6 MTU is still alright. */
+	if (priv->ip6_mtu)
+		nm_device_ipv6_set_mtu (self, priv->ip6_mtu);
+
+	if (priv->mtu != nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex))
+		nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, priv->mtu);
+}
+
+static void
+nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	guint32 plat_mtu = nm_device_ipv6_sysctl_get_int32 (self, "mtu", priv->mtu);
+	char val[16];
+
+	priv->ip6_mtu = mtu ?: plat_mtu;
+
+	if (priv->ip6_mtu && priv->mtu < priv->ip6_mtu) {
+		_LOGW (LOGD_DEVICE | LOGD_IP6, "Lowering IPv6 MTU (%d) to match device MTU (%d)",
+		       priv->ip6_mtu, priv->mtu);
+		priv->ip6_mtu = priv->mtu;
+	}
+
+	if (priv->ip6_mtu < 1280) {
+		_LOGW (LOGD_DEVICE | LOGD_IP6, "IPv6 MTU (%d) smaller than 1280, adjusting",
+		       priv->ip6_mtu);
+		priv->ip6_mtu = 1280;
+	}
+
+	if (priv->mtu < priv->ip6_mtu) {
+		_LOGW (LOGD_DEVICE | LOGD_IP6, "Raising device MTU (%d) to match IPv6 MTU (%d)",
+		       priv->mtu, priv->ip6_mtu);
+		nm_device_set_mtu (self, priv->ip6_mtu);
+	}
+
+	if (priv->ip6_mtu != plat_mtu) {
+		g_snprintf (val, sizeof (val), "%d", mtu);
+		nm_device_ipv6_sysctl_set (self, "mtu", val);
+	}
+}
+
 static void
 rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 {
@@ -4048,7 +4500,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 		 * from adding a prefix route for this address.
 		 **/
 		system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
-		                 nm_platform_check_support_kernel_extended_ifa_flags ();
+		                 nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
 	}
 
 	if (system_support)
@@ -4151,13 +4603,13 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 	}
 
 	if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
-		dhcp6_cleanup (self, TRUE, TRUE);
+		dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, TRUE);
 
 		priv->dhcp6_mode = rdisc->dhcp_level;
 		if (priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE) {
 			NMDeviceStateReason reason;
 
-			_LOGI (LOGD_DEVICE | LOGD_DHCP6,
+			_LOGD (LOGD_DEVICE | LOGD_DHCP6,
 			       "Activation: Stage 3 of 5 (IP Configure Start) starting DHCPv6"
 			       " as requested by IPv6 router...");
 			if (!dhcp6_start (self, FALSE, &reason)) {
@@ -4169,14 +4621,10 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 	}
 
 	if (changed & NM_RDISC_CONFIG_HOP_LIMIT)
-		nm_platform_sysctl_set_ip6_hop_limit_safe (nm_device_get_ip_iface (self), rdisc->hop_limit);
-
-	if (changed & NM_RDISC_CONFIG_MTU) {
-		char val[16];
+		nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, nm_device_get_ip_iface (self), rdisc->hop_limit);
 
-		g_snprintf (val, sizeof (val), "%d", rdisc->mtu);
-		nm_device_ipv6_sysctl_set (self, "mtu", val);
-	}
+	if (changed & NM_RDISC_CONFIG_MTU)
+		priv->ip6_mtu = rdisc->mtu;
 
 	nm_device_activate_schedule_ip6_config_result (self);
 }
@@ -4213,11 +4661,12 @@ addrconf6_start_with_link_ready (NMDevice *self)
 
 	g_assert (priv->rdisc);
 
-	if (!nm_device_get_ip_iface_identifier (self, &iid)) {
+	if (nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &iid)) {
+		_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
+	} else if (!nm_device_get_ip_iface_identifier (self, &iid)) {
 		_LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue");
 		return FALSE;
 	}
-	nm_rdisc_set_iid (priv->rdisc, iid);
 
 	/* Apply any manual configuration before starting RA */
 	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
@@ -4236,6 +4685,8 @@ addrconf6_start_with_link_ready (NMDevice *self)
 	                                           NM_RDISC_RA_TIMEOUT,
 	                                           G_CALLBACK (rdisc_ra_timeout),
 	                                           self);
+
+	nm_rdisc_set_iid (priv->rdisc, iid);
 	nm_rdisc_start (priv->rdisc);
 	return TRUE;
 }
@@ -4325,7 +4776,7 @@ save_ip6_properties (NMDevice *self)
 	g_hash_table_remove_all (priv->ip6_saved_properties);
 
 	for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) {
-		value = nm_platform_sysctl_get (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i]));
+		value = nm_platform_sysctl_get (NM_PLATFORM_GET, nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i]));
 		if (value) {
 			g_hash_table_insert (priv->ip6_saved_properties,
 			                     (char *) ip6_properties_to_save[i],
@@ -4363,10 +4814,9 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int ifindex = nm_device_get_ip_ifindex (self);
-	const char *iface = nm_device_get_ip_iface (self);
 	char *value;
 
-	if (!nm_platform_check_support_user_ipv6ll ())
+	if (!nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET))
 		return;
 
 	priv->nm_ipv6ll = enable;
@@ -4374,13 +4824,13 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 		const char *detail = enable ? "enable" : "disable";
 
 		_LOGD (LOGD_IP6, "will %s userland IPv6LL", detail);
-		if (  !nm_platform_link_set_user_ipv6ll_enabled (ifindex, enable)
-		   && nm_platform_get_error () != NM_PLATFORM_ERROR_NOT_FOUND)
+		if (!nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable))
 			_LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail);
 
 		if (enable) {
 			/* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */
-			value = nm_platform_sysctl_get (nm_utils_ip6_property_path (iface, "disable_ipv6"));
+			value = nm_platform_sysctl_get (NM_PLATFORM_GET, 
+			                                nm_utils_ip6_property_path (nm_device_get_ip_iface (self), "disable_ipv6"));
 			if (g_strcmp0 (value, "0") == 0)
 				nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
 			g_free (value);
@@ -4392,8 +4842,10 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 	}
 }
 
+/************************************************************************/
+
 static NMSettingIP6ConfigPrivacy
-use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
+_ip6_privacy_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
 {
 	switch (use_tempaddr) {
 	case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
@@ -4405,45 +4857,51 @@ use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
 	}
 }
 
-/* Get net.ipv6.conf.default.use_tempaddr value from /etc/sysctl.conf or
- * /lib/sysctl.d/sysctl.conf
- */
 static NMSettingIP6ConfigPrivacy
-ip6_use_tempaddr (void)
+_ip6_privacy_get (NMDevice *self)
 {
-	char *contents = NULL;
-	const char *group_name = "[forged_group]\n";
-	char *sysctl_data = NULL;
-	GKeyFile *keyfile;
-	GError *error = NULL;
-	gint tmp;
-	NMSettingIP6ConfigPrivacy ret = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+	NMSettingIP6ConfigPrivacy ip6_privacy;
+	gs_free char *value = NULL;
+	NMConnection *connection;
 
-	/* Read file contents to a string. */
-	if (!g_file_get_contents ("/etc/sysctl.conf", &contents, NULL, NULL))
-		if (!g_file_get_contents ("/lib/sysctl.d/sysctl.conf", &contents, NULL, NULL))
-			return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+	g_return_val_if_fail (self, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
 
-	/* Prepend a group so that we can use GKeyFile parser. */
-	sysctl_data = g_strdup_printf ("%s%s", group_name, contents);
+	/* 1.) First look at the per-connection setting. If it is not -1 (unknown),
+	 * use it. */
+	connection = nm_device_get_connection (self);
+	if (connection) {
+		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
 
-	keyfile = g_key_file_new ();
-	if (!g_key_file_load_from_data (keyfile, sysctl_data, -1, G_KEY_FILE_NONE, NULL))
-		goto done;
+		if (s_ip6) {
+			ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6));
+			ip6_privacy = _ip6_privacy_clamp (ip6_privacy);
+			if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
+				return ip6_privacy;
+		}
+	}
 
-	tmp = g_key_file_get_integer (keyfile, "forged_group", "net.ipv6.conf.default.use_tempaddr", &error);
-	if (error == NULL)
-		ret = use_tempaddr_clamp (tmp);
+	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+	                                               "ipv6.ip6-privacy", self);
 
-done:
-	g_free (contents);
-	g_free (sysctl_data);
-	g_clear_error (&error);
-	g_key_file_free (keyfile);
+	/* 2.) use the default value from the configuration. */
+	ip6_privacy = _nm_utils_ascii_str_to_int64 (value, 10,
+	                                            NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
+	                                            NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
+	                                            NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+	if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
+		return ip6_privacy;
 
-	return ret;
+	/* 3.) No valid default-value configured. Fallback to reading sysctl.
+	 *
+	 * Instead of reading static config files in /etc, just read the current sysctl value.
+	 * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves
+	 * the "default" entry untouched. */
+	ip6_privacy = nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, "/proc/sys/net/ipv6/conf/default/use_tempaddr", NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+	return _ip6_privacy_clamp (ip6_privacy);
 }
 
+/****************************************************************/
+
 static gboolean
 ip6_requires_slaves (NMConnection *connection)
 {
@@ -4525,6 +4983,13 @@ act_stage3_ip6_config_start (NMDevice *self,
 		return NM_ACT_STAGE_RETURN_STOP;
 	}
 
+	/* Ensure the MTU makes sense. If it was below 1280 the kernel would not
+	 * expose any ipv6 sysctls or allow presence of any addresses on the interface,
+	 * including LL, which * would make it impossible to autoconfigure MTU to a
+	 * correct value. */
+	if (!nm_device_uses_assumed_connection (self))
+		nm_device_ipv6_set_mtu (self, priv->ip6_mtu);
+
 	/* Any method past this point requires an IPv6LL address. Use NM-controlled
 	 * IPv6LL if this is not an assumed connection, since assumed connections
 	 * will already have IPv6 set up.
@@ -4535,18 +5000,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 	/* Re-enable IPv6 on the interface */
 	set_disable_ipv6 (self, "0");
 
-	/* Enable/disable IPv6 Privacy Extensions.
-	 * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf),
-	 * use that value instead of per-connection value.
-	 */
-	ip6_privacy = ip6_use_tempaddr ();
-	if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) {
-		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
-
-		if (s_ip6)
-			ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6));
-	}
-	ip6_privacy = use_tempaddr_clamp (ip6_privacy);
+	ip6_privacy = _ip6_privacy_get (self);
 
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
 		if (!addrconf6_start (self, ip6_privacy)) {
@@ -4734,11 +5188,11 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
 
 	priv->ip4_state = priv->ip6_state = IP_WAIT;
 
-	_LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) started...");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) started...");
 	nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Device should be up before we can do anything with it */
-	if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self)))
+	if (!nm_platform_link_is_up (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self)))
 		_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 
 	/* If the device is a slave, then we don't do any IP configuration but we
@@ -4772,7 +5226,7 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
 	nm_device_check_ip_failed (self, TRUE);
 
 out:
-	_LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) complete.");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) complete.");
 	return FALSE;
 }
 
@@ -4796,7 +5250,7 @@ fw_change_zone_cb (GError *error, gpointer user_data)
 	}
 
 	activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0);
-	_LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled.");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled.");
 }
 
 /*
@@ -4829,7 +5283,7 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
 	if (nm_device_uses_assumed_connection (self)) {
 		_LOGD (LOGD_DEVICE, "Activation: skip setting firewall zone '%s' for assumed device", zone ? zone : "default");
 		activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0);
-		_LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled.");
+		_LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled.");
 		return;
 	}
 
@@ -4870,8 +5324,7 @@ nm_device_activate_ip4_config_timeout (gpointer user_data)
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, AF_INET);
 
-	_LOGI (LOGD_DEVICE | LOGD_IP4,
-	       "Activation: Stage 4 of 5 (IPv4 Configure Timeout) started...");
+	_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) started...");
 
 	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &reason);
 	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
@@ -4887,8 +5340,7 @@ nm_device_activate_ip4_config_timeout (gpointer user_data)
 	nm_device_check_ip_failed (self, FALSE);
 
 out:
-	_LOGI (LOGD_DEVICE | LOGD_IP4,
-	       "Activation: Stage 4 of 5 (IPv4 Configure Timeout) complete.");
+	_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) complete.");
 	return FALSE;
 }
 
@@ -4911,8 +5363,7 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_ip4_config_timeout, AF_INET);
 
-	_LOGI (LOGD_DEVICE | LOGD_IP4,
-	       "Activation: Stage 4 of 5 (IPv4 Configure Timeout) scheduled...");
+	_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) scheduled...");
 }
 
 
@@ -4945,8 +5396,7 @@ nm_device_activate_ip6_config_timeout (gpointer user_data)
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, AF_INET6);
 
-	_LOGI (LOGD_DEVICE | LOGD_IP6,
-	       "Activation: Stage 4 of 5 (IPv6 Configure Timeout) started...");
+	_LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) started...");
 
 	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &reason);
 	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
@@ -4962,8 +5412,7 @@ nm_device_activate_ip6_config_timeout (gpointer user_data)
 	nm_device_check_ip_failed (self, FALSE);
 
 out:
-	_LOGI (LOGD_DEVICE | LOGD_IP6,
-	       "Activation: Stage 4 of 5 (IPv6 Configure Timeout) complete.");
+	_LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) complete.");
 	return FALSE;
 }
 
@@ -4986,57 +5435,33 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_ip6_config_timeout, AF_INET6);
 
-	_LOGI (LOGD_DEVICE | LOGD_IP6,
-	       "Activation: Stage 4 of 5 (IPv6 Configure Timeout) scheduled...");
-}
-
-static void
-share_child_setup (gpointer user_data G_GNUC_UNUSED)
-{
-	/* We are in the child process at this point */
-	pid_t pid = getpid ();
-	setpgid (pid, pid);
-
-	nm_unblock_posix_signals (NULL);
+	_LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) scheduled...");
 }
 
 static gboolean
 share_init (void)
 {
-	int status;
 	char *modules[] = { "ip_tables", "iptable_nat", "nf_nat_ftp", "nf_nat_irc",
 	                    "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323",
 	                    NULL };
 	char **iter;
 	int errsv;
 
-	if (!nm_platform_sysctl_set ("/proc/sys/net/ipv4/ip_forward", "1")) {
+	if (!nm_platform_sysctl_set (NM_PLATFORM_GET, "/proc/sys/net/ipv4/ip_forward", "1")) {
 		errsv = errno;
 		nm_log_err (LOGD_SHARING, "share: error starting IP forwarding: (%d) %s",
 		            errsv, strerror (errsv));
 		return FALSE;
 	}
 
-	if (!nm_platform_sysctl_set ("/proc/sys/net/ipv4/ip_dynaddr", "1")) {
+	if (!nm_platform_sysctl_set (NM_PLATFORM_GET, "/proc/sys/net/ipv4/ip_dynaddr", "1")) {
 		errsv = errno;
 		nm_log_err (LOGD_SHARING, "share: error starting IP forwarding: (%d) %s",
 		            errsv, strerror (errsv));
 	}
 
-	for (iter = modules; *iter; iter++) {
-		char *argv[3] = { "/sbin/modprobe", *iter, NULL };
-		char *envp[1] = { NULL };
-		GError *error = NULL;
-
-		if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
-		                   share_child_setup, NULL, NULL, NULL, &status, &error)) {
-			nm_log_err (LOGD_SHARING, "share: error loading NAT module %s: (%d) %s",
-			            *iter, error ? error->code : 0,
-			            (error && error->message) ? error->message : "unknown");
-			if (error)
-				g_error_free (error);
-		}
-	}
+	for (iter = modules; *iter; iter++)
+		nm_utils_modprobe (NULL, FALSE, *iter, NULL);
 
 	return TRUE;
 }
@@ -5154,8 +5579,7 @@ send_arps (NMDevice *self, const char *mode_arg)
 		       "arping: run %s", (tmp_str = g_strjoinv (" ", (char **) argv)));
 		success = g_spawn_async (NULL, (char **) argv, NULL,
 		                         G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
-		                         nm_unblock_posix_signals,
-		                         NULL, NULL, &error);
+		                         NULL, NULL, NULL, &error);
 		if (!success) {
 			_LOGW (LOGD_DEVICE | LOGD_IP4,
 			       "arping: could not send ARP for local address %s: %s",
@@ -5231,7 +5655,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, AF_INET);
 
-	_LOGI (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) started...");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) started...");
 
 	req = nm_device_get_act_request (self);
 	g_assert (req);
@@ -5240,16 +5664,15 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 
 	/* Interface must be IFF_UP before IP config can be applied */
 	ip_ifindex = nm_device_get_ip_ifindex (self);
-	if (!nm_platform_link_is_up (ip_ifindex) && !nm_device_uses_assumed_connection (self)) {
-		nm_platform_link_set_up (ip_ifindex);
-		if (!nm_platform_link_is_up (ip_ifindex))
+	if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) {
+		nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex, NULL);
+		if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex))
 			_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 	}
 
 	/* NULL to use the existing priv->dev_ip4_config */
 	if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) {
-		_LOGI (LOGD_DEVICE | LOGD_IP4,
-		       "Activation: Stage 5 of 5 (IPv4 Commit) failed");
+		_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed");
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 		goto out;
 	}
@@ -5291,11 +5714,28 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 		nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
 
 out:
-	_LOGI (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) complete.");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) complete.");
 
 	return FALSE;
 }
 
+static void
+nm_device_queued_ip_config_change_clear (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->queued_ip4_config_id) {
+		_LOGD (LOGD_DEVICE, "clearing queued IP4 config change");
+		g_source_remove (priv->queued_ip4_config_id);
+		priv->queued_ip4_config_id = 0;
+	}
+	if (priv->queued_ip6_config_id) {
+		_LOGD (LOGD_DEVICE, "clearing queued IP6 config change");
+		g_source_remove (priv->queued_ip6_config_id);
+		priv->queued_ip6_config_id = 0;
+	}
+}
+
 void
 nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *config)
 {
@@ -5308,10 +5748,10 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf
 	if (config)
 		priv->dev_ip4_config = g_object_ref (config);
 
+	nm_device_queued_ip_config_change_clear (self);
 	activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET);
 
-	_LOGI (LOGD_DEVICE | LOGD_IP4,
-	       "Activation: Stage 5 of 5 (IPv4 Configure Commit) scheduled...");
+	_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Configure Commit) scheduled...");
 }
 
 gboolean
@@ -5333,7 +5773,6 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	guint level = (priv->ip6_state == IP_DONE) ? LOGL_DEBUG : LOGL_INFO;
 	NMActRequest *req;
 	NMConnection *connection;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
@@ -5342,7 +5781,7 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, AF_INET6);
 
-	_LOG (level, LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) started...");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) started...");
 
 	req = nm_device_get_act_request (self);
 	g_assert (req);
@@ -5351,9 +5790,9 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
 
 	/* Interface must be IFF_UP before IP config can be applied */
 	ip_ifindex = nm_device_get_ip_ifindex (self);
-	if (!nm_platform_link_is_up (ip_ifindex) && !nm_device_uses_assumed_connection (self)) {
-		nm_platform_link_set_up (ip_ifindex);
-		if (!nm_platform_link_is_up (ip_ifindex))
+	if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) {
+		nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex, NULL);
+		if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex))
 			_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 	}
 
@@ -5382,12 +5821,11 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
 		if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
 			nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
 	} else {
-		_LOGW (LOGD_DEVICE | LOGD_IP6,
-		       "Activation: Stage 5 of 5 (IPv6 Commit) failed");
+		_LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed");
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 	}
 
-	_LOG (level, LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) complete.");
+	_LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) complete.");
 
 	return FALSE;
 }
@@ -5396,7 +5834,6 @@ void
 nm_device_activate_schedule_ip6_config_result (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	guint level = (priv->ip6_state == IP_DONE) ? LOGL_DEBUG : LOGL_INFO;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
@@ -5408,8 +5845,7 @@ nm_device_activate_schedule_ip6_config_result (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6);
 
-	_LOG (level, LOGD_DEVICE | LOGD_IP6,
-	      "Activation: Stage 5 of 5 (IPv6 Commit) scheduled...");
+	_LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) scheduled...");
 }
 
 gboolean
@@ -5525,7 +5961,7 @@ delete_on_deactivate_link_delete (gpointer user_data)
 
 	_LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)",
 	       data->ifindex, data->idle_add_id);
-	nm_platform_link_delete (data->ifindex);
+	nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex);
 	g_free (data);
 	return FALSE;
 }
@@ -5659,7 +6095,7 @@ delete_cb (NMDevice *self,
 	}
 
 	/* Authorized */
-	nm_platform_link_delete (nm_device_get_ifindex (self));
+	nm_platform_link_delete (NM_PLATFORM_GET, nm_device_get_ifindex (self));
 	dbus_g_method_return (context);
 }
 
@@ -5687,22 +6123,29 @@ impl_device_delete (NMDevice *self, DBusGMethodInvocation *context)
 	               NULL);
 }
 
-static void
+static gboolean
 _device_activate (NMDevice *self, NMActRequest *req)
 {
 	NMDevicePrivate *priv;
 	NMConnection *connection;
 
-	g_return_if_fail (NM_IS_DEVICE (self));
-	g_return_if_fail (NM_IS_ACT_REQUEST (req));
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	/* Ensure the activation request is still valid; the master may have
+	 * already failed in which case activation of this device should not proceed.
+	 */
+	if (nm_active_connection_get_state (NM_ACTIVE_CONNECTION (req)) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)
+		return FALSE;
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	connection = nm_act_request_get_connection (req);
 	g_assert (connection);
 
-	_LOGI (LOGD_DEVICE, "Activation: starting connection '%s'",
-	       nm_connection_get_id (connection));
+	_LOGI (LOGD_DEVICE, "Activation: starting connection '%s' (%s)",
+	       nm_connection_get_id (connection),
+	       nm_connection_get_uuid (connection));
 
 	delete_on_deactivate_unschedule (self);
 
@@ -5719,6 +6162,7 @@ _device_activate (NMDevice *self, NMActRequest *req)
 	priv->act_request = g_object_ref (req);
 
 	nm_device_activate_schedule_stage1_device_prepare (self);
+	return TRUE;
 }
 
 static void
@@ -5785,6 +6229,26 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req)
 }
 
 void
+nm_device_steal_connection (NMDevice *self, NMConnection *connection)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	_LOGW (LOGD_DEVICE, "disconnecting connection '%s' for new activation request.",
+	       nm_connection_get_id (connection));
+
+	if (   priv->queued_act_request
+	    && connection == nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request)))
+		_clear_queued_act_request (priv);
+
+	if (   priv->act_request
+	    && connection == nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request))
+	    && priv->state < NM_DEVICE_STATE_DEACTIVATING)
+		nm_device_state_changed (self,
+		                         NM_DEVICE_STATE_DEACTIVATING,
+		                         NM_DEVICE_STATE_REASON_NEW_ACTIVATION);
+}
+
+void
 nm_device_queue_activation (NMDevice *self, NMActRequest *req)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -5794,7 +6258,8 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req)
 
 	if (!priv->act_request && !must_queue) {
 		/* Just activate immediately */
-		_device_activate (self, req);
+		if (!_device_activate (self, req))
+			g_assert_not_reached ();
 		return;
 	}
 
@@ -5805,8 +6270,8 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req)
 
 	_LOGD (LOGD_DEVICE, "queue activation request waiting for %s", must_queue ? "carrier" : "currently active connection to disconnect");
 
+	/* Deactivate existing activation request first */
 	if (priv->act_request) {
-		/* Deactivate existing activation request first */
 		_LOGI (LOGD_DEVICE, "disconnecting for new activation request.");
 		nm_device_state_changed (self,
 		                         NM_DEVICE_STATE_DEACTIVATING,
@@ -5864,6 +6329,7 @@ nm_device_set_ip4_config (NMDevice *self,
                           NMIP4Config *new_config,
                           guint32 default_route_metric,
                           gboolean commit,
+                          gboolean routes_full_sync,
                           NMDeviceStateReason *reason)
 {
 	NMDevicePrivate *priv;
@@ -5886,10 +6352,14 @@ nm_device_set_ip4_config (NMDevice *self,
 	if (commit && new_config) {
 		gboolean assumed = nm_device_uses_assumed_connection (self);
 
-		/* for assumed devices we set the device_route_metric to the default which will
-		 * stop nm_platform_ip4_address_sync() to replace the device routes. */
+		nm_device_set_mtu (self, nm_ip4_config_get_mtu (new_config));
+
+		/* For assumed devices we must not touch the kernel-routes, such as the device-route.
+		 * FIXME: this is wrong in case where "assumed" means "take-over-seamlessly". In this
+		 * case, we should manage the device route, for example on new DHCP lease. */
 		success = nm_ip4_config_commit (new_config, ip_ifindex,
-		                                assumed ? NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE : default_route_metric);
+		                                routes_full_sync,
+		                                assumed ? (gint64) -1 : (gint64) default_route_metric);
 		if (!success)
 			reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
 	}
@@ -5994,6 +6464,7 @@ static gboolean
 nm_device_set_ip6_config (NMDevice *self,
                           NMIP6Config *new_config,
                           gboolean commit,
+                          gboolean routes_full_sync,
                           NMDeviceStateReason *reason)
 {
 	NMDevicePrivate *priv;
@@ -6014,7 +6485,10 @@ nm_device_set_ip6_config (NMDevice *self,
 
 	/* Always commit to nm-platform to update lifetimes */
 	if (commit && new_config) {
-		success = nm_ip6_config_commit (new_config, ip_ifindex);
+		nm_device_ipv6_set_mtu (self, priv->ip6_mtu);
+		success = nm_ip6_config_commit (new_config,
+		                                ip_ifindex,
+		                                routes_full_sync);
 		if (!success)
 			reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
 	}
@@ -6187,19 +6661,67 @@ ip_check_gw_ping_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->gw_ping.watch) {
-		g_source_remove (priv->gw_ping.watch);
-		priv->gw_ping.watch = 0;
-	}
-	if (priv->gw_ping.timeout) {
-		g_source_remove (priv->gw_ping.timeout);
-		priv->gw_ping.timeout = 0;
-	}
+	nm_clear_g_source (&priv->gw_ping.watch);
+	nm_clear_g_source (&priv->gw_ping.timeout);
 
 	if (priv->gw_ping.pid) {
 		nm_utils_kill_child_async (priv->gw_ping.pid, SIGTERM, priv->gw_ping.log_domain, "ping", 1000, NULL, NULL);
 		priv->gw_ping.pid = 0;
 	}
+
+	g_clear_pointer (&priv->gw_ping.binary, g_free);
+	g_clear_pointer (&priv->gw_ping.address, g_free);
+}
+
+static gboolean
+spawn_ping (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gs_free char *str_timeout = NULL;
+	gs_free char *tmp_str = NULL;
+	const char *args[] = { priv->gw_ping.binary, "-I", nm_device_get_ip_iface (self),
+	                       "-c", "1", "-w", NULL, priv->gw_ping.address, NULL };
+	gs_free_error GError *error = NULL;
+	gboolean ret;
+
+	args[6] = str_timeout = g_strdup_printf ("%u", priv->gw_ping.deadline);
+	tmp_str = g_strjoinv (" ", (gchar **) args);
+	_LOGD (priv->gw_ping.log_domain, "ping: running '%s'", tmp_str);
+
+	ret = g_spawn_async ("/",
+	                     (gchar **) args,
+	                      NULL,
+	                      G_SPAWN_DO_NOT_REAP_CHILD,
+	                      NULL,
+	                      NULL,
+	                      &priv->gw_ping.pid,
+	                      &error);
+
+	if (!ret) {
+		_LOGW (priv->gw_ping.log_domain, "ping: could not spawn %s: %s",
+		       priv->gw_ping.binary, error->message);
+	}
+
+	return ret;
+}
+
+static gboolean
+respawn_ping_cb (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	priv->gw_ping.watch = 0;
+
+	if (spawn_ping (self)) {
+		priv->gw_ping.watch = g_child_watch_add (priv->gw_ping.pid,
+		                                         ip_check_ping_watch_cb, self);
+	} else {
+		ip_check_gw_ping_cleanup (self);
+		ip_check_pre_up (self);
+	}
+
+	return FALSE;
 }
 
 static void
@@ -6208,6 +6730,7 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data)
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	guint log_domain = priv->gw_ping.log_domain;
+	gboolean success = FALSE;
 
 	if (!priv->gw_ping.watch)
 		return;
@@ -6215,18 +6738,25 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data)
 	priv->gw_ping.pid = 0;
 
 	if (WIFEXITED (status)) {
-		if (WEXITSTATUS (status) == 0)
+		if (WEXITSTATUS (status) == 0) {
 			_LOGD (log_domain, "ping: gateway ping succeeded");
-		else {
+			success = TRUE;
+		} else {
 			_LOGW (log_domain, "ping: gateway ping failed with error code %d",
 			       WEXITSTATUS (status));
 		}
 	} else
 		_LOGW (log_domain, "ping: stopped unexpectedly with status %d", status);
 
-	/* We've got connectivity, proceed to pre_up */
-	ip_check_gw_ping_cleanup (self);
-	ip_check_pre_up (self);
+	if (success) {
+		/* We've got connectivity, proceed to pre_up */
+		ip_check_gw_ping_cleanup (self);
+		ip_check_pre_up (self);
+	} else {
+		/* If ping exited with an error it may have returned early,
+		 * wait 1 second and restart it */
+		priv->gw_ping.watch = g_timeout_add_seconds (1, respawn_ping_cb, self);
+	}
 }
 
 static gboolean
@@ -6245,46 +6775,30 @@ ip_check_ping_timeout_cb (gpointer user_data)
 }
 
 static gboolean
-spawn_ping (NMDevice *self,
+start_ping (NMDevice *self,
             guint log_domain,
             const char *binary,
             const char *address,
             guint timeout)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *args[] = { binary, "-I", nm_device_get_ip_iface (self), "-c", "1", "-w", NULL, address, NULL };
-	GError *error = NULL;
-	char *str_timeout;
-	gs_free char *tmp_str = NULL;
-	gboolean success;
 
 	g_return_val_if_fail (priv->gw_ping.watch == 0, FALSE);
 	g_return_val_if_fail (priv->gw_ping.timeout == 0, FALSE);
 
-	args[6] = str_timeout = g_strdup_printf ("%u", timeout);
-
-	_LOGD (log_domain, "ping: running '%s'",
-	       (tmp_str = g_strjoinv (" ", (gchar **) args)));
+	priv->gw_ping.log_domain = log_domain;
+	priv->gw_ping.address = g_strdup (address);
+	priv->gw_ping.binary = g_strdup (binary);
+	priv->gw_ping.deadline = timeout + 10;	/* the proper termination is enforced by a timer */
 
-	success = g_spawn_async ("/",
-	                         (gchar **) args,
-	                         NULL,
-	                         G_SPAWN_DO_NOT_REAP_CHILD,
-	                         nm_unblock_posix_signals,
-	                         NULL,
-	                         &priv->gw_ping.pid,
-	                         &error);
-	if (success) {
-		priv->gw_ping.log_domain = log_domain;
+	if (spawn_ping (self)) {
 		priv->gw_ping.watch = g_child_watch_add (priv->gw_ping.pid, ip_check_ping_watch_cb, self);
-		priv->gw_ping.timeout = g_timeout_add_seconds (timeout + 1, ip_check_ping_timeout_cb, self);
-	} else {
-		_LOGW (log_domain, "ping: could not spawn %s: %s", binary, error->message);
-		g_clear_error (&error);
+		priv->gw_ping.timeout = g_timeout_add_seconds (timeout, ip_check_ping_timeout_cb, self);
+		return TRUE;
 	}
 
-	g_free (str_timeout);
-	return success;
+	ip_check_gw_ping_cleanup (self);
+	return FALSE;
 }
 
 static void
@@ -6337,7 +6851,7 @@ nm_device_start_ip_check (NMDevice *self)
 	}
 
 	if (buf[0])
-		spawn_ping (self, log_domain, ping_binary, buf, timeout);
+		start_ping (self, log_domain, ping_binary, buf, timeout);
 
 	/* If no ping was started, just advance to pre_up */
 	if (!priv->gw_ping.pid)
@@ -6375,7 +6889,7 @@ is_up (NMDevice *self)
 {
 	int ifindex = nm_device_get_ip_ifindex (self);
 
-	return ifindex > 0 ? nm_platform_link_is_up (ifindex) : TRUE;
+	return ifindex > 0 ? nm_platform_link_is_up (NM_PLATFORM_GET, ifindex) : TRUE;
 }
 
 gboolean
@@ -6400,7 +6914,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 
 		do {
 			g_usleep (200);
-			if (!nm_platform_link_refresh (ifindex))
+			if (!nm_platform_link_refresh (NM_PLATFORM_GET, ifindex))
 				return FALSE;
 			device_is_up = nm_device_is_up (self);
 		} while (!device_is_up && nm_utils_get_monotonic_timestamp_us () < wait_until);
@@ -6419,7 +6933,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 	 * complete (via a pending action) until either the carrier turns on, or
 	 * a timeout is reached.
 	 */
-	if (device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
+	if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
 		if (priv->carrier_wait_id)
 			g_source_remove (priv->carrier_wait_id);
 		else
@@ -6434,15 +6948,6 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 	return TRUE;
 }
 
-static void
-check_carrier (NMDevice *self)
-{
-	int ifindex = nm_device_get_ip_ifindex (self);
-
-	if (!device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
-		nm_device_set_carrier (self, nm_platform_link_is_connected (ifindex));
-}
-
 static gboolean
 bring_up (NMDevice *self, gboolean *no_firmware)
 {
@@ -6455,12 +6960,10 @@ bring_up (NMDevice *self, gboolean *no_firmware)
 		return TRUE;
 	}
 
-	result = nm_platform_link_set_up (ifindex);
-	if (no_firmware)
-		*no_firmware = nm_platform_get_error () == NM_PLATFORM_ERROR_NO_FIRMWARE;
+	result = nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, no_firmware);
 
 	/* Store carrier immediately. */
-	if (result && device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT))
+	if (result && nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT))
 		check_carrier (self);
 
 	return result;
@@ -6487,7 +6990,7 @@ nm_device_take_down (NMDevice *self, gboolean block)
 
 		do {
 			g_usleep (200);
-			if (!nm_platform_link_refresh (ifindex))
+			if (!nm_platform_link_refresh (NM_PLATFORM_GET, ifindex))
 				return;
 			device_is_up = nm_device_is_up (self);
 		} while (device_is_up && nm_utils_get_monotonic_timestamp_us () < wait_until);
@@ -6507,7 +7010,7 @@ take_down (NMDevice *self)
 	int ifindex = nm_device_get_ip_ifindex (self);
 
 	if (ifindex > 0)
-		return nm_platform_link_set_down (ifindex);
+		return nm_platform_link_set_down (NM_PLATFORM_GET, ifindex);
 
 	/* devices without ifindex are always up. */
 	_LOGD (LOGD_HW, "cannot take down device without ifindex");
@@ -6634,11 +7137,10 @@ capture_lease_config (NMDevice *self,
 }
 
 static void
-update_ip_config (NMDevice *self, gboolean initial)
+update_ip4_config (NMDevice *self, gboolean initial)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int ifindex;
-	gboolean linklocal6_just_completed = FALSE;
 	gboolean capture_resolv_conf;
 	NMDnsManagerResolvConfMode resolv_conf_mode;
 
@@ -6652,13 +7154,35 @@ update_ip_config (NMDevice *self, gboolean initial)
 	/* IPv4 */
 	g_clear_object (&priv->ext_ip4_config);
 	priv->ext_ip4_config = nm_ip4_config_capture (ifindex, capture_resolv_conf);
-	priv->ext_ip4_config_had_any_addresses = (   priv->ext_ip4_config
-	                                          && nm_ip4_config_get_num_addresses (priv->ext_ip4_config) > 0);
 	if (priv->ext_ip4_config) {
 		if (initial) {
 			g_clear_object (&priv->dev_ip4_config);
 			capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL);
 		}
+
+		/* FIXME: ext_ip4_config does not contain routes with source==RTPROT_KERNEL.
+		 * Hence, we will wrongly remove device-routes with metric=0 if they were added by
+		 * the user on purpose. This should be fixed by also tracking and exposing
+		 * kernel routes. */
+
+		/* This function was called upon external changes. Remove the configuration
+		 * (adresses,routes) that is no longer present externally from the interal
+		 * config. This way, we don't readd addresses that were manually removed
+		 * by the user. */
+		if (priv->con_ip4_config)
+			nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config);
+		if (priv->dev_ip4_config)
+			nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config);
+		if (priv->vpn4_config)
+			nm_ip4_config_intersect (priv->vpn4_config, priv->ext_ip4_config);
+		if (priv->wwan_ip4_config)
+			nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config);
+
+		/* Remove parts from ext_ip4_config to only contain the information that
+		 * was configured externally -- we already have the same configuration from
+		 * internal origins. */
+		if (priv->con_ip4_config)
+			nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config);
 		if (priv->dev_ip4_config)
 			nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config);
 		if (priv->vpn4_config)
@@ -6668,18 +7192,53 @@ update_ip_config (NMDevice *self, gboolean initial)
 
 		ip4_config_merge_and_apply (self, NULL, FALSE, NULL);
 	}
+}
+
+static void
+update_ip6_config (NMDevice *self, gboolean initial)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ifindex;
+	gboolean linklocal6_just_completed = FALSE;
+	gboolean capture_resolv_conf;
+	NMDnsManagerResolvConfMode resolv_conf_mode;
+
+	ifindex = nm_device_get_ip_ifindex (self);
+	if (!ifindex)
+		return;
+
+	resolv_conf_mode = nm_dns_manager_get_resolv_conf_mode (nm_dns_manager_get ());
+	capture_resolv_conf = initial && (resolv_conf_mode == NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT);
 
 	/* IPv6 */
 	g_clear_object (&priv->ext_ip6_config);
 	priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
-	priv->ext_ip6_config_had_any_addresses = (   priv->ext_ip6_config
-	                                          && nm_ip6_config_get_num_addresses (priv->ext_ip6_config) > 0);
 	if (priv->ext_ip6_config) {
 
 		/* Check this before modifying ext_ip6_config */
 		linklocal6_just_completed = priv->linklocal6_timeout_id &&
 		                            have_ip6_address (priv->ext_ip6_config, TRUE);
 
+		/* This function was called upon external changes. Remove the configuration
+		 * (adresses,routes) that is no longer present externally from the interal
+		 * config. This way, we don't readd addresses that were manually removed
+		 * by the user. */
+		if (priv->con_ip6_config)
+			nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config);
+		if (priv->ac_ip6_config)
+			nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config);
+		if (priv->dhcp6_ip6_config)
+			nm_ip6_config_intersect (priv->dhcp6_ip6_config, priv->ext_ip6_config);
+		if (priv->wwan_ip6_config)
+			nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config);
+		if (priv->vpn6_config)
+			nm_ip6_config_intersect (priv->vpn6_config, priv->ext_ip6_config);
+
+		/* Remove parts from ext_ip6_config to only contain the information that
+		 * was configured externally -- we already have the same configuration from
+		 * internal origins. */
+		if (priv->con_ip6_config)
+			nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config);
 		if (priv->ac_ip6_config)
 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config);
 		if (priv->dhcp6_ip6_config)
@@ -6703,11 +7262,12 @@ update_ip_config (NMDevice *self, gboolean initial)
 void
 nm_device_capture_initial_config (NMDevice *self)
 {
-	update_ip_config (self, TRUE);
+	update_ip4_config (self, TRUE);
+	update_ip6_config (self, TRUE);
 }
 
 static gboolean
-queued_ip_config_change (gpointer user_data)
+queued_ip4_config_change (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -6716,8 +7276,27 @@ queued_ip_config_change (gpointer user_data)
 	if (priv->queued_state.id)
 		return TRUE;
 
-	priv->queued_ip_config_id = 0;
-	update_ip_config (self, FALSE);
+	priv->queued_ip4_config_id = 0;
+	g_object_ref (self);
+	update_ip4_config (self, FALSE);
+	g_object_unref (self);
+
+	return FALSE;
+}
+
+static gboolean
+queued_ip6_config_change (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	/* Wait for any queued state changes */
+	if (priv->queued_state.id)
+		return TRUE;
+
+	priv->queued_ip6_config_id = 0;
+	g_object_ref (self);
+	update_ip6_config (self, FALSE);
 
 	/* If no IPv6 link-local address exists but other addresses do then we
 	 * must add the LL address to remain conformant with RFC 3513 chapter 2.1
@@ -6727,36 +7306,43 @@ queued_ip_config_change (gpointer user_data)
 	if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config))
 		check_and_add_ipv6ll_addr (self);
 
+	g_object_unref (self);
+
 	return FALSE;
 }
 
 static void
-device_ip_changed (NMPlatform *platform,
-                   int ifindex,
-                   gpointer platform_object,
-                   NMPlatformSignalChangeType change_type,
-                   NMPlatformReason reason,
-                   NMDevice *self)
+device_ipx_changed (NMPlatform *platform,
+                    NMPObjectType obj_type,
+                    int ifindex,
+                    gpointer platform_object,
+                    NMPlatformSignalChangeType change_type,
+                    NMPlatformReason reason,
+                    NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	if (nm_device_get_ip_ifindex (self) == ifindex) {
-		if (!priv->queued_ip_config_id)
-			priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self);
-
-		_LOGD (LOGD_DEVICE, "queued IP config change");
-	}
-}
+	NMDevicePrivate *priv;
 
-static void
-nm_device_queued_ip_config_change_clear (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	if (nm_device_get_ip_ifindex (self) != ifindex)
+		return;
 
-	if (priv->queued_ip_config_id) {
-		_LOGD (LOGD_DEVICE, "clearing queued IP config change");
-		g_source_remove (priv->queued_ip_config_id);
-		priv->queued_ip_config_id = 0;
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	switch (obj_type) {
+	case NMP_OBJECT_TYPE_IP4_ADDRESS:
+	case NMP_OBJECT_TYPE_IP4_ROUTE:
+		if (!priv->queued_ip4_config_id) {
+			priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self);
+			_LOGD (LOGD_DEVICE, "queued IP4 config change");
+		}
+		break;
+	case NMP_OBJECT_TYPE_IP6_ADDRESS:
+	case NMP_OBJECT_TYPE_IP6_ROUTE:
+		if (!priv->queued_ip6_config_id) {
+			priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
+			_LOGD (LOGD_DEVICE, "queued IP6 config change");
+		}
+		break;
+	default:
+		g_return_if_reached ();
 	}
 }
 
@@ -6840,7 +7426,7 @@ nm_device_set_unmanaged (NMDevice *self,
 
 		if (unmanaged)
 			nm_device_state_changed (self, NM_DEVICE_STATE_UNMANAGED, reason);
-		else
+		else if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED)
 			nm_device_state_changed (self, NM_DEVICE_STATE_UNAVAILABLE, reason);
 	}
 }
@@ -7099,7 +7685,7 @@ cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpoin
 gboolean
 nm_device_supports_vlans (NMDevice *self)
 {
-	return nm_platform_link_supports_vlans (nm_device_get_ifindex (self));
+	return nm_platform_link_supports_vlans (NM_PLATFORM_GET, nm_device_get_ifindex (self));
 }
 
 /**
@@ -7206,16 +7792,16 @@ nm_device_has_pending_action (NMDevice *self)
 /***********************************************************/
 
 static void
-_cleanup_ip_pre (NMDevice *self, gboolean deconfigure)
+_cleanup_ip_pre (NMDevice *self, CleanupType cleanup_type)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->ip4_state = priv->ip6_state = IP_NONE;
 	nm_device_queued_ip_config_change_clear (self);
 
-	dhcp4_cleanup (self, deconfigure, FALSE);
+	dhcp4_cleanup (self, cleanup_type, FALSE);
 	arp_cleanup (self);
-	dhcp6_cleanup (self, deconfigure, FALSE);
+	dhcp6_cleanup (self, cleanup_type, FALSE);
 	linklocal6_cleanup (self);
 	addrconf6_cleanup (self);
 	dnsmasq_cleanup (self);
@@ -7223,10 +7809,9 @@ _cleanup_ip_pre (NMDevice *self, gboolean deconfigure)
 }
 
 static void
-_cleanup_generic_pre (NMDevice *self, gboolean deconfigure)
+_cancel_activation (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMConnection *connection;
 
 	/* Clean up when device was deactivated during call to firewall */
 	if (priv->fw_call) {
@@ -7234,8 +7819,22 @@ _cleanup_generic_pre (NMDevice *self, gboolean deconfigure)
 		priv->fw_call = NULL;
 	}
 
+	ip_check_gw_ping_cleanup (self);
+
+	/* Break the activation chain */
+	activation_source_clear (self, TRUE, AF_INET);
+	activation_source_clear (self, TRUE, AF_INET6);
+}
+
+static void
+_cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type)
+{
+	NMConnection *connection;
+
+	_cancel_activation (self);
+
 	connection = nm_device_get_connection (self);
-	if (   deconfigure
+	if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
 	    && connection
 	    && !nm_device_uses_assumed_connection (self)) {
 		nm_firewall_manager_remove_from_zone (nm_firewall_manager_get (),
@@ -7243,28 +7842,24 @@ _cleanup_generic_pre (NMDevice *self, gboolean deconfigure)
 		                                      NULL);
 	}
 
-	ip_check_gw_ping_cleanup (self);
-
-	/* Break the activation chain */
-	activation_source_clear (self, TRUE, AF_INET);
-	activation_source_clear (self, TRUE, AF_INET6);
-
 	/* Clear any queued transitions */
 	nm_device_queued_state_clear (self);
 
-	_cleanup_ip_pre (self, deconfigure);
+	_cleanup_ip_pre (self, cleanup_type);
 }
 
 static void
-_cleanup_generic_post (NMDevice *self, gboolean deconfigure)
+_cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
 
 	priv->default_route.v4_has = FALSE;
 	priv->default_route.v4_is_assumed = TRUE;
+	priv->default_route.v4_configure_first_time = TRUE;
 	priv->default_route.v6_has = FALSE;
 	priv->default_route.v6_is_assumed = TRUE;
+	priv->default_route.v6_configure_first_time = TRUE;
 
 	nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
 	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
@@ -7272,22 +7867,21 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure)
 	/* Clean up IP configs; this does not actually deconfigure the
 	 * interface; the caller must flush routes and addresses explicitly.
 	 */
-	nm_device_set_ip4_config (self, NULL, 0, TRUE, &ignored);
-	nm_device_set_ip6_config (self, NULL, TRUE, &ignored);
+	nm_device_set_ip4_config (self, NULL, 0, TRUE, TRUE, &ignored);
+	nm_device_set_ip6_config (self, NULL, TRUE, TRUE, &ignored);
+	g_clear_object (&priv->con_ip4_config);
 	g_clear_object (&priv->dev_ip4_config);
 	g_clear_object (&priv->ext_ip4_config);
 	g_clear_object (&priv->wwan_ip4_config);
 	g_clear_object (&priv->vpn4_config);
 	g_clear_object (&priv->ip4_config);
+	g_clear_object (&priv->con_ip6_config);
 	g_clear_object (&priv->ac_ip6_config);
 	g_clear_object (&priv->ext_ip6_config);
 	g_clear_object (&priv->vpn6_config);
 	g_clear_object (&priv->wwan_ip6_config);
 	g_clear_object (&priv->ip6_config);
 
-	priv->ext_ip4_config_had_any_addresses = FALSE;
-	priv->ext_ip6_config_had_any_addresses = FALSE;
-
 	clear_act_request (self);
 
 	/* Clear legacy IPv4 address property */
@@ -7296,7 +7890,7 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure)
 		g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS);
 	}
 
-	if (deconfigure) {
+	if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
 		/* Check if the device was deactivated, and if so, delete_link.
 		 * Don't call delete_link synchronously because we are currently
 		 * handling a state change -- which is not reentrant. */
@@ -7317,7 +7911,7 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure)
  *
  */
 static void
-nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfigure)
+nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType cleanup_type)
 {
 	NMDevicePrivate *priv;
 	int ifindex;
@@ -7325,17 +7919,17 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfig
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED)
-		_LOGI (LOGD_DEVICE, "preparing device");
+		_LOGD (LOGD_DEVICE, "preparing device");
 	else
-		_LOGI (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string (reason), reason);
+		_LOGD (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string (reason), reason);
 
 	/* Save whether or not we tried IPv6 for later */
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	_cleanup_generic_pre (self, deconfigure);
+	_cleanup_generic_pre (self, cleanup_type);
 
 	/* Turn off kernel IPv6 */
-	if (deconfigure) {
+	if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
 		set_disable_ipv6 (self, "1");
 		nm_device_ipv6_sysctl_set (self, "accept_ra", "0");
 		nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0");
@@ -7356,11 +7950,11 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfig
 	/* Take out any entries in the routing table and any IP address the device had. */
 	ifindex = nm_device_get_ip_ifindex (self);
 	if (ifindex > 0) {
-		nm_platform_route_flush (ifindex);
-		nm_platform_address_flush (ifindex);
+		nm_route_manager_route_flush (nm_route_manager_get (), ifindex);
+		nm_platform_address_flush (NM_PLATFORM_GET, ifindex);
 	}
 
-	_cleanup_generic_post (self, deconfigure);
+	_cleanup_generic_post (self, cleanup_type);
 }
 
 static char *
@@ -7689,11 +8283,11 @@ _set_state_full (NMDevice *self,
 		nm_device_set_firmware_missing (self, FALSE);
 		if (old_state > NM_DEVICE_STATE_UNMANAGED) {
 			if (reason == NM_DEVICE_STATE_REASON_REMOVED) {
-				nm_device_cleanup (self, reason, FALSE);
+				nm_device_cleanup (self, reason, CLEANUP_TYPE_REMOVED);
 			} else {
 				/* Clean up if the device is now unmanaged but was activated */
 				if (nm_device_get_act_request (self))
-					nm_device_cleanup (self, reason, TRUE);
+					nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE);
 				nm_device_take_down (self, TRUE);
 				set_nm_ipv6ll (self, FALSE);
 				restore_ip6_properties (self);
@@ -7722,7 +8316,7 @@ _set_state_full (NMDevice *self,
 			 * Note that we "deactivate" the device even when coming from
 			 * UNMANAGED, to ensure that it's in a clean state.
 			 */
-			nm_device_cleanup (self, reason, TRUE);
+			nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE);
 		}
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
@@ -7732,7 +8326,7 @@ _set_state_full (NMDevice *self,
 			 */
 			set_nm_ipv6ll (self, TRUE);
 
-			nm_device_cleanup (self, reason, TRUE);
+			nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE);
 		} else if (old_state < NM_DEVICE_STATE_DISCONNECTED) {
 			if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
 				/* Ensure IPv6 is set up as it may not have been done when
@@ -7747,7 +8341,7 @@ _set_state_full (NMDevice *self,
 			/* Clean up any half-done IP operations if the device's layer2
 			 * finds out it needs authentication during IP config.
 			 */
-			_cleanup_ip_pre (self, TRUE);
+			_cleanup_ip_pre (self, CLEANUP_TYPE_DECONFIGURE);
 		}
 		break;
 	default:
@@ -7774,8 +8368,9 @@ _set_state_full (NMDevice *self,
 		 * reasons.
 		 */
 		if (nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
-			_LOGD (LOGD_DEVICE, "device is available, will transition to DISCONNECTED");
-			nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
+			nm_device_queue_recheck_available (self,
+			                                   NM_DEVICE_STATE_REASON_NONE,
+			                                   NM_DEVICE_STATE_REASON_NONE);
 		} else {
 			if (old_state == NM_DEVICE_STATE_UNMANAGED)
 				_LOGD (LOGD_DEVICE, "device not yet available for transition to DISCONNECTED");
@@ -7785,6 +8380,14 @@ _set_state_full (NMDevice *self,
 		}
 		break;
 	case NM_DEVICE_STATE_DEACTIVATING:
+		_cancel_activation (self);
+
+		if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
+			/* We cache the ignore_carrier state to not react on config-reloads while the connection
+			 * is active. But on deactivating, reset the ignore-carrier flag to the current state. */
+			priv->ignore_carrier = nm_config_data_get_ignore_carrier (NM_CONFIG_GET_DATA, self);
+		}
+
 		if (quitting) {
 			nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN,
 			                         nm_act_request_get_connection (req),
@@ -7807,13 +8410,18 @@ _set_state_full (NMDevice *self,
 		if (   priv->queued_act_request
 		    && !priv->queued_act_request_is_waiting_for_carrier) {
 			NMActRequest *queued_req;
+			gboolean success;
 
 			queued_req = priv->queued_act_request;
 			priv->queued_act_request = NULL;
-			_device_activate (self, queued_req);
+			success = _device_activate (self, queued_req);
 			g_object_unref (queued_req);
-		} else if (   old_state > NM_DEVICE_STATE_DISCONNECTED
-		           && nm_device_get_default_unmanaged (self))
+			if (success)
+				break;
+			/* fall through */
+		}
+		if (   old_state > NM_DEVICE_STATE_DISCONNECTED
+		    && nm_device_get_default_unmanaged (self))
 			nm_device_queue_state (self, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
 		break;
 	case NM_DEVICE_STATE_ACTIVATED:
@@ -8026,7 +8634,7 @@ nm_device_update_hw_address (NMDevice *self)
 	if (ifindex <= 0)
 		return;
 
-	hwaddr = nm_platform_link_get_address (ifindex, &hwaddrlen);
+	hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddrlen);
 
 	if (hwaddrlen) {
 		if (!priv->hw_addr || !nm_utils_hwaddr_matches (priv->hw_addr, -1, hwaddr, hwaddrlen)) {
@@ -8072,7 +8680,7 @@ nm_device_set_hw_addr (NMDevice *self, const char *addr,
 	/* Can't change MAC address while device is up */
 	nm_device_take_down (self, FALSE);
 
-	success = nm_platform_link_set_address (nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len);
+	success = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len);
 	if (success) {
 		/* MAC address succesfully changed; update the current MAC to match */
 		nm_device_update_hw_address (self);
@@ -8094,6 +8702,22 @@ nm_device_set_hw_addr (NMDevice *self, const char *addr,
 	return success;
 }
 
+const char *
+nm_device_get_permanent_hw_address (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->perm_hw_addr;
+}
+
+const char *
+nm_device_get_initial_hw_address (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->initial_hw_addr;
+}
+
 /**
  * nm_device_spec_match_list:
  * @self: an #NMDevice
@@ -8123,24 +8747,34 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs)
 	if (!specs)
 		return FALSE;
 
-	return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs);
+	return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs) == NM_MATCH_SPEC_MATCH;
 }
 
-static gboolean
+static NMMatchSpecMatchType
 spec_match_list (NMDevice *self, const GSList *specs)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	gboolean matched = FALSE;
-
-	if (nm_match_spec_string (specs, "*"))
-		return TRUE;
-
-	if (priv->hw_addr_len)
-		matched = nm_match_spec_hwaddr (specs, priv->hw_addr);
-
-	if (!matched)
-		matched = nm_match_spec_interface_name (specs, nm_device_get_iface (self));
+	NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m;
+	const GSList *iter;
 
+	for (iter = specs; iter; iter = g_slist_next (iter)) {
+		if (!strcmp ((const char *) iter->data, "*")) {
+			matched = NM_MATCH_SPEC_MATCH;
+			break;
+		}
+	}
+	if (priv->hw_addr_len) {
+		m = nm_match_spec_hwaddr (specs, priv->hw_addr);
+		matched = MAX (matched, m);
+	}
+	if (matched != NM_MATCH_SPEC_NEG_MATCH) {
+		m = nm_match_spec_interface_name (specs, nm_device_get_iface (self));
+		matched = MAX (matched, m);
+	}
+	if (matched != NM_MATCH_SPEC_NEG_MATCH) {
+		m = nm_match_spec_device_type (specs, nm_device_get_type_description (self));
+		matched = MAX (matched, m);
+	}
 	return matched;
 }
 
@@ -8165,47 +8799,9 @@ nm_device_init (NMDevice *self)
 	priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
 
 	priv->default_route.v4_is_assumed = TRUE;
+	priv->default_route.v4_configure_first_time = TRUE;
 	priv->default_route.v6_is_assumed = TRUE;
-}
-
-/*
- * Get driver info from SIOCETHTOOL ioctl() for 'iface'
- * Returns driver and firmware versions to 'driver_version and' 'firmware_version'
- */
-static gboolean
-device_get_driver_info (NMDevice *self, const char *iface, char **driver_version, char **firmware_version)
-{
-	struct ethtool_drvinfo drvinfo;
-	struct ifreq req;
-	int fd;
-
-	fd = socket (PF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		_LOGW (LOGD_HW, "couldn't open control socket.");
-		return FALSE;
-	}
-
-	/* Get driver and firmware version info */
-	memset (&drvinfo, 0, sizeof (drvinfo));
-	memset (&req, 0, sizeof (struct ifreq));
-	strncpy (req.ifr_name, iface, IFNAMSIZ);
-	drvinfo.cmd = ETHTOOL_GDRVINFO;
-	req.ifr_data = &drvinfo;
-
-	errno = 0;
-	if (ioctl (fd, SIOCETHTOOL, &req) < 0) {
-		_LOGD (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d",
-		       iface, errno);
-		close (fd);
-		return FALSE;
-	}
-	if (driver_version)
-		*driver_version = g_strdup (drvinfo.version);
-	if (firmware_version)
-		*firmware_version = g_strdup (drvinfo.fw_version);
-
-	close (fd);
-	return TRUE;
+	priv->default_route.v6_configure_first_time = TRUE;
 }
 
 static GObject*
@@ -8243,27 +8839,40 @@ constructor (GType type,
 	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
 		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
 
-	if (priv->ifindex <= 0 && !device_has_capability (self, NM_DEVICE_CAP_IS_NON_KERNEL))
-		_LOGW (LOGD_HW, "failed to look up interface index");
+	if (priv->ifindex > 0) {
+		priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex);
+		priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex);
+		if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex))
+			priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
+		priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex);
+
+		nm_platform_link_get_driver_info (NM_PLATFORM_GET,
+		                                  priv->ifindex,
+		                                  NULL,
+		                                  &priv->driver_version,
+		                                  &priv->firmware_version);
+	}
 
-	device_get_driver_info (self, priv->iface, &priv->driver_version, &priv->firmware_version);
+	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
+		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
 
 	/* Watch for external IP config changes */
 	platform = nm_platform_get ();
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), self);
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), self);
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self);
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self);
 
 	/* trigger initial ip config change to initialize ip-config */
-	priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self);
+	priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self);
+	priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
 
-	if (nm_platform_check_support_user_ipv6ll ()) {
+	if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) {
 		int ip_ifindex = nm_device_get_ip_ifindex (self);
 
 		if (ip_ifindex > 0)
-			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (ip_ifindex);
+			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, ip_ifindex);
 	}
 
 	return object;
@@ -8282,18 +8891,39 @@ constructed (GObject *object)
 
 	nm_device_update_hw_address (self);
 
-	if (NM_DEVICE_GET_CLASS (self)->update_permanent_hw_address)
-		NM_DEVICE_GET_CLASS (self)->update_permanent_hw_address (self);
+	if (priv->hw_addr_len) {
+		priv->initial_hw_addr = g_strdup (priv->hw_addr);
+		_LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr);
 
-	if (NM_DEVICE_GET_CLASS (self)->update_initial_hw_address)
-		NM_DEVICE_GET_CLASS (self)->update_initial_hw_address (self);
+		if (priv->ifindex > 0) {
+			guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
+			size_t len = 0;
+
+			if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) {
+				g_warn_if_fail (len == priv->hw_addr_len);
+				priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
+				_LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s",
+				       priv->perm_hw_addr);
+			} else {
+				/* Fall back to current address */
+				_LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address");
+				priv->perm_hw_addr = g_strdup (priv->hw_addr);
+			}
+		}
+	}
 
-	/* Have to call update_initial_hw_address() before calling get_ignore_carrier() */
-	if (device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
-		priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), self);
+	/* Note: initial hardware address must be read before calling get_ignore_carrier() */
+	if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
+		NMConfig *config = nm_config_get ();
+
+		priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self);
+		g_signal_connect (G_OBJECT (config),
+		                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
+		                  G_CALLBACK (config_changed_update_ignore_carrier),
+		                  self);
 
 		check_carrier (self);
-		_LOGI (LOGD_HW,
+		_LOGD (LOGD_HW,
 		       "carrier is %s%s",
 		       priv->carrier ? "ON" : "OFF",
 		       priv->ignore_carrier ? " (but ignored)" : "");
@@ -8302,18 +8932,8 @@ constructed (GObject *object)
 		priv->carrier = TRUE;
 	}
 
-	if (priv->ifindex > 0) {
-		priv->is_software = nm_platform_link_is_software (priv->ifindex);
-		priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex);
-		priv->dev_id = nm_platform_link_get_dev_id (priv->ifindex);
-		priv->mtu = nm_platform_link_get_mtu (priv->ifindex);
-	}
-	/* Indicate software device in capabilities. */
-	if (priv->is_software)
-		priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
-
 	/* Enslave ourselves */
-	master = nm_platform_link_get_master (priv->ifindex);
+	master = nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex);
 	if (master)
 		device_set_master (self, master);
 
@@ -8355,9 +8975,11 @@ dispose (GObject *object)
 
 	_LOGD (LOGD_DEVICE, "dispose(): %s", G_OBJECT_TYPE_NAME (self));
 
+	g_signal_handlers_disconnect_by_func (nm_config_get (), config_changed_update_ignore_carrier, self);
+
 	dispatcher_cleanup (self);
 
-	_cleanup_generic_pre (self, FALSE);
+	_cleanup_generic_pre (self, CLEANUP_TYPE_KEEP);
 
 	g_warn_if_fail (priv->slaves == NULL);
 	g_assert (priv->master_ready_id == 0);
@@ -8365,7 +8987,7 @@ dispose (GObject *object)
 	/* Let the kernel manage IPv6LL again */
 	set_nm_ipv6ll (self, FALSE);
 
-	_cleanup_generic_post (self, FALSE);
+	_cleanup_generic_post (self, CLEANUP_TYPE_KEEP);
 
 	g_hash_table_remove_all (priv->ip6_saved_properties);
 
@@ -8374,6 +8996,11 @@ dispose (GObject *object)
 		priv->recheck_assume_id = 0;
 	}
 
+	if (priv->recheck_available.call_id) {
+		g_source_remove (priv->recheck_available.call_id);
+		priv->recheck_available.call_id = 0;
+	}
+
 	link_disconnect_action_cancel (self);
 
 	if (priv->con_provider) {
@@ -8393,9 +9020,12 @@ dispose (GObject *object)
 	_clear_queued_act_request (priv);
 
 	platform = nm_platform_get ();
-	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self);
+	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self);
 	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
 
+	nm_clear_g_source (&priv->device_link_changed_id);
+	nm_clear_g_source (&priv->device_ip_link_changed_id);
+
 	G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
 }
 
@@ -8408,6 +9038,8 @@ finalize (GObject *object)
 	_LOGD (LOGD_DEVICE, "finalize(): %s", G_OBJECT_TYPE_NAME (self));
 
 	g_free (priv->hw_addr);
+	g_free (priv->perm_hw_addr);
+	g_free (priv->initial_hw_addr);
 	g_slist_free_full (priv->pending_actions, g_free);
 	g_clear_pointer (&priv->physical_port_id, g_free);
 	g_free (priv->udi);
@@ -8418,6 +9050,7 @@ finalize (GObject *object)
 	g_free (priv->driver_version);
 	g_free (priv->firmware_version);
 	g_free (priv->type_desc);
+	g_free (priv->type_description);
 	g_free (priv->dhcp_anycast_address);
 
 	g_hash_table_unref (priv->ip6_saved_properties);
@@ -8435,19 +9068,20 @@ set_property (GObject *object, guint prop_id,
 	NMPlatformLink *platform_device;
 	const char *hw_addr, *p;
 	guint count;
- 
+
 	switch (prop_id) {
 	case PROP_PLATFORM_DEVICE:
 		platform_device = g_value_get_pointer (value);
 		if (platform_device) {
 			g_free (priv->udi);
-			priv->udi = g_strdup (platform_device->udi);
+			priv->udi = g_strdup (nm_platform_link_get_udi (NM_PLATFORM_GET, platform_device->ifindex));
 			g_free (priv->iface);
 			priv->iface = g_strdup (platform_device->name);
 			priv->ifindex = platform_device->ifindex;
-			priv->up = platform_device->up;
+			priv->up = NM_FLAGS_HAS (platform_device->flags, IFF_UP);
 			g_free (priv->driver);
 			priv->driver = g_strdup (platform_device->driver);
+			priv->platform_link_initialized = platform_device->initialized;
 		}
 		break;
 	case PROP_UDI:
@@ -8460,9 +9094,9 @@ set_property (GObject *object, guint prop_id,
 		if (g_value_get_string (value)) {
 			g_free (priv->iface);
 			priv->iface = g_value_dup_string (value);
-			priv->ifindex = nm_platform_link_get_ifindex (priv->iface);
+			priv->ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->iface);
 			if (priv->ifindex > 0)
-				priv->up = nm_platform_link_is_up (priv->ifindex);
+				priv->up = nm_platform_link_is_up (NM_PLATFORM_GET, priv->ifindex);
 		}
 		break;
 	case PROP_DRIVER:
@@ -8696,10 +9330,12 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout;
 	klass->have_any_ready_slaves = have_any_ready_slaves;
 
+	klass->get_type_description = get_type_description;
 	klass->spec_match_list = spec_match_list;
 	klass->can_auto_connect = can_auto_connect;
 	klass->check_connection_compatible = check_connection_compatible;
 	klass->check_connection_available = check_connection_available;
+	klass->can_unmanaged_external_down = can_unmanaged_external_down;
 	klass->is_up = is_up;
 	klass->bring_up = bring_up;
 	klass->take_down = take_down;
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index e204af84..f1dab9bf 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -130,19 +130,17 @@ typedef struct {
 	void            (* link_changed) (NMDevice *self, NMPlatformLink *info);
 
 	/* Hardware state (IFF_UP) */
-	gboolean        (*is_up)      (NMDevice *self);
-	gboolean        (*bring_up)   (NMDevice *self, gboolean *no_firmware);
-	gboolean        (*take_down)  (NMDevice *self);
+	gboolean        (*can_unmanaged_external_down)  (NMDevice *self);
+	gboolean        (*is_up)                        (NMDevice *self);
+	gboolean        (*bring_up)                     (NMDevice *self, gboolean *no_firmware);
+	gboolean        (*take_down)                    (NMDevice *self);
 
 	/* Carrier state (IFF_LOWER_UP) */
 	void            (*carrier_changed) (NMDevice *, gboolean carrier);
 
-	void        (* update_permanent_hw_address) (NMDevice *self);
-	void        (* update_initial_hw_address) (NMDevice *self);
-
 	gboolean    (* get_ip_iface_identifier) (NMDevice *self, NMUtilsIPv6IfaceId *out_iid);
 
-	guint32		(* get_generic_capabilities)	(NMDevice *self);
+	NMDeviceCapabilities (* get_generic_capabilities) (NMDevice *self);
 
 	gboolean	(* is_available) (NMDevice *self, NMDeviceCheckDevAvailableFlags flags);
 
@@ -213,7 +211,9 @@ typedef struct {
 	/* Sync deactivating (in the DISCONNECTED phase) */
 	void			(* deactivate)			(NMDevice *self);
 
-	gboolean        (* spec_match_list)     (NMDevice *self, const GSList *specs);
+	const char *(*get_type_description) (NMDevice *self);
+
+	NMMatchSpecMatchType (* spec_match_list)   (NMDevice *self, const GSList *specs);
 
 	/* Update the connection with currently configured L2 settings */
 	void            (* update_connection) (NMDevice *device, NMConnection *connection);
@@ -235,6 +235,21 @@ typedef struct {
 	gboolean        (* have_any_ready_slaves) (NMDevice *self,
 	                                           const GSList *slaves);
 
+	/**
+	 * component_added:
+	 * @self: the #NMDevice
+	 * @component: the component (device, modem, etc) which was added
+	 *
+	 * Notifies @self that a new component was added to the Manager.  This
+	 * may include any kind of %GObject subclass, and the device is expected
+	 * to match only specific components they care about, like %NMModem objects
+	 * or %NMDevice objects.
+	 *
+	 * Returns: %TRUE if the component was claimed exclusively and no further
+	 * devices should be notified of the new component.  %FALSE to indicate
+	 * that the component was not exclusively claimed and other devices should
+	 * be notified.
+	 */
 	gboolean        (* component_added) (NMDevice *self, GObject *component);
 
 	gboolean        (* owns_iface) (NMDevice *self, const char *iface);
@@ -264,13 +279,16 @@ int             nm_device_get_ip_ifindex(NMDevice *dev);
 const char *	nm_device_get_driver	(NMDevice *dev);
 const char *	nm_device_get_driver_version	(NMDevice *dev);
 const char *	nm_device_get_type_desc (NMDevice *dev);
+const char *	nm_device_get_type_description (NMDevice *dev);
 NMDeviceType	nm_device_get_device_type	(NMDevice *dev);
 
 int			nm_device_get_priority (NMDevice *dev);
 guint32     nm_device_get_ip4_route_metric (NMDevice *dev);
 guint32     nm_device_get_ip6_route_metric (NMDevice *dev);
 
-const char *    nm_device_get_hw_address   (NMDevice *dev);
+const char *    nm_device_get_hw_address           (NMDevice *dev);
+const char *    nm_device_get_permanent_hw_address (NMDevice *dev);
+const char *    nm_device_get_initial_hw_address   (NMDevice *dev);
 
 NMDhcp4Config * nm_device_get_dhcp4_config (NMDevice *dev);
 NMDhcp6Config * nm_device_get_dhcp6_config (NMDevice *dev);
@@ -342,6 +360,8 @@ RfKillType nm_device_get_rfkill_type (NMDevice *device);
  * @NM_UNMANAGED_USER: %TRUE when unmanaged by user decision (via unmanaged-specs)
  * @NM_UNMANAGED_PARENT: %TRUE when unmanaged due to parent device being unmanaged
  * @NM_UNMANAGED_EXTERNAL_DOWN: %TRUE when unmanaged because !IFF_UP and not created by NM
+ * @NM_UNMANAGED_PLATFORM_INIT: %TRUE when unmanaged because platform link not
+ *   yet initialized
  */
 typedef enum {
 	NM_UNMANAGED_NONE          = 0x00,
@@ -350,6 +370,7 @@ typedef enum {
 	NM_UNMANAGED_USER          = 0x04,
 	NM_UNMANAGED_PARENT        = 0x08,
 	NM_UNMANAGED_EXTERNAL_DOWN = 0x10,
+	NM_UNMANAGED_PLATFORM_INIT = 0x20,
 
 	/* Boundary value */
 	__NM_UNMANAGED_LAST,
@@ -371,6 +392,8 @@ void nm_device_set_initial_unmanaged_flag (NMDevice *device,
 gboolean nm_device_get_is_nm_owned (NMDevice *device);
 void     nm_device_set_nm_owned    (NMDevice *device);
 
+gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps);
+
 gboolean nm_device_get_autoconnect (NMDevice *device);
 
 void nm_device_handle_autoip4_event (NMDevice *self,
@@ -387,6 +410,8 @@ void nm_device_queue_state   (NMDevice *self,
 
 gboolean nm_device_get_firmware_missing (NMDevice *self);
 
+void nm_device_steal_connection (NMDevice *device, NMConnection *connection);
+
 void nm_device_queue_activation (NMDevice *device, NMActRequest *req);
 
 gboolean nm_device_supports_vlans (NMDevice *device);
diff --git a/src/devices/team/Makefile.in b/src/devices/team/Makefile.in
index 29ad0598..342c41ee 100644
--- a/src/devices/team/Makefile.in
+++ b/src/devices/team/Makefile.in
@@ -227,6 +227,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -340,6 +341,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 49202c36..23750f35 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -39,7 +39,6 @@
 #include "nm-dbus-manager.h"
 #include "nm-enum-types.h"
 #include "nm-team-enum-types.h"
-#include "nm-posix-signals.h"
 #include "nm-core-internal.h"
 #include "gsystem-local-alloc.h"
 
@@ -71,18 +70,16 @@ static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
 
 /******************************************************************/
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *device)
 {
-	return NM_DEVICE_CAP_CARRIER_DETECT;
+	return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
 }
 
 static gboolean
 is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
-	if (NM_DEVICE_GET_CLASS (device)->is_up)
-		return NM_DEVICE_GET_CLASS (device)->is_up (device);
-	return FALSE;
+	return TRUE;
 }
 
 static gboolean
@@ -424,23 +421,6 @@ teamd_process_watch_cb (GPid pid, gint status, gpointer user_data)
 	}
 }
 
-static void
-teamd_child_setup (gpointer user_data G_GNUC_UNUSED)
-{
-	/* We are in the child process at this point.
-	 * Give child it's own program group for signal
-	 * separation.
-	 */
-	pid_t pid = getpid ();
-	setpgid (pid, pid);
-
-	/*
-	 * We blocked signals in main(). We need to restore original signal
-	 * mask for avahi-autoipd here so that it can receive signals.
-	 */
-	nm_unblock_posix_signals (NULL);
-}
-
 static gboolean
 teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error)
 {
@@ -448,7 +428,7 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error)
 	gs_free char *tmp_str = NULL;
 
 	if (!teamd_binary) {
-		teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL);
+		teamd_binary = nm_utils_find_helper ("teamd", NULL, error);
 		if (!teamd_binary) {
 			_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: teamd binary not found");
 			return FALSE;
@@ -463,7 +443,7 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error)
 	g_ptr_array_add (argv, NULL);
 
 	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
-	return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, NULL, error);
+	return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, NULL, NULL, NULL, NULL, NULL, error);
 }
 
 static gboolean
@@ -514,7 +494,7 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team)
 
 	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
 	if (!g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                    teamd_child_setup, NULL, &priv->teamd_pid, &error)) {
+	                    nm_utils_setpgid, NULL, &priv->teamd_pid, &error)) {
 		_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message);
 		teamd_cleanup (device, TRUE);
 		return FALSE;
@@ -638,7 +618,8 @@ enslave_slave (NMDevice *device,
 				}
 			}
 		}
-		success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device),
+		success = nm_platform_link_enslave (NM_PLATFORM_GET,
+		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 		nm_device_bring_up (slave, TRUE, &no_firmware);
 
@@ -663,7 +644,8 @@ release_slave (NMDevice *device,
 	gboolean success = TRUE, no_firmware = FALSE;
 
 	if (configure) {
-		success = nm_platform_link_release (nm_device_get_ip_ifindex (device),
+		success = nm_platform_link_release (NM_PLATFORM_GET,
+		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 
 		if (success)
@@ -694,8 +676,6 @@ release_slave (NMDevice *device,
 NMDevice *
 nm_device_team_new (NMPlatformLink *platform_device)
 {
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TEAM,
 	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
 	                                  NM_DEVICE_DRIVER, "team",
@@ -708,21 +688,18 @@ nm_device_team_new (NMPlatformLink *platform_device)
 NMDevice *
 nm_device_team_new_for_connection (NMConnection *connection, GError **error)
 {
-	const char *iface;
-
-	g_return_val_if_fail (connection != NULL, NULL);
-
-	iface = nm_connection_get_interface_name (connection);
-	g_return_val_if_fail (iface != NULL, NULL);
-
-	if (   !nm_platform_team_add (iface)
-	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		g_set_error (error,
-		             NM_DEVICE_ERROR,
-		             NM_DEVICE_ERROR_CREATION_FAILED,
-		             "failed to create team master interface '%s' for connection '%s': %s",
-		             iface, nm_connection_get_id (connection),
-		             nm_platform_get_error_msg ());
+	const char *iface = nm_connection_get_interface_name (connection);
+	NMPlatformError plerr;
+
+	g_assert (iface);
+
+	plerr = nm_platform_team_add (NM_PLATFORM_GET, iface, NULL);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create team master interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
 		return NULL;
 	}
 
diff --git a/src/devices/team/nm-team-factory.c b/src/devices/team/nm-team-factory.c
index cb887cac..d87919b6 100644
--- a/src/devices/team/nm-team-factory.c
+++ b/src/devices/team/nm-team-factory.c
@@ -48,11 +48,9 @@ nm_device_factory_create (GError **error)
 /************************************************************************/
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	if (plink->type == NM_LINK_TYPE_TEAM)
-		 return nm_device_team_new (plink);
-	return NULL;
+	return nm_device_team_new (plink);
 }
 
 static NMDevice *
@@ -61,16 +59,13 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
                                       NMDevice *parent,
                                       GError **error)
 {
-	if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME))
-		return nm_device_team_new_for_connection (connection, error);
-	return NULL;
+	return nm_device_team_new_for_connection (connection, error);
 }
 
-static NMDeviceType
-get_device_type (NMDeviceFactory *factory)
-{
-	return NM_DEVICE_TYPE_TEAM;
-}
+NM_DEVICE_FACTORY_DECLARE_TYPES (
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_TEAM)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_TEAM_SETTING_NAME)
+)
 
 /************************************************************************/
 
@@ -84,7 +79,7 @@ device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
 	factory_iface->new_link = new_link;
 	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
-	factory_iface->get_device_type = get_device_type;
+	factory_iface->get_supported_types = get_supported_types;
 }
 
 static void
diff --git a/src/devices/wifi/Makefile.in b/src/devices/wifi/Makefile.in
index f5e88007..b679bfc2 100644
--- a/src/devices/wifi/Makefile.in
+++ b/src/devices/wifi/Makefile.in
@@ -267,6 +267,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -380,6 +381,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index dc3dfbc6..e67d1a20 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -197,8 +197,8 @@ _mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel)
 {
 	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
 
-	if (nm_platform_mesh_get_channel (ifindex) != channel) {
-		if (nm_platform_mesh_set_channel (ifindex, channel))
+	if (nm_platform_mesh_get_channel (NM_PLATFORM_GET, ifindex) != channel) {
+		if (nm_platform_mesh_set_channel (NM_PLATFORM_GET, ifindex, channel))
 			g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL);
 	}
 }
@@ -224,7 +224,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		_mesh_set_channel (self, channel);
 
 	ssid = nm_setting_olpc_mesh_get_ssid (s_mesh);
-	nm_platform_mesh_set_ssid (nm_device_get_ifindex (device),
+	nm_platform_mesh_set_ssid (NM_PLATFORM_GET,
+	                           nm_device_get_ifindex (device),
 	                           g_bytes_get_data (ssid, NULL),
 	                           g_bytes_get_size (ssid));
 
@@ -368,9 +369,9 @@ device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data)
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
 
 	if (!priv->companion && check_companion (self, other)) {
-		nm_device_state_changed (NM_DEVICE (self),
-		                         NM_DEVICE_STATE_DISCONNECTED,
-		                         NM_DEVICE_STATE_REASON_NONE);
+		nm_device_queue_recheck_available (NM_DEVICE (self),
+		                                   NM_DEVICE_STATE_REASON_NONE,
+		                                   NM_DEVICE_STATE_REASON_NONE);
 		nm_device_remove_pending_action (NM_DEVICE (self), "waiting for companion", TRUE);
 	}
 }
@@ -398,9 +399,9 @@ find_companion (NMDeviceOlpcMesh *self)
 	/* Try to find the companion if it's already known to the NMManager */
 	for (list = nm_manager_get_devices (nm_manager_get ()); list ; list = g_slist_next (list)) {
 		if (check_companion (self, NM_DEVICE (list->data))) {
-			nm_device_queue_state (NM_DEVICE (self),
-			                       NM_DEVICE_STATE_DISCONNECTED,
-			                       NM_DEVICE_STATE_REASON_NONE);
+			nm_device_queue_recheck_available (NM_DEVICE (self),
+			                                   NM_DEVICE_STATE_REASON_NONE,
+			                                   NM_DEVICE_STATE_REASON_NONE);
 			nm_device_remove_pending_action (NM_DEVICE (self), "waiting for companion", TRUE);
 			break;
 		}
@@ -453,7 +454,7 @@ constructor (GType type,
 
 	self = NM_DEVICE_OLPC_MESH (object);
 
-	if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), &caps)) {
+	if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &caps)) {
 		_LOGW (LOGD_HW | LOGD_OLPC, "failed to initialize WiFi driver");
 		g_object_unref (object);
 		return NULL;
@@ -482,7 +483,7 @@ get_property (GObject *object, guint prop_id,
 			g_value_set_boxed (value, "/");
 		break;
 	case PROP_ACTIVE_CHANNEL:
-		g_value_set_uint (value, nm_platform_mesh_get_channel (nm_device_get_ifindex (NM_DEVICE (device))));
+		g_value_set_uint (value, nm_platform_mesh_get_channel (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (device))));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index ce9a3f75..2e116f87 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -26,13 +26,7 @@
 #include <dbus/dbus.h>
 #include <netinet/in.h>
 #include <string.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <signal.h>
 #include <unistd.h>
-#include <linux/sockios.h>
-#include <linux/ethtool.h>
-#include <sys/ioctl.h>
 #include <errno.h>
 
 #include "nm-glib-compat.h"
@@ -116,10 +110,7 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 struct _NMDeviceWifiPrivate {
-	gboolean          disposed;
-
-	char *            perm_hw_addr;    /* Permanent MAC address */
-	char *            initial_hw_addr; /* Initial MAC address (as seen when NM starts) */
+	gboolean disposed;
 
 	gint8             invalid_strength_counter;
 
@@ -165,7 +156,7 @@ static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
 
 static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface,
                                          const char *object_path,
-                                         GHashTable *properties,
+                                         GVariant *properties,
                                          NMDeviceWifi * self);
 
 static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
@@ -213,7 +204,8 @@ constructor (GType type,
 	self = NM_DEVICE_WIFI (object);
 	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)),
+	if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET,
+	                                        nm_device_get_ifindex (NM_DEVICE (self)),
 	                                        &priv->capabilities)) {
 		_LOGW (LOGD_HW | LOGD_WIFI, "failed to initialize WiFi driver");
 		g_object_unref (object);
@@ -362,21 +354,21 @@ find_active_ap (NMDeviceWifi *self,
 	NM80211Mode devmode;
 	guint32 devfreq;
 
-	nm_platform_wifi_get_bssid (ifindex, bssid);
+	nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid);
 	_LOGD (LOGD_WIFI, "active BSSID: %02x:%02x:%02x:%02x:%02x:%02x",
 	       bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
 
 	if (!nm_ethernet_address_is_valid (bssid, ETH_ALEN))
 		return NULL;
 
-	ssid = nm_platform_wifi_get_ssid (ifindex);
+	ssid = nm_platform_wifi_get_ssid (NM_PLATFORM_GET, ifindex);
 	_LOGD (LOGD_WIFI, "active SSID: %s%s%s",
 	       ssid ? "'" : "",
 	       ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
 	       ssid ? "'" : "");
 
-	devmode = nm_platform_wifi_get_mode (ifindex);
-	devfreq = nm_platform_wifi_get_frequency (ifindex);
+	devmode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, ifindex);
+	devfreq = nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex);
 
 	/* When matching hidden APs, do a second pass that ignores the SSID check,
 	 * because NM might not yet know the SSID of the hidden AP in the scan list
@@ -591,7 +583,7 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
 	if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) {
 		guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
 
-		nm_platform_wifi_get_bssid (ifindex, bssid);
+		nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid);
 		/* 0x02 means "locally administered" and should be OR-ed into
 		 * the first byte of IBSS BSSIDs.
 		 */
@@ -607,7 +599,7 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
 		/* Try to smooth out the strength.  Atmel cards, for example, will give no strength
 		 * one second and normal strength the next.
 		 */
-		percent = nm_platform_wifi_get_quality (ifindex);
+		percent = nm_platform_wifi_get_quality (NM_PLATFORM_GET, ifindex);
 		if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
 			nm_ap_set_strength (new_ap, (gint8) percent);
 			priv->invalid_strength_counter = 0;
@@ -639,7 +631,7 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
 		set_current_ap (self, new_ap, TRUE, FALSE);
 	}
 
-	new_rate = nm_platform_wifi_get_rate (ifindex);
+	new_rate = nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex);
 	if (new_rate != priv->rate) {
 		priv->rate = new_rate;
 		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE);
@@ -738,18 +730,18 @@ deactivate (NMDevice *device)
 	set_current_ap (self, NULL, TRUE, FALSE);
 
 	/* Clear any critical protocol notification in the Wi-Fi stack */
-	nm_platform_wifi_indicate_addressing_running (ifindex, FALSE);
+	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE);
 
 	/* Reset MAC address back to initial address */
-	if (priv->initial_hw_addr)
-		nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_WIFI);
+	if (nm_device_get_initial_hw_address (device))
+		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_WIFI);
 
 	/* Ensure we're in infrastructure mode after deactivation; some devices
 	 * (usually older ones) don't scan well in adhoc mode.
 	 */
-	if (nm_platform_wifi_get_mode (ifindex) != NM_802_11_MODE_INFRA) {
+	if (nm_platform_wifi_get_mode (NM_PLATFORM_GET, ifindex) != NM_802_11_MODE_INFRA) {
 		nm_device_take_down (NM_DEVICE (self), TRUE);
-		nm_platform_wifi_set_mode (ifindex, NM_802_11_MODE_INFRA);
+		nm_platform_wifi_set_mode (NM_PLATFORM_GET, ifindex, NM_802_11_MODE_INFRA);
 		nm_device_bring_up (NM_DEVICE (self), TRUE, NULL);
 	}
 
@@ -806,6 +798,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	const char * const *mac_blacklist;
 	int i;
 	const char *mode;
+	const char *perm_hw_addr;
 
 	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection))
 		return FALSE;
@@ -820,21 +813,25 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	if (!s_wireless)
 		return FALSE;
 
+	perm_hw_addr = nm_device_get_permanent_hw_address (device);
 	mac = nm_setting_wireless_get_mac_address (s_wireless);
-	if (mac && !nm_utils_hwaddr_matches (mac, -1, priv->perm_hw_addr, -1))
-		return FALSE;
-
-	/* Check for MAC address blacklist */
-	mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
-	for (i = 0; mac_blacklist[i]; i++) {
-		if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
-			g_warn_if_reached ();
+	if (perm_hw_addr) {
+		if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
 			return FALSE;
-		}
 
-		if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, priv->perm_hw_addr, -1))
-			return FALSE;
-	}
+		/* Check for MAC address blacklist */
+		mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
+		for (i = 0; mac_blacklist[i]; i++) {
+			if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
+				g_warn_if_reached ();
+				return FALSE;
+			}
+
+			if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1))
+				return FALSE;
+		}
+	} else if (mac)
+		return FALSE;
 
 	if (is_adhoc_wpa (connection))
 		return FALSE;
@@ -970,6 +967,7 @@ complete_connection (NMDevice *device,
 	GBytes *setting_ssid = NULL;
 	GSList *iter;
 	gboolean hidden = FALSE;
+	const char *perm_hw_addr;
 
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	s_wsec = nm_connection_get_setting_wireless_security (connection);
@@ -1105,29 +1103,31 @@ complete_connection (NMDevice *device,
 	if (hidden)
 		g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
 
-	setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
-	if (setting_mac) {
-		/* Make sure the setting MAC (if any) matches the device's permanent MAC */
-		if (!nm_utils_hwaddr_matches (setting_mac, -1, priv->perm_hw_addr, -1)) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("connection does not match device"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS);
-			return FALSE;
-		}
-	} else {
-		guint8 perm_hw_addr[ETH_ALEN];
+	perm_hw_addr = nm_device_get_permanent_hw_address (device);
+	if (perm_hw_addr) {
+		setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
+		if (setting_mac) {
+			/* Make sure the setting MAC (if any) matches the device's permanent MAC */
+			if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) {
+				g_set_error_literal (error,
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("connection does not match device"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS);
+				return FALSE;
+			}
+		} else {
+			guint8 tmp[ETH_ALEN];
 
-		/* Lock the connection to this device by default if it uses a
-		 * permanent MAC address (ie not a 'locally administered' one)
-		 */
-		nm_utils_hwaddr_aton (priv->perm_hw_addr, perm_hw_addr, ETH_ALEN);
-		if (   !(perm_hw_addr[0] & 0x02)
-		    && !nm_utils_hwaddr_matches (perm_hw_addr, ETH_ALEN, NULL, ETH_ALEN)) {
-			g_object_set (G_OBJECT (s_wifi),
-			              NM_SETTING_WIRELESS_MAC_ADDRESS, priv->perm_hw_addr,
-			              NULL);
+			/* Lock the connection to this device by default if it uses a
+			 * permanent MAC address (ie not a 'locally administered' one)
+			 */
+			nm_utils_hwaddr_aton (perm_hw_addr, tmp, ETH_ALEN);
+			if (!(tmp[0] & 0x02)) {
+				g_object_set (G_OBJECT (s_wifi),
+				              NM_SETTING_WIRELESS_MAC_ADDRESS, perm_hw_addr,
+				              NULL);
+			}
 		}
 	}
 
@@ -1832,7 +1832,7 @@ schedule_scanlist_cull (NMDeviceWifi *self)
 static void
 supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
                              const char *object_path,
-                             GHashTable *properties,
+                             GVariant *properties,
                              NMDeviceWifi *self)
 {
 	NMDeviceState state;
@@ -2148,6 +2148,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	NMDevice *device = NM_DEVICE (self);
 	NMDeviceState devstate;
 	gboolean scanning;
+	gboolean recheck_available = FALSE;
 
 	if (new_state == old_state)
 		return;
@@ -2167,23 +2168,9 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 
 	switch (new_state) {
 	case NM_SUPPLICANT_INTERFACE_STATE_READY:
+		_LOGD (LOGD_WIFI_SCAN, "supplicant ready");
+		recheck_available = TRUE;
 		priv->scan_interval = SCAN_INTERVAL_MIN;
-
-		/* If the interface can now be activated because the supplicant is now
-		 * available, transition to DISCONNECTED.
-		 */
-		if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_DISCONNECTED,
-			                         NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE);
-		}
-
-		_LOGD (LOGD_WIFI_SCAN, "supplicant ready, requesting initial scan");
-
-		/* Request a scan to get latest results */
-		cancel_pending_scan (self);
-		request_wireless_scan (self);
-
 		if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY)
 			nm_device_remove_pending_action (device, "waiting for supplicant", TRUE);
 		break;
@@ -2243,6 +2230,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		}
 		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+		recheck_available = TRUE;
 		cleanup_association_attempt (self, FALSE);
 
 		if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY)
@@ -2255,15 +2243,17 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		 */
 		supplicant_interface_release (self);
 		supplicant_interface_acquire (self);
-
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_UNAVAILABLE,
-		                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 		break;
 	default:
 		break;
 	}
 
+	if (recheck_available) {
+		nm_device_queue_recheck_available (NM_DEVICE (device),
+		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
+		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	}
+
 	/* Signal scanning state changes */
 	if (   new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING
 	    || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
@@ -2507,62 +2497,6 @@ error:
 
 /****************************************************************************/
 
-static void
-update_permanent_hw_address (NMDevice *device)
-{
-	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	struct ifreq req;
-	struct ethtool_perm_addr *epaddr = NULL;
-	int fd, ret, errsv;
-
-	g_return_if_fail (priv->perm_hw_addr == NULL);
-
-	fd = socket (PF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		_LOGE (LOGD_HW, "could not open control socket.");
-		return;
-	}
-
-	/* Get permanent MAC address */
-	memset (&req, 0, sizeof (struct ifreq));
-	strncpy (req.ifr_name, nm_device_get_iface (device), IFNAMSIZ);
-
-	epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN);
-	epaddr->cmd = ETHTOOL_GPERMADDR;
-	epaddr->size = ETH_ALEN;
-	req.ifr_data = (void *) epaddr;
-
-	errno = 0;
-	ret = ioctl (fd, SIOCETHTOOL, &req);
-	errsv = errno;
-	if ((ret < 0) || !nm_ethernet_address_is_valid (epaddr->data, ETH_ALEN)) {
-		_LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address (error %d)",
-		       errsv);
-		/* Fall back to current address */
-		nm_utils_hwaddr_aton (nm_device_get_hw_address (device), epaddr->data, ETH_ALEN);
-	}
-
-	priv->perm_hw_addr = nm_utils_hwaddr_ntoa (epaddr->data, ETH_ALEN);
-
-	g_free (epaddr);
-	close (fd);
-}
-
-static void
-update_initial_hw_address (NMDevice *device)
-{
-	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
-	/* This sets initial MAC address from current MAC address. It should only
-	 * be called from NMDevice constructor() to really get the initial address.
-	 */
-	priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (device));
-
-	_LOGD (LOGD_DEVICE | LOGD_ETHER, "read initial MAC address %s", priv->initial_hw_addr);
-}
-
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
@@ -2687,9 +2621,9 @@ ensure_hotspot_frequency (NMDeviceWifi *self,
 		return;
 
 	if (g_strcmp0 (band, "a") == 0)
-		freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), a_freqs);
+		freq = nm_platform_wifi_find_frequency (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), a_freqs);
 	else
-		freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), bg_freqs);
+		freq = nm_platform_wifi_find_frequency (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), bg_freqs);
 
 	if (!freq)
 		freq = (g_strcmp0 (band, "a") == 0) ? 5180 : 2462;
@@ -2823,7 +2757,7 @@ act_stage3_ip4_config_start (NMDevice *device,
 
 	/* Indicate that a critical protocol is about to start */
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
-		nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE);
+		nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), TRUE);
 
 	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, reason);
 }
@@ -2846,7 +2780,7 @@ act_stage3_ip6_config_start (NMDevice *device,
 	/* Indicate that a critical protocol is about to start */
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 ||
 	    strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0)
-		nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE);
+		nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), TRUE);
 
 	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, reason);
 }
@@ -2998,7 +2932,7 @@ activation_success_handler (NMDevice *device)
 	g_assert (connection);
 
 	/* Clear any critical protocol notification in the wifi stack */
-	nm_platform_wifi_indicate_addressing_running (ifindex, FALSE);
+	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE);
 
 	/* Clear wireless secrets tries on success */
 	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
@@ -3017,16 +2951,16 @@ activation_success_handler (NMDevice *device)
 	 * But if activation was successful, the card will know the BSSID.  Grab
 	 * the BSSID off the card and fill in the BSSID of the activation AP.
 	 */
-	nm_platform_wifi_get_bssid (ifindex, bssid);
+	nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid);
 	if (!nm_ap_get_address (ap)) {
 		char *bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
 		nm_ap_set_address (ap, bssid_str);
 		g_free (bssid_str);
 	}
 	if (!nm_ap_get_freq (ap))
-		nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (ifindex));
+		nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex));
 	if (!nm_ap_get_max_bitrate (ap))
-		nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (ifindex));
+		nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex));
 
 	tmp_ap = find_active_ap (self, ap, TRUE);
 	if (tmp_ap) {
@@ -3076,7 +3010,7 @@ activation_failure_handler (NMDevice *device)
 	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
 
 	/* Clear any critical protocol notification in the wifi stack */
-	nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), FALSE);
+	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), FALSE);
 }
 
 static void
@@ -3126,7 +3060,7 @@ device_state_changed (NMDevice *device,
 		break;
 	case NM_DEVICE_STATE_IP_CHECK:
 		/* Clear any critical protocol notification in the wifi stack */
-		nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), FALSE);
+		nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), FALSE);
 		break;
 	case NM_DEVICE_STATE_ACTIVATED:
 		activation_success_handler (device);
@@ -3253,12 +3187,6 @@ dispose (GObject *object)
 static void
 finalize (GObject *object)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
-	g_free (priv->perm_hw_addr);
-	g_free (priv->initial_hw_addr);
-
 	G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
 }
 
@@ -3273,7 +3201,7 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_PERM_HW_ADDRESS:
-		g_value_set_string (value, priv->perm_hw_addr);
+		g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (device)));
 		break;
 	case PROP_MODE:
 		g_value_set_uint (value, priv->mode);
@@ -3332,8 +3260,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	object_class->finalize = finalize;
 
 	parent_class->bring_up = bring_up;
-	parent_class->update_permanent_hw_address = update_permanent_hw_address;
-	parent_class->update_initial_hw_address = update_initial_hw_address;
 	parent_class->can_auto_connect = can_auto_connect;
 	parent_class->is_available = is_available;
 	parent_class->check_connection_compatible = check_connection_compatible;
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index 66d73563..d70dd9a6 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -30,6 +30,7 @@
 #include "nm-utils.h"
 #include "nm-logging.h"
 #include "nm-dbus-manager.h"
+#include "nm-core-internal.h"
 
 #include "nm-setting-wireless.h"
 #include "nm-glib-compat.h"
@@ -330,163 +331,134 @@ nm_ap_new (void)
 }
 
 static NM80211ApSecurityFlags
-pair_to_flags (const char *str)
+security_from_vardict (GVariant *security)
 {
-	g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE);
+	NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
+	const char **array, *tmp;
 
-	if (strcmp (str, "tkip") == 0)
-		return NM_802_11_AP_SEC_PAIR_TKIP;
-	if (strcmp (str, "ccmp") == 0)
-		return NM_802_11_AP_SEC_PAIR_CCMP;
-	return NM_802_11_AP_SEC_NONE;
-}
+	g_return_val_if_fail (g_variant_is_of_type (security, G_VARIANT_TYPE_VARDICT), NM_802_11_AP_SEC_NONE);
 
-static NM80211ApSecurityFlags
-group_to_flags (const char *str)
-{
-	g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE);
-
-	if (strcmp (str, "wep40") == 0)
-		return NM_802_11_AP_SEC_GROUP_WEP40;
-	if (strcmp (str, "wep104") == 0)
-		return NM_802_11_AP_SEC_GROUP_WEP104;
-	if (strcmp (str, "tkip") == 0)
-		return NM_802_11_AP_SEC_GROUP_TKIP;
-	if (strcmp (str, "ccmp") == 0)
-		return NM_802_11_AP_SEC_GROUP_CCMP;
-	return NM_802_11_AP_SEC_NONE;
-}
-
-static NM80211ApSecurityFlags
-security_from_dict (GHashTable *security)
-{
-	GValue *value;
-	NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
-	const char **items, **iter;
-
-	value = g_hash_table_lookup (security, "KeyMgmt");
-	if (value) {
-		items = g_value_get_boxed (value);
-		for (iter = items; iter && *iter; iter++) {
-			if (strcmp (*iter, "wpa-psk") == 0)
-				flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
-			else if (strcmp (*iter, "wpa-eap") == 0)
-				flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
-		}
+	if (g_variant_lookup (security, "KeyMgmt", "^a&s", &array)) {
+		if (_nm_utils_string_in_list ("wpa-psk", array))
+			flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
+		if (_nm_utils_string_in_list ("wpa-eap", array))
+			flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+		g_free (array);
 	}
 
-	value = g_hash_table_lookup (security, "Pairwise");
-	if (value) {
-		items = g_value_get_boxed (value);
-		for (iter = items; iter && *iter; iter++)
-			flags |= pair_to_flags (*iter);
+	if (g_variant_lookup (security, "Pairwise", "^a&s", &array)) {
+		if (_nm_utils_string_in_list ("tkip", array))
+			flags |= NM_802_11_AP_SEC_PAIR_TKIP;
+		if (_nm_utils_string_in_list ("ccmp", array))
+			flags |= NM_802_11_AP_SEC_PAIR_CCMP;
+		g_free (array);
 	}
 
-	value = g_hash_table_lookup (security, "Group");
-	if (value)
-		flags |= group_to_flags (g_value_get_string (value));
+	if (g_variant_lookup (security, "Group", "&s", &tmp)) {
+		if (strcmp (tmp, "wep40") == 0)
+			flags |= NM_802_11_AP_SEC_GROUP_WEP40;
+		if (strcmp (tmp, "wep104") == 0)
+			flags |= NM_802_11_AP_SEC_GROUP_WEP104;
+		if (strcmp (tmp, "tkip") == 0)
+			flags |= NM_802_11_AP_SEC_GROUP_TKIP;
+		if (strcmp (tmp, "ccmp") == 0)
+			flags |= NM_802_11_AP_SEC_GROUP_CCMP;
+	}
 
 	return flags;
 }
 
-static void
-foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
-{
-	GValue *variant = (GValue *) value;
-	NMAccessPoint *ap = (NMAccessPoint *) user_data;
-
-	if (G_VALUE_HOLDS_BOXED (variant)) {
-		GArray *array = g_value_get_boxed (variant);
-
-		if (!strcmp (key, "SSID")) {
-			guint32 len = MIN (32, array->len);
-
-			/* Stupid ieee80211 layer uses <hidden> */
-			if (((len == 8) || (len == 9))
-				&& (memcmp (array->data, "<hidden>", 8) == 0))
-				return;
-
-			if (nm_utils_is_empty_ssid ((const guint8 *) array->data, len))
-				return;
-
-			nm_ap_set_ssid (ap, (const guint8 *) array->data, len);
-		} else if (!strcmp (key, "BSSID")) {
-			char *addr;
-
-			if (array->len != ETH_ALEN)
-				return;
-			addr = nm_utils_hwaddr_ntoa (array->data, array->len);
-			nm_ap_set_address (ap, addr);
-			g_free (addr);
-		} else if (!strcmp (key, "Rates")) {
-			guint32 maxrate = 0;
-			int i;
-
-			/* Find the max AP rate */
-			for (i = 0; i < array->len; i++) {
-				guint32 r = g_array_index (array, guint32, i);
-
-				if (r > maxrate) {
-					maxrate = r;
-					nm_ap_set_max_bitrate (ap, r / 1000);
-				}
-			}
-		} else if (!strcmp (key, "WPA")) {
-			NM80211ApSecurityFlags flags = nm_ap_get_wpa_flags (ap);
-
-			flags |= security_from_dict (g_value_get_boxed (variant));
-			nm_ap_set_wpa_flags (ap, flags);
-		} else if (!strcmp (key, "RSN")) {
-			NM80211ApSecurityFlags flags = nm_ap_get_rsn_flags (ap);
-
-			flags |= security_from_dict (g_value_get_boxed (variant));
-			nm_ap_set_rsn_flags (ap, flags);
-		}
-	} else if (G_VALUE_HOLDS_UINT (variant)) {
-		guint32 val = g_value_get_uint (variant);
-
-		if (!strcmp (key, "Frequency"))
-			nm_ap_set_freq (ap, val);
-	} else if (G_VALUE_HOLDS_INT (variant)) {
-		gint val = g_value_get_int (variant);
-
-		if (!strcmp (key, "Signal"))
-			nm_ap_set_strength (ap, nm_ap_utils_level_to_quality (val));
-	} else if (G_VALUE_HOLDS_STRING (variant)) {
-		const char *val = g_value_get_string (variant);
-
-		if (val && !strcmp (key, "Mode")) {
-			if (strcmp (val, "infrastructure") == 0)
-				nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
-			else if (strcmp (val, "ad-hoc") == 0)
-				nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
-		}
-	} else if (G_VALUE_HOLDS_BOOLEAN (variant)) {
-		gboolean val = g_value_get_boolean (variant);
-
-		if (strcmp (key, "Privacy") == 0) {
-			if (val) {
-				NM80211ApFlags flags = nm_ap_get_flags (ap);
-				nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY);
-			}
-		}
-	}
-}
-
 NMAccessPoint *
-nm_ap_new_from_properties (const char *supplicant_path, GHashTable *properties)
+nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
 {
-	NMAccessPoint *ap;
-	const char *addr;
 	const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 	const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	const char *addr;
+	const guint8 *bytes;
+	NMAccessPoint *ap;
+	GVariant *v;
+	gsize len;
+	gboolean b = FALSE;
+	const char *s;
+	gint16 i16;
+	guint16 u16;
 
 	g_return_val_if_fail (properties != NULL, NULL);
 
 	ap = nm_ap_new ();
 
 	g_object_freeze_notify (G_OBJECT (ap));
-	g_hash_table_foreach (properties, foreach_property_cb, ap);
+
+	if (g_variant_lookup (properties, "Privacy", "b", &b) && b)
+		nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY);
+
+	if (g_variant_lookup (properties, "Mode", "&s", &s)) {
+		if (!g_strcmp0 (s, "infrastructure"))
+			nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+		else if (!g_strcmp0 (s, "ad-hoc"))
+			nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
+	}
+
+	if (g_variant_lookup (properties, "Signal", "n", &i16))
+		nm_ap_set_strength (ap, nm_ap_utils_level_to_quality (i16));
+
+	if (g_variant_lookup (properties, "Frequency", "q", &u16))
+		nm_ap_set_freq (ap, u16);
+
+	v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING);
+	if (v) {
+		bytes = g_variant_get_fixed_array (v, &len, 1);
+		len = MIN (32, len);
+
+		/* Stupid ieee80211 layer uses <hidden> */
+		if (   bytes && len
+		    && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8))
+		    && !nm_utils_is_empty_ssid (bytes, len))
+			nm_ap_set_ssid (ap, bytes, len);
+
+		g_variant_unref (v);
+	}
+
+	v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING);
+	if (v) {
+		bytes = g_variant_get_fixed_array (v, &len, 1);
+		if (len == ETH_ALEN) {
+			char *a;
+
+			a = nm_utils_hwaddr_ntoa (bytes, len);
+			nm_ap_set_address (ap, a);
+			g_free (a);
+		}
+		g_variant_unref (v);
+	}
+
+	v = g_variant_lookup_value (properties, "Rates", G_VARIANT_TYPE ("au")); 
+	if (v) {
+		const guint32 *rates = g_variant_get_fixed_array (v, &len, sizeof (guint32));
+		guint32 maxrate = 0;
+		int i;
+
+		/* Find the max AP rate */
+		for (i = 0; i < len; i++) {
+			if (rates[i] > maxrate) {
+				maxrate = rates[i];
+				nm_ap_set_max_bitrate (ap, rates[i] / 1000);
+			}
+		}
+		g_variant_unref (v);
+	}
+
+	v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT);
+	if (v) {
+		nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | security_from_vardict (v));
+		g_variant_unref (v);
+	}
+
+	v = g_variant_lookup_value (properties, "RSN", G_VARIANT_TYPE_VARDICT);
+	if (v) {
+		nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | security_from_vardict (v));
+		g_variant_unref (v);
+	}
 
 	nm_ap_set_supplicant_path (ap, supplicant_path);
 
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 0abb28fc..8ad9acb7 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -56,7 +56,7 @@ typedef struct {
 GType nm_ap_get_type (void);
 
 NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path,
-                                           GHashTable *properties);
+                                           GVariant *properties);
 NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection);
 void            nm_ap_export_to_dbus    (NMAccessPoint *ap);
 
diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c
index 4093c612..c4b4042e 100644
--- a/src/devices/wifi/nm-wifi-factory.c
+++ b/src/devices/wifi/nm-wifi-factory.c
@@ -23,6 +23,8 @@
 #include <gmodule.h>
 
 #include "nm-device-factory.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-olpc-mesh.h"
 #include "nm-device-wifi.h"
 #include "nm-device-olpc-mesh.h"
 #include "nm-settings-connection.h"
@@ -57,26 +59,25 @@ nm_device_factory_create (GError **error)
 /**************************************************************************/
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
 	if (plink->type == NM_LINK_TYPE_WIFI)
 		return nm_device_wifi_new (plink);
 	else if (plink->type == NM_LINK_TYPE_OLPC_MESH)
 		return nm_device_olpc_mesh_new (plink);
-	return NULL;
+	g_assert_not_reached ();
 }
 
-static NMDeviceType
-get_device_type (NMDeviceFactory *factory)
-{
-	return NM_DEVICE_TYPE_WIFI;
-}
+NM_DEVICE_FACTORY_DECLARE_TYPES (
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_WIFI, NM_LINK_TYPE_OLPC_MESH)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_OLPC_MESH_SETTING_NAME)
+)
 
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
 	factory_iface->new_link = new_link;
-	factory_iface->get_device_type = get_device_type;
+	factory_iface->get_supported_types = get_supported_types;
 }
 
 static void
diff --git a/src/devices/wifi/tests/Makefile.in b/src/devices/wifi/tests/Makefile.in
index e3b7c767..f807ee77 100644
--- a/src/devices/wifi/tests/Makefile.in
+++ b/src/devices/wifi/tests/Makefile.in
@@ -204,13 +204,196 @@ am__tty_colors = { \
     std=''; \
   fi; \
 }
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
 am__DIST_COMMON = $(srcdir)/Makefile.in \
-	$(top_srcdir)/build-aux/depcomp
+	$(top_srcdir)/build-aux/depcomp \
+	$(top_srcdir)/build-aux/test-driver
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -324,6 +507,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
@@ -492,7 +676,7 @@ test_wifi_ap_utils_LDADD = $(top_builddir)/src/libNetworkManager.la
 all: all-am
 
 .SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -653,98 +837,168 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-check-TESTS: $(TESTS)
-	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
-	srcdir=$(srcdir); export srcdir; \
-	list=' $(TESTS) '; \
-	$(am__tty_colors); \
-	if test -n "$$list"; then \
-	  for tst in $$list; do \
-	    if test -f ./$$tst; then dir=./; \
-	    elif test -f $$tst; then dir=; \
-	    else dir="$(srcdir)/"; fi; \
-	    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xpass=`expr $$xpass + 1`; \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=XPASS; \
-	      ;; \
-	      *) \
-		col=$$grn; res=PASS; \
-	      ;; \
-	      esac; \
-	    elif test $$? -ne 77; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xfail=`expr $$xfail + 1`; \
-		col=$$lgn; res=XFAIL; \
-	      ;; \
-	      *) \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=FAIL; \
-	      ;; \
-	      esac; \
-	    else \
-	      skip=`expr $$skip + 1`; \
-	      col=$$blu; res=SKIP; \
-	    fi; \
-	    echo "$${col}$$res$${std}: $$tst"; \
-	  done; \
-	  if test "$$all" -eq 1; then \
-	    tests="test"; \
-	    All=""; \
-	  else \
-	    tests="tests"; \
-	    All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
 	  fi; \
-	  if test "$$failed" -eq 0; then \
-	    if test "$$xfail" -eq 0; then \
-	      banner="$$All$$all $$tests passed"; \
-	    else \
-	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
-	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
-	    fi; \
-	  else \
-	    if test "$$xpass" -eq 0; then \
-	      banner="$$failed of $$all $$tests failed"; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	elif test -n "$$redo_logs"; then \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
 	    else \
-	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
-	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
 	    fi; \
-	  fi; \
-	  dashes="$$banner"; \
-	  skipped=""; \
-	  if test "$$skip" -ne 0; then \
-	    if test "$$skip" -eq 1; then \
-	      skipped="($$skip test was not run)"; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
 	    else \
-	      skipped="($$skip tests were not run)"; \
+	      color_start= color_end=; \
 	    fi; \
-	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$skipped"; \
-	  fi; \
-	  report=""; \
-	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
-	    report="Please report to $(PACKAGE_BUGREPORT)"; \
-	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$report"; \
-	  fi; \
-	  dashes=`echo "$$dashes" | sed s/./=/g`; \
-	  if test "$$failed" -eq 0; then \
-	    col="$$grn"; \
-	  else \
-	    col="$$red"; \
-	  fi; \
-	  echo "$${col}$$dashes$${std}"; \
-	  echo "$${col}$$banner$${std}"; \
-	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
-	  test -z "$$report" || echo "$${col}$$report$${std}"; \
-	  echo "$${col}$$dashes$${std}"; \
-	  test "$$failed" -eq 0; \
-	else :; fi
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all 
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test-wifi-ap-utils.log: test-wifi-ap-utils$(EXEEXT)
+	@p='test-wifi-ap-utils$(EXEEXT)'; \
+	b='test-wifi-ap-utils'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
 
 distdir: $(DISTFILES)
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -801,6 +1055,9 @@ install-strip:
 	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
 	fi
 mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
 
 clean-generic:
 
@@ -896,7 +1153,7 @@ uninstall-am:
 	installcheck-am installdirs maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-compile \
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags tags-am uninstall uninstall-am
+	recheck tags tags-am uninstall uninstall-am
 
 .PRECIOUS: Makefile
 
diff --git a/src/devices/wimax/Makefile.in b/src/devices/wimax/Makefile.in
index 32800fb7..8ad4b4e2 100644
--- a/src/devices/wimax/Makefile.in
+++ b/src/devices/wimax/Makefile.in
@@ -221,6 +221,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -334,6 +335,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/devices/wimax/nm-wimax-factory.c b/src/devices/wimax/nm-wimax-factory.c
index 39e9a287..7dc580fc 100644
--- a/src/devices/wimax/nm-wimax-factory.c
+++ b/src/devices/wimax/nm-wimax-factory.c
@@ -25,6 +25,7 @@
 #include "nm-device-factory.h"
 #include "nm-device-wimax.h"
 #include "nm-platform.h"
+#include "nm-setting-wimax.h"
 
 #define NM_TYPE_WIMAX_FACTORY            (nm_wimax_factory_get_type ())
 #define NM_WIMAX_FACTORY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_WIMAX_FACTORY, NMWimaxFactory))
@@ -55,7 +56,7 @@ nm_device_factory_create (GError **error)
 /**************************************************************************/
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
 	/* FIXME: check udev 'DEVTYPE' instead; but since we only support Intel
 	 * WiMAX devices for now this is appropriate.
@@ -66,17 +67,16 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
 	return (NMDevice *) nm_device_wimax_new (plink);
 }
 
-static NMDeviceType
-get_device_type (NMDeviceFactory *factory)
-{
-	return NM_DEVICE_TYPE_WIMAX;
-}
+NM_DEVICE_FACTORY_DECLARE_TYPES (
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_WIMAX)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIMAX_SETTING_NAME)
+)
 
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
 	factory_iface->new_link = new_link;
-	factory_iface->get_device_type = get_device_type;
+	factory_iface->get_supported_types = get_supported_types;
 }
 
 static void
diff --git a/src/devices/wimax/nm-wimax-util.h b/src/devices/wimax/nm-wimax-util.h
index a1852d55..5fedc6e8 100644
--- a/src/devices/wimax/nm-wimax-util.h
+++ b/src/devices/wimax/nm-wimax-util.h
@@ -23,7 +23,7 @@
 
 #include <glib.h>
 
-#include "nm-utils-internal.h"
+#include "nm-macros-internal.h"
 
 NM_PRAGMA_WARNING_DISABLE("-Wstrict-prototypes")
 #include <WiMaxType.h>
diff --git a/src/devices/wwan/Makefile.in b/src/devices/wwan/Makefile.in
index 1a8aef3b..227c622f 100644
--- a/src/devices/wwan/Makefile.in
+++ b/src/devices/wwan/Makefile.in
@@ -232,6 +232,7 @@ ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -345,6 +346,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
+LOG_DRIVER = @LOG_DRIVER@
 LTLIBICONV = @LTLIBICONV@
 LTLIBINTL = @LTLIBINTL@
 LTLIBOBJS = @LTLIBOBJS@
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index f819d1d7..ca724c04 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -300,19 +300,9 @@ modem_state_cb (NMModem *modem,
 		nm_device_recheck_available_connections (device);
 	}
 
-	if ((dev_state >= NM_DEVICE_STATE_DISCONNECTED) && !nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_UNAVAILABLE,
-		                         NM_DEVICE_STATE_REASON_MODEM_FAILED);
-		return;
-	}
-
-	if ((dev_state == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_DISCONNECTED,
-		                         NM_DEVICE_STATE_REASON_MODEM_AVAILABLE);
-		return;
-	}
+	nm_device_queue_recheck_available (device,
+	                                   NM_DEVICE_STATE_REASON_MODEM_AVAILABLE,
+	                                   NM_DEVICE_STATE_REASON_MODEM_FAILED);
 }
 
 static void
@@ -376,12 +366,24 @@ device_state_changed (NMDevice *device,
 	}
 }
 
-static guint32
+static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *device)
 {
 	return NM_DEVICE_CAP_IS_NON_KERNEL;
 }
 
+static const char *
+get_type_description (NMDevice *device)
+{
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+
+	if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS))
+		return "gsm";
+	if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO))
+		return "cdma";
+	return NM_DEVICE_CLASS (nm_device_modem_parent_class)->get_type_description (device);
+}
+
 static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
@@ -751,6 +753,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 	object_class->constructed = constructed;
 
 	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->get_type_description = get_type_description;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->check_connection_available = check_connection_available;
 	device_class->complete_connection = complete_connection;
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 69eaddf3..3385a6fd 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -32,6 +32,7 @@
 #include "nm-device-private.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-modem-enum-types.h"
+#include "nm-route-manager.h"
 
 G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT)
 
@@ -615,7 +616,7 @@ nm_modem_ip4_pre_commit (NMModem *modem,
 
 		g_assert (address);
 		if (address->plen == 32)
-			nm_platform_link_set_noarp (nm_device_get_ip_ifindex (device));
+			nm_platform_link_set_noarp (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device));
 	}
 }
 
@@ -912,9 +913,9 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 		    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
 			ifindex = nm_device_get_ip_ifindex (device);
 			if (ifindex > 0) {
-				nm_platform_route_flush (ifindex);
-				nm_platform_address_flush (ifindex);
-				nm_platform_link_set_down (ifindex);
+				nm_route_manager_route_flush (nm_route_manager_get (), ifindex);
+				nm_platform_address_flush (NM_PLATFORM_GET, ifindex);
+				nm_platform_link_set_down (NM_PLATFORM_GET, ifindex);
 			}
 		}
 	}
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index 2b9c3d4d..49b0b0af 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -25,9 +25,12 @@
 
 #include "nm-device-factory.h"
 #include "nm-wwan-factory.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-cdma.h"
 #include "nm-modem-manager.h"
 #include "nm-device-modem.h"
 #include "nm-logging.h"
+#include "nm-platform.h"
 
 static GType nm_wwan_factory_get_type (void);
 
@@ -87,10 +90,17 @@ modem_added_cb (NMModemManager *manager,
 }
 
 
-static NMDeviceType
-get_device_type (NMDeviceFactory *factory)
+NM_DEVICE_FACTORY_DECLARE_TYPES (
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_WWAN_ETHERNET)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_GSM_SETTING_NAME, NM_SETTING_CDMA_SETTING_NAME)
+)
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
 {
-	return NM_DEVICE_TYPE_MODEM;
+	g_warn_if_fail (plink->type == NM_LINK_TYPE_WWAN_ETHERNET);
+	*out_ignore = TRUE;
+	return NULL;
 }
 
 static void
@@ -115,7 +125,8 @@ nm_wwan_factory_init (NMWwanFactory *self)
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
-	factory_iface->get_device_type = get_device_type;
+	factory_iface->get_supported_types = get_supported_types;
+	factory_iface->new_link = new_link;
 	factory_iface->start = start;
 }