summary refs log tree commit diff
path: root/src/devices/nm-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/nm-device.c')
-rw-r--r--src/devices/nm-device.c2399
1 files changed, 1424 insertions, 975 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 93782a45..cdd3f7cb 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -19,7 +19,7 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#include "config.h"
+#include "nm-default.h"
 
 #include <netinet/in.h>
 #include <string.h>
@@ -34,7 +34,6 @@
 #include <netlink/route/addr.h>
 #include <linux/if_addr.h>
 
-#include "nm-default.h"
 #include "nm-device.h"
 #include "nm-device-private.h"
 #include "NetworkManagerUtils.h"
@@ -64,6 +63,7 @@
 #include "nm-lldp-listener.h"
 #include "sd-ipv4ll.h"
 #include "nm-audit-manager.h"
+#include "nm-arping-manager.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDevice);
@@ -94,8 +94,7 @@ enum {
 };
 static guint signals[LAST_SIGNAL] = { 0 };
 
-enum {
-	PROP_0,
+NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
 	PROP_UDI,
 	PROP_IFACE,
 	PROP_IP_IFACE,
@@ -132,8 +131,7 @@ enum {
 	PROP_LLDP_NEIGHBORS,
 	PROP_REAL,
 	PROP_SLAVES,
-	LAST_PROP
-};
+);
 
 #define DEFAULT_AUTOCONNECT TRUE
 
@@ -193,10 +191,16 @@ typedef struct {
 	int ifindex;
 } DeleteOnDeactivateData;
 
+typedef void (*ArpingCallback) (NMDevice *, NMIP4Config **, gboolean);
+
+typedef struct {
+	ArpingCallback callback;
+	NMDevice *device;
+	NMIP4Config **configs;
+} ArpingData;
+
 typedef struct _NMDevicePrivate {
 	gboolean in_state_changed;
-	gboolean initialized;
-	gboolean platform_link_initialized;
 
 	guint device_link_changed_id;
 	guint device_ip_link_changed_id;
@@ -234,7 +238,7 @@ typedef struct _NMDevicePrivate {
 	char *        physical_port_id;
 	guint         dev_id;
 
-	gboolean                managed_touched_by_user;
+	NMUnmanagedFlags        unmanaged_mask;
 	NMUnmanagedFlags        unmanaged_flags;
 	gboolean                is_nm_owned; /* whether the device is a device owned and created by NM */
 	DeleteOnDeactivateData *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */
@@ -300,7 +304,6 @@ typedef struct _NMDevicePrivate {
 	NMDhcp4Config * dhcp4_config;
 	guint           dhcp4_restart_id;
 
-	guint           arp_round2_id;
 	PingInfo        gw_ping;
 
 	/* dnsmasq stuff for shared connections */
@@ -315,12 +318,19 @@ typedef struct _NMDevicePrivate {
 	sd_ipv4ll *    ipv4ll;
 	guint          ipv4ll_timeout;
 
+	/* IPv4 DAD stuff */
+	struct {
+		GSList *          dad_list;
+		NMArpingManager * announcing;
+	} arping;
+
 	/* IP6 configuration info */
 	NMIP6Config *  ip6_config;
 	IpState        ip6_state;
 	NMIP6Config *  con_ip6_config; /* config from the setting */
 	NMIP6Config *  wwan_ip6_config;
 	NMIP6Config *  ext_ip6_config; /* Stuff added outside NM */
+	NMIP6Config *  ext_ip6_config_captured; /* Configuration captured from platform. */
 	GSList *       vpn6_configs;   /* VPNs which use this device */
 	gboolean       nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
 	guint32        ip6_mtu;
@@ -390,13 +400,10 @@ static void nm_device_slave_notify_enslave (NMDevice *self, gboolean success);
 static void nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason);
 
 static gboolean addrconf6_start_with_link_ready (NMDevice *self);
-static gboolean dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection);
 static NMActStageReturn linklocal6_start (NMDevice *self);
 
 static void _carrier_wait_check_queued_act_request (NMDevice *self);
 
-static gboolean nm_device_get_default_unmanaged (NMDevice *self);
-
 static const char *_activation_func_to_string (ActivationHandleFunc func);
 static void activation_source_handle_cb (NMDevice *self, int family);
 
@@ -408,10 +415,6 @@ static void _set_state_full (NMDevice *self,
 static gboolean queued_ip4_config_change (gpointer user_data);
 static gboolean queued_ip6_config_change (gpointer user_data);
 
-static void _set_unmanaged_flags (NMDevice *self,
-                                  NMUnmanagedFlags flags,
-                                  gboolean unmanaged);
-
 /***********************************************************/
 
 #define QUEUED_PREFIX "queued state change to "
@@ -446,74 +449,75 @@ state_to_string (NMDeviceState state)
 	return queued_state_to_string (state) + strlen (QUEUED_PREFIX);
 }
 
-NM_UTILS_STRING_LOOKUP_TABLE_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason, NULL,
-	[NM_DEVICE_STATE_REASON_UNKNOWN]                  = "unknown",
-	[NM_DEVICE_STATE_REASON_NONE]                     = "none",
-	[NM_DEVICE_STATE_REASON_NOW_MANAGED]              = "managed",
-	[NM_DEVICE_STATE_REASON_NOW_UNMANAGED]            = "unmanaged",
-	[NM_DEVICE_STATE_REASON_CONFIG_FAILED]            = "config-failed",
-	[NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE]    = "ip-config-unavailable",
-	[NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED]        = "ip-config-expired",
-	[NM_DEVICE_STATE_REASON_NO_SECRETS]               = "no-secrets",
-	[NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT]    = "supplicant-disconnect",
-	[NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED] = "supplicant-config-failed",
-	[NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED]        = "supplicant-failed",
-	[NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT]       = "supplicant-timeout",
-	[NM_DEVICE_STATE_REASON_PPP_START_FAILED]         = "ppp-start-failed",
-	[NM_DEVICE_STATE_REASON_PPP_DISCONNECT]           = "ppp-disconnect",
-	[NM_DEVICE_STATE_REASON_PPP_FAILED]               = "ppp-failed",
-	[NM_DEVICE_STATE_REASON_DHCP_START_FAILED]        = "dhcp-start-failed",
-	[NM_DEVICE_STATE_REASON_DHCP_ERROR]               = "dhcp-error",
-	[NM_DEVICE_STATE_REASON_DHCP_FAILED]              = "dhcp-failed",
-	[NM_DEVICE_STATE_REASON_SHARED_START_FAILED]      = "sharing-start-failed",
-	[NM_DEVICE_STATE_REASON_SHARED_FAILED]            = "sharing-failed",
-	[NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED]      = "autoip-start-failed",
-	[NM_DEVICE_STATE_REASON_AUTOIP_ERROR]             = "autoip-error",
-	[NM_DEVICE_STATE_REASON_AUTOIP_FAILED]            = "autoip-failed",
-	[NM_DEVICE_STATE_REASON_MODEM_BUSY]               = "modem-busy",
-	[NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE]       = "modem-no-dialtone",
-	[NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER]         = "modem-no-carrier",
-	[NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT]       = "modem-dial-timeout",
-	[NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED]        = "modem-dial-failed",
-	[NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED]        = "modem-init-failed",
-	[NM_DEVICE_STATE_REASON_GSM_APN_FAILED]           = "gsm-apn-failed",
-	[NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING] = "gsm-registration-idle",
-	[NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED]  = "gsm-registration-denied",
-	[NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT] = "gsm-registration-timeout",
-	[NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED]  = "gsm-registration-failed",
-	[NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED]     = "gsm-pin-check-failed",
-	[NM_DEVICE_STATE_REASON_FIRMWARE_MISSING]         = "firmware-missing",
-	[NM_DEVICE_STATE_REASON_REMOVED]                  = "removed",
-	[NM_DEVICE_STATE_REASON_SLEEPING]                 = "sleeping",
-	[NM_DEVICE_STATE_REASON_CONNECTION_REMOVED]       = "connection-removed",
-	[NM_DEVICE_STATE_REASON_USER_REQUESTED]           = "user-requested",
-	[NM_DEVICE_STATE_REASON_CARRIER]                  = "carrier-changed",
-	[NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED]       = "connection-assumed",
-	[NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE]     = "supplicant-available",
-	[NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND]          = "modem-not-found",
-	[NM_DEVICE_STATE_REASON_BT_FAILED]                = "bluetooth-failed",
-	[NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED]     = "gsm-sim-not-inserted",
-	[NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED]     = "gsm-sim-pin-required",
-	[NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED]     = "gsm-sim-puk-required",
-	[NM_DEVICE_STATE_REASON_GSM_SIM_WRONG]            = "gsm-sim-wrong",
-	[NM_DEVICE_STATE_REASON_INFINIBAND_MODE]          = "infiniband-mode",
-	[NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED]        = "dependency-failed",
-	[NM_DEVICE_STATE_REASON_BR2684_FAILED]            = "br2684-bridge-failed",
-	[NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE] = "modem-manager-unavailable",
-	[NM_DEVICE_STATE_REASON_SSID_NOT_FOUND]           = "ssid-not-found",
-	[NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED] = "secondary-connection-failed",
-	[NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED]          = "dcb-fcoe-failed",
-	[NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED]     = "teamd-control-failed",
-	[NM_DEVICE_STATE_REASON_MODEM_FAILED]             = "modem-failed",
-	[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",
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason,
+	NM_UTILS_LOOKUP_DEFAULT (NULL),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_UNKNOWN,                        "unknown"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NONE,                           "none"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NOW_MANAGED,                    "managed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NOW_UNMANAGED,                  "unmanaged"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_CONFIG_FAILED,                  "config-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE,          "ip-config-unavailable"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED,              "ip-config-expired"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NO_SECRETS,                     "no-secrets"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT,          "supplicant-disconnect"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED,       "supplicant-config-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED,              "supplicant-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT,             "supplicant-timeout"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PPP_START_FAILED,               "ppp-start-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PPP_DISCONNECT,                 "ppp-disconnect"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PPP_FAILED,                     "ppp-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_DHCP_START_FAILED,              "dhcp-start-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_DHCP_ERROR,                     "dhcp-error"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_DHCP_FAILED,                    "dhcp-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SHARED_START_FAILED,            "sharing-start-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SHARED_FAILED,                  "sharing-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED,            "autoip-start-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_AUTOIP_ERROR,                   "autoip-error"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_AUTOIP_FAILED,                  "autoip-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_BUSY,                     "modem-busy"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE,             "modem-no-dialtone"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER,               "modem-no-carrier"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT,             "modem-dial-timeout"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED,              "modem-dial-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED,              "modem-init-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_APN_FAILED,                 "gsm-apn-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING, "gsm-registration-idle"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED,        "gsm-registration-denied"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT,       "gsm-registration-timeout"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED,        "gsm-registration-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED,           "gsm-pin-check-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_FIRMWARE_MISSING,               "firmware-missing"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_REMOVED,                        "removed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SLEEPING,                       "sleeping"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_CONNECTION_REMOVED,             "connection-removed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_USER_REQUESTED,                 "user-requested"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_CARRIER,                        "carrier-changed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED,             "connection-assumed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,           "supplicant-available"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND,                "modem-not-found"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_BT_FAILED,                      "bluetooth-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED,           "gsm-sim-not-inserted"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED,           "gsm-sim-pin-required"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED,           "gsm-sim-puk-required"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_GSM_SIM_WRONG,                  "gsm-sim-wrong"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_INFINIBAND_MODE,                "infiniband-mode"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED,              "dependency-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_BR2684_FAILED,                  "br2684-bridge-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE,      "modem-manager-unavailable"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SSID_NOT_FOUND,                 "ssid-not-found"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED,    "secondary-connection-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED,                "dcb-fcoe-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED,           "teamd-control-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_FAILED,                   "modem-failed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_MODEM_AVAILABLE,                "modem-available"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT,              "sim-pin-incorrect"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NEW_ACTIVATION,                 "new-activation"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_CHANGED,                 "parent-changed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED,         "parent-managed-changed"),
 );
 
 #define reason_to_string(reason) \
-	NM_UTILS_STRING_LOOKUP_TABLE (_reason_to_string, reason)
+	NM_UTILS_LOOKUP_STR (_reason_to_string, reason)
 
 /***********************************************************/
 
@@ -649,7 +653,7 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 
 	/* Emit change notification */
 	if (g_strcmp0 (old_ip_iface, priv->ip_iface))
-		g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
+		_notify (self, PROP_IP_IFACE);
 	g_free (old_ip_iface);
 }
 
@@ -772,6 +776,7 @@ nm_device_get_priority (NMDevice *self)
 	switch (nm_device_get_device_type (self)) {
 	/* 50 is reserved for VPN (NM_VPN_ROUTE_METRIC_DEFAULT) */
 	case NM_DEVICE_TYPE_ETHERNET:
+	case NM_DEVICE_TYPE_VETH:
 		return 100;
 	case NM_DEVICE_TYPE_INFINIBAND:
 		return 150;
@@ -981,6 +986,26 @@ nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFla
 	return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request, compare_flags);
 }
 
+NMSetting *
+nm_device_get_applied_setting (NMDevice *device, GType setting_type)
+{
+	NMActRequest *req;
+	NMSetting *setting = NULL;
+
+	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+	req = nm_device_get_act_request (device);
+	if (req) {
+		NMConnection *connection;
+
+		connection = nm_act_request_get_applied_connection (req);
+		if (connection)
+			setting = nm_connection_get_setting (connection, setting_type);
+	}
+
+	return setting;
+}
+
 RfKillType
 nm_device_get_rfkill_type (NMDevice *self)
 {
@@ -1158,6 +1183,7 @@ nm_device_master_release_one_slave (NMDevice *self, NMDevice *slave, gboolean co
 	 * when slaves change.
 	 */
 	nm_device_update_hw_address (self);
+	nm_device_set_unmanaged_by_flags (slave, NM_UNMANAGED_IS_SLAVE, NM_UNMAN_FLAG_OP_FORGET, NM_DEVICE_STATE_REASON_REMOVED);
 }
 
 /**
@@ -1173,54 +1199,6 @@ can_unmanaged_external_down (NMDevice *self)
 	return nm_device_is_software (self) && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
 }
 
-/**
- * nm_device_finish_init:
- * @self: the master device
- *
- * Whatever needs to be done post-initialization, when the device has a DBus
- * object name.
- */
-void
-nm_device_finish_init (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	g_assert (priv->initialized == FALSE);
-
-	/* Do not manage externally created software devices until they are IFF_UP */
-	if (   NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)
-	    && priv->ifindex > 0
-	    && (   !priv->up
-	        || !priv->platform_link_initialized))
-		nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE);
-
-	if (priv->master)
-		nm_device_master_enslave_slave (priv->master, self, NULL);
-
-	if (priv->ifindex > 0) {
-		if (priv->ifindex == 1) {
-			/* Unmanaged the loopback device with an explicit NM_UNMANAGED_LOOPBACK flag.
-			 * Later we might want to manage 'lo' too. Currently that doesn't work because
-			 * NetworkManager might down the interface or remove the 127.0.0.1 address. */
-			nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_LOOPBACK, TRUE);
-		} else if (priv->platform_link_initialized || (priv->is_nm_owned && nm_device_is_software (self))) {
-			gboolean platform_unmanaged = FALSE;
-
-			if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged))
-				nm_device_set_unmanaged_flags_initial (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_unmanaged_flags_initial (self, NM_UNMANAGED_PLATFORM_INIT, TRUE);
-		}
-	}
-
-	priv->initialized = TRUE;
-}
-
 static void
 update_dynamic_ip_setup (NMDevice *self)
 {
@@ -1272,7 +1250,7 @@ carrier_changed (NMDevice *self, gboolean carrier)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (!nm_device_get_managed (self))
+	if (priv->state <= NM_DEVICE_STATE_UNMANAGED)
 		return;
 
 	nm_device_recheck_available_connections (self);
@@ -1304,8 +1282,6 @@ carrier_changed (NMDevice *self, gboolean carrier)
 	}
 
 	if (carrier) {
-		g_warn_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE);
-
 		if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) {
 			nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED,
 			                       NM_DEVICE_STATE_REASON_CARRIER);
@@ -1324,8 +1300,6 @@ carrier_changed (NMDevice *self, gboolean carrier)
 			update_dynamic_ip_setup (self);
 		}
 	} else {
-		g_return_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE);
-
 		if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) {
 			if (nm_device_queued_state_peek (self) >= NM_DEVICE_STATE_DISCONNECTED)
 				nm_device_queued_state_clear (self);
@@ -1378,7 +1352,7 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier)
 		return;
 
 	priv->carrier = carrier;
-	g_object_notify (G_OBJECT (self), NM_DEVICE_CARRIER);
+	_notify (self, PROP_CARRIER);
 
 	if (priv->carrier) {
 		_LOGI (LOGD_DEVICE, "link connected");
@@ -1450,7 +1424,6 @@ device_link_changed (NMDevice *self)
 	NMPlatformLink info;
 	const NMPlatformLink *pllink;
 	int ifindex;
-	gboolean just_initialized = FALSE;
 	gboolean was_up;
 
 	priv->device_link_changed_id = 0;
@@ -1467,26 +1440,26 @@ device_link_changed (NMDevice *self)
 		/* Update UDI to what udev gives us */
 		g_free (priv->udi);
 		priv->udi = g_strdup (udi);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
+		_notify (self, PROP_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);
+		_notify (self, PROP_DRIVER);
 	}
 
 	/* Update MTU if it has changed. */
 	if (priv->mtu != info.mtu) {
 		priv->mtu = info.mtu;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_MTU);
+		_notify (self, PROP_MTU);
 	}
 
 	if (info.driver && g_strcmp0 (priv->driver, info.driver) != 0) {
 		g_free (priv->driver);
 		priv->driver = g_strdup (info.driver);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER);
+		_notify (self, PROP_DRIVER);
 	}
 
 	if (info.name[0] && strcmp (priv->iface, info.name) != 0) {
@@ -1498,9 +1471,9 @@ device_link_changed (NMDevice *self)
 		/* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */
 		ip_ifname_changed = !priv->ip_iface;
 
-		g_object_notify (G_OBJECT (self), NM_DEVICE_IFACE);
+		_notify (self, PROP_IFACE);
 		if (ip_ifname_changed)
-			g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
+			_notify (self, PROP_IP_IFACE);
 
 		/* Re-match available connections against the new interface name */
 		nm_device_recheck_available_connections (self);
@@ -1524,71 +1497,62 @@ device_link_changed (NMDevice *self)
 	if (ip_ifname_changed)
 		update_dynamic_ip_setup (self);
 
-	if (priv->ifindex > 0 && !priv->platform_link_initialized && info.initialized) {
-		gboolean platform_unmanaged = FALSE;
-
-		priv->platform_link_initialized = TRUE;
-
-		if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged)) {
-			nm_device_set_unmanaged_flags (self,
-			                               NM_UNMANAGED_DEFAULT,
-			                               platform_unmanaged,
-			                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
-		}
-
-		nm_device_set_unmanaged_flags (self,
-		                               NM_UNMANAGED_PLATFORM_INIT,
-		                               FALSE,
-		                               NM_DEVICE_STATE_REASON_NOW_MANAGED);
-
-		just_initialized = TRUE;
-	}
-
 	was_up = priv->up;
-	priv->up = NM_FLAGS_HAS (info.flags, IFF_UP);
+	priv->up = NM_FLAGS_HAS (info.n_ifi_flags, IFF_UP);
+
+	if (   priv->ifindex > 0
+	    && info.initialized
+	    && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) {
+		NMDeviceStateReason reason;
+
+		nm_device_set_unmanaged_by_user_udev (self);
+
+		/* If the devices that need an external IFF_UP go managed below,
+		 * it means they're already up. In that case we should use an "assumed"
+		 * reason to prevent the cleanup sequence from being run on transition
+		 * from "unmanaged" to "unavailable". */
+		if (   priv->up
+		    && !nm_device_get_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN)
+		    && NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)) {
+			/* Ensure the assume check is queued before any queued state changes
+			 * from the transition to UNAVAILABLE.
+			 */
+			nm_device_queue_recheck_assume (self);
+			reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED;
+		} else
+			reason = NM_DEVICE_STATE_REASON_NOW_MANAGED;
 
-	if (   priv->platform_link_initialized
-	    && (   just_initialized
-	        || priv->up != was_up)) {
+		nm_device_set_unmanaged_by_flags (self, NM_UNMANAGED_PLATFORM_INIT, FALSE, reason);
+	}
 
+	if (   priv->ifindex > 0
+	    && priv->up != was_up
+	    && NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)) {
 		/* Manage externally-created software interfaces only when they are IFF_UP */
-		g_assert (priv->ifindex > 0);
-		if (NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)) {
-			gboolean external_down = !!nm_device_get_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN);
-
-			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.
-					 */
-					nm_device_queue_recheck_assume (self);
-
-					/* Resetting the EXTERNAL_DOWN flag may change the device's state
-					 * to UNAVAILABLE.  To ensure that the state change doesn't touch
-					 * the device before assumption occurs, pass
-					 * NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED as the reason.
-					 */
-					nm_device_set_unmanaged_flags (self,
-					                               NM_UNMANAGED_EXTERNAL_DOWN,
-					                               FALSE,
-					                               NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
-				} else {
-					/* Don't trigger a state change; if the device is in a
-					 * state higher than UNAVAILABLE, it is already IFF_UP
-					 * or an explicit activation request was received.
-					 */
-					_set_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN, FALSE);
-				}
-			} 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.
-				 */
-				nm_device_set_unmanaged_flags (self,
-				                               NM_UNMANAGED_EXTERNAL_DOWN,
-				                               TRUE,
-				                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
-			}
+		if (   priv->up
+		    && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN)) {
+			/* Ensure the assume check is queued before any queued state changes
+			 * from the transition to UNAVAILABLE.
+			 */
+			nm_device_queue_recheck_assume (self);
 		}
+
+		/* In case of @priv->up, resetting the EXTERNAL_DOWN flag may change the device's
+		 * state to UNAVAILABLE. To ensure that the state change doesn't touch
+		 * the device before assumption occurs, pass NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED
+		 * as the reason.
+		 *
+		 * In case of !@priv->up, and the device is already unmanaged for other reasons, the
+		 * state-change-reason has no effect.
+		 * If the device is managed for an explict user-request, the state-change-reason
+		 * also has no effect, because the device stays managed.
+		 *
+		 * The state-change-reason only has effect if the device was assumed
+		 * and is now to be unmanaged. */
+		nm_device_set_unmanaged_by_flags (self,
+		                                  NM_UNMANAGED_EXTERNAL_DOWN,
+		                                  !priv->up,
+		                                  NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 	}
 
 	device_recheck_slave_status (self, &info);
@@ -1617,7 +1581,7 @@ device_ip_link_changed (NMDevice *self)
 		g_free (priv->ip_iface);
 		priv->ip_iface = g_strdup (pllink->name);
 
-		g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
+		_notify (self, PROP_IP_IFACE);
 		update_dynamic_ip_setup (self);
 	}
 	return G_SOURCE_REMOVE;
@@ -1782,7 +1746,11 @@ nm_device_create_and_realize (NMDevice *self,
 	realize_start_setup (self, plink);
 	nm_device_realize_finish (self, plink);
 
-	g_return_val_if_fail (nm_device_check_connection_compatible (self, connection), TRUE);
+	if (nm_device_get_managed (self, FALSE)) {
+		nm_device_state_changed (self,
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         NM_DEVICE_STATE_REASON_NOW_MANAGED);
+	}
 	return TRUE;
 }
 
@@ -1798,25 +1766,24 @@ update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
 	if (udi && !g_strcmp0 (udi, priv->udi)) {
 		g_free (priv->udi);
 		priv->udi = g_strdup (udi);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
+		_notify (self, PROP_UDI);
 	}
 
 	if (!g_strcmp0 (plink->name, priv->iface)) {
 		g_free (priv->iface);
 		priv->iface = g_strdup (plink->name);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_IFACE);
+		_notify (self, PROP_IFACE);
 	}
 
 	priv->ifindex = plink->ifindex;
-	g_object_notify (G_OBJECT (self), NM_DEVICE_IFINDEX);
+	_notify (self, PROP_IFINDEX);
 
-	priv->up = NM_FLAGS_HAS (plink->flags, IFF_UP);
+	priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP);
 	if (plink->driver && g_strcmp0 (plink->driver, priv->driver) != 0) {
 		g_free (priv->driver);
 		priv->driver = g_strdup (plink->driver);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER);
+		_notify (self, PROP_DRIVER);
 	}
-	priv->platform_link_initialized = plink->initialized;
 }
 
 static void
@@ -1874,6 +1841,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 
 	/* The device should not be realized */
 	g_return_if_fail (!priv->real);
+	g_return_if_fail (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT));
 	g_return_if_fail (priv->ip_ifindex <= 0);
 	g_return_if_fail (priv->ip_iface == NULL);
 
@@ -1891,7 +1859,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 
 	if (priv->ifindex > 0) {
 		priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID);
+		_notify (self, PROP_PHYSICAL_PORT_ID);
 
 		priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex);
 
@@ -1899,7 +1867,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 			priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
 
 		priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_MTU);
+		_notify (self, PROP_MTU);
 
 		nm_platform_link_get_driver_info (NM_PLATFORM_GET,
 		                                  priv->ifindex,
@@ -1907,9 +1875,9 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 		                                  &priv->driver_version,
 		                                  &priv->firmware_version);
 		if (priv->driver_version)
-			g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER_VERSION);
+			_notify (self, PROP_DRIVER_VERSION);
 		if (priv->firmware_version)
-			g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_VERSION);
+			_notify (self, PROP_FIRMWARE_VERSION);
 
 		if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET))
 			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, priv->ifindex);
@@ -1921,7 +1889,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 	if (!priv->udi) {
 		/* Use a placeholder UDI until we get a real one */
 		priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
+		_notify (self, PROP_UDI);
 	}
 
 	/* trigger initial ip config change to initialize ip-config */
@@ -1951,9 +1919,26 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 		priv->carrier = TRUE;
 	}
 
-	g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES);
+	_notify (self, PROP_CAPABILITIES);
 
 	klass->realize_start_notify (self, plink);
+
+	/* Do not manage externally created software devices until they are IFF_UP */
+	if (   priv->ifindex > 0
+	    && plink
+	    && !priv->up
+	    && NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self))
+		nm_device_set_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE);
+
+	/* Unmanaged the loopback device with an explicit NM_UNMANAGED_LOOPBACK flag.
+	 * Later we might want to manage 'lo' too. Currently that doesn't work because
+	 * NetworkManager might down the interface or remove the 127.0.0.1 address. */
+	nm_device_set_unmanaged_flags (self, NM_UNMANAGED_LOOPBACK, priv->ifindex == 1);
+
+	nm_device_set_unmanaged_by_user_udev (self);
+
+	nm_device_set_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT,
+	                               plink && !plink->initialized);
 }
 
 /**
@@ -1977,17 +1962,15 @@ nm_device_realize_finish (NMDevice *self, const NMPlatformLink *plink)
 
 	g_return_if_fail (!priv->real);
 
-	if (plink) {
-		update_device_from_platform_link (self, plink);
+	if (plink)
 		device_recheck_slave_status (self, plink);
-	}
 
 	priv->real = TRUE;
-	g_object_notify (G_OBJECT (self), NM_DEVICE_REAL);
+	_notify (self, PROP_REAL);
 
 	nm_device_recheck_available_connections (self);
 
-	/* Balanced by a freeze in realize_start_setup() */
+	/* Balanced by a freeze in realize_start_setup(). */
 	g_object_thaw_notify (G_OBJECT (self));
 }
 
@@ -2000,7 +1983,7 @@ unrealize_notify (NMDevice *self)
 }
 
 static gboolean
-available_connection_check_delete_unrealized_on_idle (gpointer user_data)
+available_connections_check_delete_unrealized_on_idle (gpointer user_data)
 {
 	NMDevice *self = user_data;
 	NMDevicePrivate *priv;
@@ -2019,7 +2002,7 @@ available_connection_check_delete_unrealized_on_idle (gpointer user_data)
 }
 
 static void
-available_connection_check_delete_unrealized (NMDevice *self)
+available_connections_check_delete_unrealized (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
@@ -2028,7 +2011,7 @@ available_connection_check_delete_unrealized (NMDevice *self)
 
 	if (   g_hash_table_size (priv->available_connections) == 0
 	    && !nm_device_is_real (self))
-		priv->check_delete_unrealized_id = g_idle_add (available_connection_check_delete_unrealized_on_idle, self);
+		priv->check_delete_unrealized_id = g_idle_add (available_connections_check_delete_unrealized_on_idle, self);
 }
 
 /**
@@ -2078,32 +2061,32 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 
 	if (priv->ifindex > 0) {
 		priv->ifindex = 0;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_IFINDEX);
+		_notify (self, PROP_IFINDEX);
 	}
 	priv->ip_ifindex = 0;
 	if (priv->ip_iface) {
 		g_clear_pointer (&priv->ip_iface, g_free);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
+		_notify (self, PROP_IP_IFACE);
 	}
 	if (priv->driver_version) {
 		g_clear_pointer (&priv->driver_version, g_free);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER_VERSION);
+		_notify (self, PROP_DRIVER_VERSION);
 	}
 	if (priv->firmware_version) {
 		g_clear_pointer (&priv->firmware_version, g_free);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_VERSION);
+		_notify (self, PROP_FIRMWARE_VERSION);
 	}
 	if (priv->udi) {
 		g_clear_pointer (&priv->udi, g_free);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
+		_notify (self, PROP_UDI);
 	}
 	if (priv->hw_addr) {
 		g_clear_pointer (&priv->hw_addr, g_free);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS);
+		_notify (self, PROP_HW_ADDRESS);
 	}
 	if (priv->physical_port_id) {
 		g_clear_pointer (&priv->physical_port_id, g_free);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID);
+		_notify (self, PROP_PHYSICAL_PORT_ID);
 	}
 
 	g_clear_pointer (&priv->perm_hw_addr, g_free);
@@ -2112,15 +2095,27 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 	priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
 	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
 		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES);
+	_notify (self, PROP_CAPABILITIES);
 
 	priv->real = FALSE;
-	g_object_notify (G_OBJECT (self), NM_DEVICE_REAL);
+	_notify (self, PROP_REAL);
 
 	nm_device_set_autoconnect (self, DEFAULT_AUTOCONNECT);
 
 	g_object_thaw_notify (G_OBJECT (self));
 
+	nm_device_set_unmanaged_flags (self,
+	                               NM_UNMANAGED_PLATFORM_INIT,
+	                               TRUE);
+
+	nm_device_set_unmanaged_flags (self,
+	                               NM_UNMANAGED_PARENT |
+	                               NM_UNMANAGED_LOOPBACK |
+	                               NM_UNMANAGED_USER_UDEV |
+	                               NM_UNMANAGED_EXTERNAL_DOWN |
+	                               NM_UNMANAGED_IS_SLAVE,
+	                               NM_UNMAN_FLAG_OP_FORGET);
+
 	nm_device_state_changed (self,
 	                         NM_DEVICE_STATE_UNMANAGED,
 	                         remove_resources ?
@@ -2295,10 +2290,13 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure)
 
 		/* no need to emit
 		 *
-		 *   g_object_notify (G_OBJECT (slave), NM_DEVICE_MASTER);
+		 *   _notify (slave, PROP_MASTER);
 		 *
 		 * because slave_priv->is_enslaved is not true, thus the value
 		 * didn't change yet. */
+
+		g_warn_if_fail (!NM_FLAGS_HAS (slave_priv->unmanaged_mask, NM_UNMANAGED_IS_SLAVE));
+		nm_device_set_unmanaged_by_flags (slave, NM_UNMANAGED_IS_SLAVE, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 	} else
 		g_return_if_fail (slave_priv->master == self);
 
@@ -2476,8 +2474,8 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success)
 				_LOGI (LOGD_DEVICE, "enslaved to %s", nm_device_get_iface (priv->master));
 
 			priv->is_enslaved = TRUE;
-			g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
-			g_object_notify (G_OBJECT (priv->master), NM_DEVICE_SLAVES);
+			_notify (self, PROP_MASTER);
+			_notify (priv->master, PROP_SLAVES);
 		} else if (activating) {
 			_LOGW (LOGD_DEVICE, "Activation: connection '%s' could not be enslaved",
 			       nm_connection_get_id (connection));
@@ -2535,8 +2533,8 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 
 	if (priv->is_enslaved) {
 		priv->is_enslaved = FALSE;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
-		g_object_notify (G_OBJECT (priv->master), NM_DEVICE_SLAVES);
+		_notify (self, PROP_MASTER);
+		_notify (priv->master, PROP_SLAVES);
 	}
 }
 
@@ -2662,19 +2660,12 @@ nm_device_set_autoconnect (NMDevice *self, gboolean autoconnect)
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	if (priv->autoconnect == autoconnect)
-		return;
+	autoconnect = !!autoconnect;
 
-	if (autoconnect) {
-		/* Default-unmanaged devices never autoconnect */
-		if (!nm_device_get_default_unmanaged (self)) {
-			priv->autoconnect = TRUE;
-			g_object_notify (G_OBJECT (self), NM_DEVICE_AUTOCONNECT);
-		}
-	} else {
-		priv->autoconnect = FALSE;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_AUTOCONNECT);
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (priv->autoconnect != autoconnect) {
+		priv->autoconnect = autoconnect;
+		_notify (self, PROP_AUTOCONNECT);
 	}
 }
 
@@ -2980,15 +2971,18 @@ nm_device_complete_connection (NMDevice *self,
 static gboolean
 check_connection_compatible (NMDevice *self, NMConnection *connection)
 {
-	NMSettingConnection *s_con;
-	const char *config_iface, *device_iface;
+	const char *device_iface = nm_device_get_iface (self);
+	gs_free char *conn_iface = nm_manager_get_connection_iface (nm_manager_get (),
+	                                                            connection,
+	                                                            NULL, NULL);
 
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
+	/* We always need a interface name for virtual devices, but for
+	 * physical ones a connection without interface name is fine for
+	 * any device. */
+	if (!conn_iface)
+		return !nm_connection_is_virtual (connection);
 
-	config_iface = nm_setting_connection_get_interface_name (s_con);
-	device_iface = nm_device_get_iface (self);
-	if (config_iface && strcmp (config_iface, device_iface) != 0)
+	if (strcmp (conn_iface, device_iface) != 0)
 		return FALSE;
 
 	return TRUE;
@@ -3401,7 +3395,7 @@ lldp_neighbors_changed (NMLldpListener *lldp_listener, GParamSpec *pspec,
 {
 	NMDevice *self = NM_DEVICE (user_data);
 
-	g_object_notify (G_OBJECT (self), NM_DEVICE_LLDP_NEIGHBORS);
+	_notify (self, PROP_LLDP_NEIGHBORS);
 }
 
 static gboolean
@@ -3457,7 +3451,7 @@ activate_stage1_device_prepare (NMDevice *self)
 	priv->ip4_state = priv->ip6_state = IP_NONE;
 
 	/* Notify the new ActiveConnection along with the state change */
-	g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION);
+	_notify (self, PROP_ACTIVE_CONNECTION);
 
 	nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
 
@@ -3629,12 +3623,12 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
 }
 
 /*
- * nm_device_check_ip_failed
+ * check_ip_failed
  *
  * Progress the device to appropriate state if both IPv4 and IPv6 failed
  */
 static void
-nm_device_check_ip_failed (NMDevice *self, gboolean may_fail)
+check_ip_failed (NMDevice *self, gboolean may_fail)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMDeviceState state;
@@ -3664,6 +3658,212 @@ nm_device_check_ip_failed (NMDevice *self, gboolean may_fail)
 	                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 }
 
+/*
+ * check_ip_done
+ *
+ * Progress the device to ip connectivity check state if IPv4 or IPv6 succeeded
+ */
+static void
+check_ip_done (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG)
+		return;
+
+	if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET))
+		return;
+
+	if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6))
+		return;
+
+	nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
+}
+
+/*********************************************/
+/* IPv4 DAD stuff */
+
+static guint
+get_ipv4_dad_timeout (NMDevice *self)
+{
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip4 = NULL;
+	gs_free char *value = NULL;
+	gint ret = 0;
+
+	connection = nm_device_get_applied_connection (self);
+	if (connection)
+		s_ip4 = nm_connection_get_setting_ip4_config (connection);
+
+	if (s_ip4) {
+		ret = nm_setting_ip_config_get_dad_timeout (s_ip4);
+
+		if (ret < 0) {
+			value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+			                                               "ipv4.dad-timeout", self);
+			ret = _nm_utils_ascii_str_to_int64 (value, 10, -1,
+			                                    NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX,
+			                                    -1);
+			ret = ret < 0 ? 0 : ret;
+		}
+	}
+
+	return ret;
+}
+
+static void
+arping_data_destroy (gpointer ptr, GClosure *closure)
+{
+	ArpingData *data = ptr;
+	int i;
+
+	if (data) {
+		for (i = 0; data->configs && data->configs[i]; i++)
+			g_object_unref (data->configs[i]);
+		g_free (data->configs);
+		g_slice_free (ArpingData, data);
+	}
+}
+
+static void
+ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean success)
+{
+	NMIP4Config *empty;
+
+	if (success) {
+		empty = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
+		nm_device_activate_schedule_ip4_config_result (self, empty);
+		g_object_unref (empty);
+	} else {
+		nm_device_queue_state (self, NM_DEVICE_STATE_FAILED,
+		                       NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+	}
+}
+
+static void
+arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *data)
+{
+	NMDevice *self;
+	NMDevicePrivate *priv;
+	const NMPlatformIP4Address *address;
+	gboolean result, success = TRUE;
+	int i, j;
+
+	g_assert (data);
+	self = data->device;
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	for (i = 0; data->configs && data->configs[i]; i++) {
+		for (j = 0; j < nm_ip4_config_get_num_addresses (data->configs[i]); j++) {
+			address = nm_ip4_config_get_address (data->configs[i], j);
+			result = nm_arping_manager_check_address (arping_manager, address->address);
+			success &= result;
+
+			_NMLOG (result ? LOGL_DEBUG : LOGL_WARN,
+			        LOGD_DEVICE,
+			        "IPv4 DAD result: address %s is %s",
+			        nm_utils_inet4_ntop (address->address, NULL),
+			        result ? "unique" : "duplicate");
+		}
+	}
+
+	data->callback (self, data->configs, success);
+
+	priv->arping.dad_list = g_slist_remove (priv->arping.dad_list, arping_manager);
+	nm_arping_manager_destroy (arping_manager);
+}
+
+/**
+ * ipv4_dad_start:
+ * @self: device instance
+ * @configs: NULL-terminated array of IPv4 configurations
+ * @cb: callback function
+ *
+ * Start IPv4 DAD on device @self, check addresses in @configs and call @cb
+ * when the procedure ends. @cb will be called in any case, even if DAD can't
+ * be started. @configs will be unreferenced after @cb has been called.
+ */
+static void
+ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMArpingManager *arping_manager;
+	const NMPlatformIP4Address *address;
+	ArpingData *data;
+	guint timeout;
+	gboolean ret, addr_found;
+	const guint8 *hw_addr;
+	size_t hw_addr_len = 0;
+	GError *error = NULL;
+	guint i, j;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_if_fail (configs);
+	g_return_if_fail (cb);
+
+	for (i = 0, addr_found = FALSE; configs[i]; i++) {
+		if (nm_ip4_config_get_num_addresses (configs[i]) > 0) {
+			addr_found = TRUE;
+			break;
+		}
+	}
+
+	timeout = get_ipv4_dad_timeout (self);
+	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET,
+	                                        nm_device_get_ip_ifindex (self),
+	                                        &hw_addr_len);
+
+	if (   !timeout
+	    || !hw_addr
+	    || !hw_addr_len
+	    || !addr_found
+	    || nm_device_uses_assumed_connection (self)) {
+
+		/* DAD not needed, signal success */
+		cb (self, configs, TRUE);
+
+		for (i = 0; configs[i]; i++)
+			g_object_unref (configs[i]);
+		g_free (configs);
+
+		return;
+	}
+
+	/* don't take additional references of @arping_manager that outlive @self.
+	 * Otherwise, the callback can be invoked on a dangling pointer as we don't
+	 * disconnect the handler. */
+	arping_manager = nm_arping_manager_new (nm_device_get_ip_ifindex (self));
+	priv->arping.dad_list = g_slist_append (priv->arping.dad_list, arping_manager);
+
+	data = g_slice_new0 (ArpingData);
+	data->configs = configs;
+	data->callback = cb;
+	data->device = self;
+
+	for (i = 0; configs[i]; i++) {
+		for (j = 0; j < nm_ip4_config_get_num_addresses (configs[i]); j++) {
+			address = nm_ip4_config_get_address (configs[i], j);
+			nm_arping_manager_add_address (arping_manager, address->address);
+		}
+	}
+
+	g_signal_connect_data (arping_manager, NM_ARPING_MANAGER_PROBE_TERMINATED,
+	                       G_CALLBACK (arping_manager_probe_terminated), data,
+	                       arping_data_destroy, 0);
+
+	ret = nm_arping_manager_start_probe (arping_manager, timeout, &error);
+
+	if (!ret) {
+		_LOGW (LOGD_DEVICE, "arping probe failed: %s", error->message);
+
+		/* DAD could not be started, signal success */
+		cb (self, configs, TRUE);
+
+		priv->arping.dad_list = g_slist_remove (priv->arping.dad_list, arping_manager);
+		nm_arping_manager_destroy (arping_manager);
+	}
+}
+
 /*********************************************/
 /* IPv4LL stuff */
 
@@ -3738,14 +3938,14 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 		if (r < 0) {
 			_LOGE (LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r);
 			priv->ip4_state = IP_FAIL;
-			nm_device_check_ip_failed (self, FALSE);
+			check_ip_failed (self, FALSE);
 			return;
 		}
 
 		if ((address.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
 			_LOGE (LOGD_AUTOIP4, "invalid address %08x received (not link-local).", address.s_addr);
 			priv->ip4_state = IP_FAIL;
-			nm_device_check_ip_failed (self, FALSE);
+			check_ip_failed (self, FALSE);
 			return;
 		}
 
@@ -3753,7 +3953,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 		if (config == NULL) {
 			_LOGE (LOGD_AUTOIP4, "failed to get IPv4LL config");
 			priv->ip4_state = IP_FAIL;
-			nm_device_check_ip_failed (self, FALSE);
+			check_ip_failed (self, FALSE);
 			return;
 		}
 
@@ -3764,7 +3964,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 			if (!ip4_config_merge_and_apply (self, config, TRUE, NULL)) {
 				_LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change.");
 				priv->ip4_state = IP_FAIL;
-				nm_device_check_ip_failed (self, FALSE);
+				check_ip_failed (self, FALSE);
 			}
 		} else
 			g_assert_not_reached ();
@@ -3774,7 +3974,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 	default:
 		_LOGW (LOGD_AUTOIP4, "IPv4LL address no longer valid after event %d.", event);
 		priv->ip4_state = IP_FAIL;
-		nm_device_check_ip_failed (self, FALSE);
+		check_ip_failed (self, FALSE);
 	}
 }
 
@@ -3985,7 +4185,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 
 	if (priv->dhcp4_config) {
 		nm_exported_object_clear_and_unexport (&priv->dhcp4_config);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
+		_notify (self, PROP_DHCP4_CONFIG);
 	}
 }
 
@@ -4156,6 +4356,8 @@ END_ADD_DEFAULT_ROUTE:
 		priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) &priv->default_route.v4);
 	}
 
+	nm_ip4_config_addresses_sort (composite);
+
 	/* Allow setting MTU etc */
 	if (commit) {
 		if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit)
@@ -4253,6 +4455,17 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 }
 
 static void
+dhcp4_dad_cb (NMDevice *self, NMIP4Config **configs, gboolean success)
+{
+	if (success)
+		nm_device_activate_schedule_ip4_config_result (self, configs[1]);
+	else {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+	}
+}
+
+static void
 dhcp4_state_changed (NMDhcpClient *client,
                      NMDhcpState state,
                      NMIP4Config *ip4_config,
@@ -4262,6 +4475,8 @@ dhcp4_state_changed (NMDhcpClient *client,
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMIP4Config *manual, **configs;
+	NMConnection *connection;
 
 	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE);
 	g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
@@ -4279,11 +4494,23 @@ dhcp4_state_changed (NMDhcpClient *client,
 		}
 
 		nm_dhcp4_config_set_options (priv->dhcp4_config, options);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
+		_notify (self, PROP_DHCP4_CONFIG);
 
-		if (priv->ip4_state == IP_CONF)
-			nm_device_activate_schedule_ip4_config_result (self, ip4_config);
-		else if (priv->ip4_state == IP_DONE) {
+		if (priv->ip4_state == IP_CONF) {
+			connection = nm_device_get_applied_connection (self);
+			g_assert (connection);
+
+			manual = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
+			nm_ip4_config_merge_setting (manual,
+			                             nm_connection_get_setting_ip4_config (connection),
+			                             nm_device_get_ip4_route_metric (self));
+
+			configs = g_new0 (NMIP4Config *, 3);
+			configs[0] = manual;
+			configs[1] = g_object_ref (ip4_config);
+
+			ipv4_dad_start (self, configs, dhcp4_dad_cb);
+		} else if (priv->ip4_state == IP_DONE) {
 			dhcp4_lease_change (self, ip4_config);
 			nm_device_update_metered (self);
 		}
@@ -4312,7 +4539,7 @@ dhcp4_get_timeout (NMDevice *self, NMSettingIP4Config *s_ip4)
 	gs_free char *value = NULL;
 	int timeout;
 
-	timeout = nm_setting_ip4_config_get_dhcp_timeout (s_ip4);
+	timeout = nm_setting_ip_config_get_dhcp_timeout (NM_SETTING_IP_CONFIG (s_ip4));
 	if (timeout)
 		return timeout;
 
@@ -4629,10 +4856,17 @@ act_stage3_ip4_config_start (NMDevice *self,
 	else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0)
 		ret = ipv4ll_start (self, reason);
 	else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) {
-		/* Use only IPv4 config from the connection data */
-		*out_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
-		g_assert (*out_config);
-		ret = NM_ACT_STAGE_RETURN_SUCCESS;
+		NMIP4Config **configs, *config;
+
+		config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
+		nm_ip4_config_merge_setting (config,
+		                             nm_connection_get_setting_ip4_config (connection),
+		                             nm_device_get_ip4_route_metric (self));
+
+		configs = g_new0 (NMIP4Config *, 2);
+		configs[0] = config;
+		ipv4_dad_start (self, configs, ipv4_manual_method_apply);
+		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
 		*out_config = shared4_new_config (self, connection, reason);
 		if (*out_config) {
@@ -4676,7 +4910,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 
 	if (priv->dhcp6_config) {
 		nm_exported_object_clear_and_unexport (&priv->dhcp6_config);
-		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+		_notify (self, PROP_DHCP6_CONFIG);
 	}
 }
 
@@ -5011,7 +5245,7 @@ dhcp6_state_changed (NMDhcpClient *client,
 				priv->dhcp6_ip6_config = g_object_ref (ip6_config);
 				priv->dhcp6_event_id = g_strdup (event_id);
 				nm_dhcp6_config_set_options (priv->dhcp6_config, options);
-				g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+				_notify (self, PROP_DHCP6_CONFIG);
 			}
 		}
 
@@ -5057,9 +5291,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	GByteArray *tmp = NULL;
 	const guint8 *hw_addr;
 	size_t hw_addr_len = 0;
-	const struct in6_addr *ll_addr = NULL;
-	NMIP6Config *ip6_config;
-	int i;
+	const NMPlatformIP6Address *ll_addr = NULL;
 
 	g_assert (connection);
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
@@ -5071,22 +5303,16 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 		g_byte_array_append (tmp, hw_addr, hw_addr_len);
 	}
 
-	ip6_config = priv->ext_ip6_config;
-	for (i = 0; ip6_config && i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
-		const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ip6_config, i);
+	if (priv->ext_ip6_config_captured)
+		ll_addr = nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE);
 
-		if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
-			ll_addr = &addr->address;
-			break;
-		}
-	}
 	g_return_val_if_fail (ll_addr, FALSE);
 
 	priv->dhcp6_client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
 	                                                nm_device_get_ip_iface (self),
 	                                                nm_device_get_ip_ifindex (self),
 	                                                tmp,
-	                                                ll_addr,
+	                                                &ll_addr->address,
 	                                                nm_connection_get_uuid (connection),
 	                                                nm_device_get_ip6_route_metric (self),
 	                                                nm_setting_ip_config_get_dhcp_send_hostname (s_ip6),
@@ -5169,27 +5395,6 @@ nm_device_dhcp6_renew (NMDevice *self, gboolean release)
 
 /******************************************/
 
-static gboolean
-have_ip6_address (const NMIP6Config *ip6_config, gboolean linklocal)
-{
-	guint i;
-
-	if (!ip6_config)
-		return FALSE;
-
-	linklocal = !!linklocal;
-
-	for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
-		const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ip6_config, i);
-
-		if ((IN6_IS_ADDR_LINKLOCAL (&addr->address) == linklocal) &&
-		    !(addr->flags & IFA_F_TENTATIVE))
-			return TRUE;
-	}
-
-	return FALSE;
-}
-
 static void
 linklocal6_cleanup (NMDevice *self)
 {
@@ -5223,7 +5428,7 @@ linklocal6_complete (NMDevice *self)
 	const char *method;
 
 	g_assert (priv->linklocal6_timeout_id);
-	g_assert (have_ip6_address (priv->ip6_config, TRUE));
+	g_assert (nm_ip6_config_get_address_first_nontentative (priv->ip6_config, TRUE));
 
 	linklocal6_cleanup (self);
 
@@ -5271,7 +5476,7 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 
 			addr = nm_ip6_config_get_address (priv->ip6_config, i);
 			if (   IN6_IS_ADDR_LINKLOCAL (&addr->address)
-			    && !(addr->flags & IFA_F_DADFAILED)) {
+			    && !(addr->n_ifa_flags & IFA_F_DADFAILED)) {
 				/* Already have an LL address, nothing to do */
 				return;
 			}
@@ -5288,9 +5493,9 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 	if (s_ip6 && nm_setting_ip6_config_get_addr_gen_mode (s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
 		if (!nm_utils_ipv6_addr_set_stable_privacy (&lladdr,
 		                                            nm_device_get_iface (self),
-			                                    nm_connection_get_uuid (connection),
+		                                            nm_connection_get_uuid (connection),
 		                                            priv->linklocal6_dad_counter++,
-			                                    &error)) {
+		                                            &error)) {
 			_LOGW (LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message);
 			g_clear_error (&error);
 			linklocal6_failed (self);
@@ -5340,7 +5545,8 @@ linklocal6_start (NMDevice *self)
 
 	linklocal6_cleanup (self);
 
-	if (have_ip6_address (priv->ip6_config, TRUE))
+	if (   priv->ip6_config
+	    && nm_ip6_config_get_address_first_nontentative (priv->ip6_config, TRUE))
 		return NM_ACT_STAGE_RETURN_FINISH;
 
 	connection = nm_device_get_applied_connection (self);
@@ -5421,7 +5627,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int i;
 	int system_support;
-	guint ifa_flags = 0x00;
+	guint32 ifa_flags = 0x00;
 
 	/*
 	 * Check, whether kernel is recent enough to help user space handling RA.
@@ -5477,7 +5683,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 			if (address.preferred > address.lifetime)
 				address.preferred = address.lifetime;
 			address.source = NM_IP_CONFIG_SOURCE_RDISC;
-			address.flags = ifa_flags;
+			address.n_ifa_flags = ifa_flags;
 
 			nm_ip6_config_add_address (priv->ac_ip6_config, &address);
 		}
@@ -5575,7 +5781,8 @@ rdisc_ra_timeout (NMRDisc *rdisc, NMDevice *self)
 		 * IPv6 configuration, like manual IPv6 addresses or external IPv6
 		 * config, consider that sufficient for IPv6 success.
 		 */
-		if (have_ip6_address (priv->ip6_config, FALSE))
+		if (   priv->ip6_config
+		    && nm_ip6_config_get_address_first_nontentative (priv->ip6_config, FALSE))
 			nm_device_activate_schedule_ip6_config_result (self);
 		else
 			nm_device_activate_schedule_ip6_config_timeout (self);
@@ -5769,7 +5976,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 
 		if (enable) {
 			/* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */
-			value = nm_platform_sysctl_get (NM_PLATFORM_GET, 
+			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");
@@ -6054,8 +6261,7 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
 	} else if (ret == NM_ACT_STAGE_RETURN_FINISH) {
 		/* Early finish, nothing more to do */
 		priv->ip6_state = IP_DONE;
-		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);
+		check_ip_done (self);
 	} else if (ret == NM_ACT_STAGE_RETURN_WAIT) {
 		/* Wait for something to try IP config again */
 		priv->ip6_state = IP_WAIT;
@@ -6116,7 +6322,7 @@ activate_stage3_ip_config_start (NMDevice *self)
 	    && !nm_device_activate_stage3_ip6_start (self))
 		return;
 
-	nm_device_check_ip_failed (self, TRUE);
+	check_ip_failed (self, TRUE);
 }
 
 static gboolean
@@ -6251,7 +6457,7 @@ activate_stage4_ip4_config_timeout (NMDevice *self)
 
 	priv->ip4_state = IP_FAIL;
 
-	nm_device_check_ip_failed (self, FALSE);
+	check_ip_failed (self, FALSE);
 }
 
 
@@ -6311,7 +6517,7 @@ activate_stage4_ip6_config_timeout (NMDevice *self)
 
 	priv->ip6_state = IP_FAIL;
 
-	nm_device_check_ip_failed (self, FALSE);
+	check_ip_failed (self, FALSE);
 }
 
 
@@ -6410,16 +6616,16 @@ start_sharing (NMDevice *self, NMIP4Config *config)
 	req = nm_device_get_act_request (self);
 	g_assert (req);
 
-	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT", ip_iface);
-	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT", ip_iface);
-	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT", ip_iface);
-	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT", ip_iface);
-	add_share_rule (req, "filter", "FORWARD --in-interface %s --jump REJECT", ip_iface);
-	add_share_rule (req, "filter", "FORWARD --out-interface %s --jump REJECT", ip_iface);
-	add_share_rule (req, "filter", "FORWARD --in-interface %s --out-interface %s --jump ACCEPT", ip_iface, ip_iface);
-	add_share_rule (req, "filter", "FORWARD --source %s/%s --in-interface %s --jump ACCEPT", str_addr, str_mask, ip_iface);
-	add_share_rule (req, "filter", "FORWARD --destination %s/%s --out-interface %s --match state --state ESTABLISHED,RELATED --jump ACCEPT", str_addr, str_mask, ip_iface);
 	add_share_rule (req, "nat", "POSTROUTING --source %s/%s ! --destination %s/%s --jump MASQUERADE", str_addr, str_mask, str_addr, str_mask);
+	add_share_rule (req, "filter", "FORWARD --destination %s/%s --out-interface %s --match state --state ESTABLISHED,RELATED --jump ACCEPT", str_addr, str_mask, ip_iface);
+	add_share_rule (req, "filter", "FORWARD --source %s/%s --in-interface %s --jump ACCEPT", str_addr, str_mask, ip_iface);
+	add_share_rule (req, "filter", "FORWARD --in-interface %s --out-interface %s --jump ACCEPT", ip_iface, ip_iface);
+	add_share_rule (req, "filter", "FORWARD --out-interface %s --jump REJECT", ip_iface);
+	add_share_rule (req, "filter", "FORWARD --in-interface %s --jump REJECT", ip_iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT", ip_iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT", ip_iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT", ip_iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT", ip_iface);
 
 	nm_act_request_set_shared (req, TRUE);
 
@@ -6438,77 +6644,14 @@ start_sharing (NMDevice *self, NMIP4Config *config)
 }
 
 static void
-send_arps (NMDevice *self, const char *mode_arg)
-{
-	const char *argv[] = { NULL, mode_arg, "-q", "-I", nm_device_get_ip_iface (self), "-c", "1", NULL, NULL };
-	int ip_arg = G_N_ELEMENTS (argv) - 2;
-	NMConnection *connection;
-	NMSettingIPConfig *s_ip4;
-	int i, num;
-	NMIPAddress *addr;
-	GError *error = NULL;
-
-	connection = nm_device_get_applied_connection (self);
-	if (!connection)
-		return;
-	s_ip4 = nm_connection_get_setting_ip4_config (connection);
-	if (!s_ip4)
-		return;
-	num = nm_setting_ip_config_get_num_addresses (s_ip4);
-	if (num == 0)
-		return;
-
-	argv[0] = nm_utils_find_helper ("arping", NULL, NULL);
-	if (!argv[0]) {
-		_LOGW (LOGD_DEVICE | LOGD_IP4, "arping could not be found; no ARPs will be sent");
-		return;
-	}
-
-	for (i = 0; i < num; i++) {
-		gs_free char *tmp_str = NULL;
-		gboolean success;
-
-		addr = nm_setting_ip_config_get_address (s_ip4, i);
-		argv[ip_arg] = nm_ip_address_get_address (addr);
-
-		_LOGD (LOGD_DEVICE | LOGD_IP4,
-		       "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,
-		                         NULL, NULL, NULL, &error);
-		if (!success) {
-			_LOGW (LOGD_DEVICE | LOGD_IP4,
-			       "arping: could not send ARP for local address %s: %s",
-			       argv[ip_arg], error->message);
-			g_clear_error (&error);
-		}
-	}
-}
-
-static gboolean
-arp_announce_round2 (gpointer user_data)
-{
-	NMDevice *self = user_data;
-	NMDevicePrivate *priv;
-
-	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
-
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	priv->arp_round2_id = 0;
-
-	if (   priv->state >= NM_DEVICE_STATE_IP_CONFIG
-	    && priv->state <= NM_DEVICE_STATE_ACTIVATED)
-		send_arps (self, "-U");
-
-	return G_SOURCE_REMOVE;
-}
-
-static void
 arp_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	nm_clear_g_source (&priv->arp_round2_id);
+	if (priv->arping.announcing) {
+		nm_arping_manager_destroy (priv->arping.announcing);
+		priv->arping.announcing = NULL;
+	}
 }
 
 static void
@@ -6517,10 +6660,19 @@ arp_announce (NMDevice *self)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip4;
-	int num;
+	guint num, i;
+	const guint8 *hw_addr;
+	size_t hw_addr_len = 0;
 
 	arp_cleanup (self);
 
+	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET,
+	                                        nm_device_get_ip_ifindex (self),
+	                                        &hw_addr_len);
+
+	if (!hw_addr_len || !hw_addr)
+		return;
+
 	/* We only care about manually-configured addresses; DHCP- and autoip-configured
 	 * ones should already have been seen on the network at this point.
 	 */
@@ -6534,8 +6686,19 @@ arp_announce (NMDevice *self)
 	if (num == 0)
 		return;
 
-	send_arps (self, "-A");
-	priv->arp_round2_id = g_timeout_add_seconds (2, arp_announce_round2, self);
+	priv->arping.announcing = nm_arping_manager_new (nm_device_get_ip_ifindex (self));
+
+	for (i = 0; i < num; i++) {
+		NMIPAddress *ip = nm_setting_ip_config_get_address (s_ip4, i);
+		in_addr_t addr;
+
+		if (inet_pton (AF_INET, nm_ip_address_get_address (ip), &addr) == 1)
+			nm_arping_manager_add_address (priv->arping.announcing, addr);
+		else
+			g_warn_if_reached ();
+	}
+
+	nm_arping_manager_announce_addresses (priv->arping.announcing);
 }
 
 static void
@@ -6597,13 +6760,11 @@ activate_stage5_ip4_config_commit (NMDevice *self)
 
 	arp_announce (self);
 
-	/* Enter the IP_CHECK state if this is the first method to complete */
-	priv->ip4_state = IP_DONE;
-
 	nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
 
-	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);
+	/* Enter the IP_CHECK state if this is the first method to complete */
+	priv->ip4_state = IP_DONE;
+	check_ip_done (self);
 }
 
 static void
@@ -6690,26 +6851,24 @@ activate_stage5_ip6_config_commit (NMDevice *self)
 				 * then ensure dispatcher scripts get the DHCP lease information.
 				 */
 				nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE,
-						    nm_device_get_settings_connection (self),
-						    nm_device_get_applied_connection (self),
-						    self,
-						    NULL,
-						    NULL,
-						    NULL);
+				                    nm_device_get_settings_connection (self),
+				                    nm_device_get_applied_connection (self),
+				                    self,
+				                    NULL,
+				                    NULL,
+				                    NULL);
 			} else {
 				/* still waiting for first dhcp6 lease. */
 				return;
 			}
 		}
 
-		/* Enter the IP_CHECK state if this is the first method to complete */
-		priv->ip6_state = IP_DONE;
-
 		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
 		nm_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE);
 
-		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);
+		/* Enter the IP_CHECK state if this is the first method to complete */
+		priv->ip6_state = IP_DONE;
+		check_ip_done (self);
 	} else {
 		_LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed");
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
@@ -6760,7 +6919,7 @@ clear_act_request (NMDevice *self)
 	nm_clear_g_signal_handler (priv->act_request, &priv->master_ready_id);
 
 	g_clear_object (&priv->act_request);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION);
+	_notify (self, PROP_ACTIVE_CONNECTION);
 }
 
 static void
@@ -6792,7 +6951,7 @@ _update_ip4_address (NMDevice *self)
 		addr = nm_ip4_config_get_address (priv->ip4_config, 0)->address;
 		if (addr != priv->ip4_address) {
 			priv->ip4_address = addr;
-			g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS);
+			_notify (self, PROP_IP4_ADDRESS);
 		}
 	}
 }
@@ -6911,25 +7070,23 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type)
 	addrconf6_cleanup (self);
 }
 
-G_GNUC_NULL_TERMINATED
 static gboolean
-_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, GError **error, ...)
+_hash_check_invalid_keys_impl (GHashTable *hash, const char *setting_name, GError **error, const char **argv)
 {
-	va_list ap;
-	const char *key;
 	guint found_keys = 0;
+	guint i;
+
+	nm_assert (argv && argv[0]);
 
 #if NM_MORE_ASSERTS > 10
 	/* Assert that the keys are unique. */
 	{
 		gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
 
-		va_start (ap, error);
-		while ((key = va_arg (ap, const char *))) {
-			if (!g_hash_table_add (check_dups, (char *) key))
+		for (i = 0; argv[i]; i++) {
+			if (!g_hash_table_add (check_dups, (char *) argv[i]))
 				nm_assert (FALSE);
 		}
-		va_end (ap);
 		nm_assert (g_hash_table_size (check_dups) > 0);
 	}
 #endif
@@ -6937,12 +7094,10 @@ _hash_check_invalid_keys (GHashTable *hash, const char *setting_name, GError **e
 	if (!hash || g_hash_table_size (hash) == 0)
 		return TRUE;
 
-	va_start (ap, error);
-	while ((key = va_arg (ap, const char *))) {
-		if (g_hash_table_contains (hash, key))
+	for (i = 0; argv[i]; i++) {
+		if (g_hash_table_contains (hash, argv[i]))
 			found_keys++;
 	}
-	va_end (ap);
 
 	if (found_keys != g_hash_table_size (hash)) {
 		GHashTableIter iter;
@@ -6954,14 +7109,12 @@ _hash_check_invalid_keys (GHashTable *hash, const char *setting_name, GError **e
 
 		g_hash_table_iter_init (&iter, hash);
 		while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) {
-			va_start (ap, error);
-			while ((key = va_arg (ap, const char *))) {
-				if (!strcmp (key, k)) {
+			for (i = 0; argv[i]; i++) {
+				if (!strcmp (argv[i], k)) {
 					first_invalid_key = k;
 					break;
 				}
 			}
-			va_end (ap);
 			if (first_invalid_key)
 				break;
 		}
@@ -6978,6 +7131,7 @@ _hash_check_invalid_keys (GHashTable *hash, const char *setting_name, GError **e
 
 	return TRUE;
 }
+#define _hash_check_invalid_keys(hash, setting_name, error, ...) _hash_check_invalid_keys_impl (hash, setting_name, error, ((const char *[]) { __VA_ARGS__, NULL }))
 
 void
 nm_device_reactivate_ip4_config (NMDevice *self,
@@ -7002,8 +7156,10 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 			priv->ip4_state = IP_WAIT;
 			if (!nm_device_activate_stage3_ip4_start (self))
 				_LOGW (LOGD_IP4, "Failed to apply IPv4 configuration");
-		} else
-			ip4_config_merge_and_apply (self, NULL, TRUE, NULL);
+		} else {
+			if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
+				_LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration");
+		}
 	}
 }
 
@@ -7030,8 +7186,10 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 			priv->ip6_state = IP_WAIT;
 			if (!nm_device_activate_stage3_ip6_start (self))
 				_LOGW (LOGD_IP6, "Failed to apply IPv6 configuration");
-		} else
-			ip6_config_merge_and_apply (self, TRUE, NULL);
+		} else {
+			if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+				_LOGW (LOGD_IP4, "Failed to reapply IPv6 configuration");
+		}
 	}
 }
 
@@ -7039,6 +7197,8 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 /* reapply_connection:
  * @connection: the new connection settings to be applied or %NULL to reapply
  *   the current settings connection
+ * @version_id: either zero, or the current version id for the applied
+ *   connection.
  * @error: the error if %FALSE is returned
  *
  * Change configuration of an already configured device if possible.
@@ -7049,6 +7209,7 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 static gboolean
 reapply_connection (NMDevice *self,
                     NMConnection *connection,
+                    guint64 version_id,
                     GError **error)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -7079,24 +7240,36 @@ reapply_connection (NMDevice *self,
 	if (!_hash_check_invalid_keys (diffs, NULL, error,
 	                               NM_SETTING_IP4_CONFIG_SETTING_NAME,
 	                               NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	                               NM_SETTING_CONNECTION_SETTING_NAME,
-	                               NULL))
+	                               NM_SETTING_CONNECTION_SETTING_NAME))
 		return FALSE;
 
 	if (!_hash_check_invalid_keys (diffs ? g_hash_table_lookup (diffs, NM_SETTING_CONNECTION_SETTING_NAME) : NULL,
 	                               NM_SETTING_CONNECTION_SETTING_NAME,
 	                               error,
 	                               NM_SETTING_CONNECTION_ZONE,
-	                               NM_SETTING_CONNECTION_METERED,
-	                               NULL))
+	                               NM_SETTING_CONNECTION_METERED))
 		return FALSE;
 
-	_LOGD (LOGD_DEVICE, "reapply");
+	if (   version_id != 0
+	    && version_id != nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request)) {
+		g_set_error_literal (error,
+		                     NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_VERSION_ID_MISMATCH,
+		                     "Reapply failed because device changed in the meantime and the version-id mismatches");
+		return FALSE;
+	}
 
 	/**************************************************************************
 	 * Update applied connection
 	 *************************************************************************/
 
+	if (diffs)
+		nm_active_connection_version_id_bump ((NMActiveConnection *) priv->act_request);
+
+	_LOGD (LOGD_DEVICE, "reapply (version-id %llu%s)",
+	       (long long unsigned) nm_active_connection_version_id_get (((NMActiveConnection *) priv->act_request)),
+	       diffs ? "" : " (unmodified)");
+
 	if (diffs) {
 		con_old = applied_clone  = nm_simple_connection_new_clone (applied);
 		con_new = applied;
@@ -7122,6 +7295,11 @@ reapply_connection (NMDevice *self,
 	return TRUE;
 }
 
+typedef struct {
+	NMConnection *connection;
+	guint64 version_id;
+} ReapplyData;
+
 static void
 reapply_cb (NMDevice *self,
             GDBusMethodInvocation *context,
@@ -7129,9 +7307,17 @@ reapply_cb (NMDevice *self,
             GError *error,
             gpointer user_data)
 {
-	gs_unref_object NMConnection *connection = NM_CONNECTION (user_data);
+	ReapplyData *reapply_data = user_data;
+	guint64 version_id = 0;
+	gs_unref_object NMConnection *connection = NULL;
 	GError *local = NULL;
 
+	if (reapply_data) {
+		connection = reapply_data->connection;
+		version_id = reapply_data->version_id;
+		g_slice_free (ReapplyData, reapply_data);
+	}
+
 	if (error) {
 		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, error->message);
 		g_dbus_method_invocation_return_gerror (context, error);
@@ -7140,6 +7326,7 @@ reapply_cb (NMDevice *self,
 
 	if (!reapply_connection (self,
 	                         connection ? : (NMConnection *) nm_device_get_settings_connection (self),
+	                         version_id,
 	                         &local)) {
 		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, local->message);
 		g_dbus_method_invocation_take_error (context, local);
@@ -7154,17 +7341,19 @@ static void
 impl_device_reapply (NMDevice *self,
                      GDBusMethodInvocation *context,
                      GVariant *settings,
-                     guint flags)
+                     guint64 version_id,
+                     guint32 flags)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingsConnection *settings_connection;
 	NMConnection *connection = NULL;
 	GError *error = NULL;
+	ReapplyData *reapply_data;
 
 	/* No flags supported as of now. */
 	if (flags != 0) {
 		error = g_error_new_literal (NM_DEVICE_ERROR,
-		                             NM_DEVICE_ERROR_NOT_ACTIVE,
+		                             NM_DEVICE_ERROR_FAILED,
 		                             "Invalid flags specified");
 		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message);
 		g_dbus_method_invocation_take_error (context, error);
@@ -7195,6 +7384,13 @@ impl_device_reapply (NMDevice *self,
 		nm_connection_clear_secrets (connection);
 	}
 
+	if (connection || version_id) {
+		reapply_data = g_slice_new (ReapplyData);
+		reapply_data->connection = connection;
+		reapply_data->version_id = version_id;
+	} else
+		reapply_data = NULL;
+
 	/* Ask the manager to authenticate this request for us */
 	g_signal_emit (self, signals[AUTH_REQUEST], 0,
 	               context,
@@ -7202,10 +7398,104 @@ impl_device_reapply (NMDevice *self,
 	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
 	               TRUE,
 	               reapply_cb,
-	               connection);
+	               reapply_data);
+}
+
+/*****************************************************************************/
+
+static void
+get_applied_connection_cb (NMDevice *self,
+                           GDBusMethodInvocation *context,
+                           NMAuthSubject *subject,
+                           GError *error,
+                           gpointer user_data /* possibly dangling pointer */)
+{
+	NMDevicePrivate *priv;
+	NMConnection *applied_connection;
+	GVariant *settings;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	if (error) {
+		g_dbus_method_invocation_return_gerror (context, error);
+		return;
+	}
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	applied_connection = nm_device_get_applied_connection (self);
+
+	if (!applied_connection) {
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ACTIVE,
+		                             "Device is not activated");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	if (applied_connection != user_data) {
+		/* The applied connection changed due to a race. Reauthenticate. */
+		g_signal_emit (self, signals[AUTH_REQUEST], 0,
+		               context,
+		               applied_connection,
+		               NM_AUTH_PERMISSION_NETWORK_CONTROL,
+		               TRUE,
+		               get_applied_connection_cb,
+		               applied_connection /* no need take a ref. We will not dereference this pointer. */);
+		return;
+	}
+
+	settings = nm_connection_to_dbus (applied_connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
+	if (!settings)
+		settings = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0);
+
+	g_dbus_method_invocation_return_value (context,
+	                                       g_variant_new ("(@a{sa{sv}}t)",
+	                                                      settings,
+	                                                      nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request)));
 }
 
 static void
+impl_device_get_applied_connection (NMDevice *self,
+                                    GDBusMethodInvocation *context,
+                                    guint32 flags)
+{
+	NMConnection *applied_connection;
+	GError *error = NULL;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	/* No flags supported as of now. */
+	if (flags != 0) {
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_FAILED,
+		                             "Invalid flags specified");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	applied_connection = nm_device_get_applied_connection (self);
+	if (!applied_connection) {
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ACTIVE,
+		                             "Device is not activated");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	/* Ask the manager to authenticate this request for us */
+	g_signal_emit (self, signals[AUTH_REQUEST], 0,
+	               context,
+	               applied_connection,
+	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
+	               TRUE,
+	               get_applied_connection_cb,
+	               applied_connection /* no need take a ref. We will not dereference this pointer. */);
+}
+
+/*****************************************************************************/
+
+static void
 disconnect_cb (NMDevice *self,
                GDBusMethodInvocation *context,
                NMAuthSubject *subject,
@@ -7329,6 +7619,7 @@ _device_activate (NMDevice *self, NMActRequest *req)
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+	g_return_val_if_fail (nm_device_get_managed (self, FALSE), FALSE);
 
 	/* Ensure the activation request is still valid; the master may have
 	 * already failed in which case activation of this device should not proceed.
@@ -7347,13 +7638,6 @@ _device_activate (NMDevice *self, NMActRequest *req)
 
 	delete_on_deactivate_unschedule (self);
 
-	/* Move default unmanaged devices to DISCONNECTED state here */
-	if (nm_device_get_default_unmanaged (self) && priv->state == NM_DEVICE_STATE_UNMANAGED) {
-		nm_device_state_changed (self,
-		                         NM_DEVICE_STATE_DISCONNECTED,
-		                         NM_DEVICE_STATE_REASON_NOW_MANAGED);
-	}
-
 	/* note: don't notify D-Bus of the new AC here, but do it later when
 	 * changing state to PREPARE so that the two properties change together.
 	 */
@@ -7602,7 +7886,7 @@ nm_device_set_ip4_config (NMDevice *self,
 		_update_ip4_address (self);
 
 		if (old_config != priv->ip4_config)
-			g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_CONFIG);
+			_notify (self, PROP_IP4_CONFIG);
 		g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, old_config);
 
 		if (old_config != priv->ip4_config)
@@ -7769,7 +8053,7 @@ nm_device_set_ip6_config (NMDevice *self,
 
 	if (has_changes) {
 		if (old_config != priv->ip6_config)
-			g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG);
+			_notify (self, PROP_IP6_CONFIG);
 		g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, old_config);
 
 		if (old_config != priv->ip6_config)
@@ -8275,7 +8559,7 @@ nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	if (priv->firmware_missing != new_missing) {
 		priv->firmware_missing = new_missing;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_MISSING);
+		_notify (self, PROP_FIRMWARE_MISSING);
 	}
 }
 
@@ -8295,7 +8579,7 @@ nm_device_set_nm_plugin_missing (NMDevice *self, gboolean new_missing)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	if (priv->nm_plugin_missing != new_missing) {
 		priv->nm_plugin_missing = new_missing;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_NM_PLUGIN_MISSING);
+		_notify (self, PROP_NM_PLUGIN_MISSING);
 	}
 }
 
@@ -8507,7 +8791,6 @@ 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;
 
@@ -8520,12 +8803,11 @@ update_ip6_config (NMDevice *self, gboolean initial)
 
 	/* 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);
-	if (priv->ext_ip6_config) {
+	g_clear_object (&priv->ext_ip6_config_captured);
+	priv->ext_ip6_config_captured = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+	if (priv->ext_ip6_config_captured) {
 
-		/* Check this before modifying ext_ip6_config */
-		linklocal6_just_completed = priv->linklocal6_timeout_id &&
-		                            have_ip6_address (priv->ext_ip6_config, TRUE);
+		priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured);
 
 		/* This function was called upon external changes. Remove the configuration
 		 * (addresses,routes) that is no longer present externally from the internal
@@ -8557,7 +8839,9 @@ update_ip6_config (NMDevice *self, gboolean initial)
 		ip6_config_merge_and_apply (self, FALSE, NULL);
 	}
 
-	if (linklocal6_just_completed) {
+	if (   priv->linklocal6_timeout_id
+	    && priv->ext_ip6_config_captured
+	    && nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE)) {
 		/* linklocal6 is ready now, do the state transition... we are also
 		 * invoked as g_idle_add, so no problems with reentrance doing it now.
 		 */
@@ -8673,8 +8957,8 @@ device_ipx_changed (NMPlatform *platform,
 
 		if (   priv->state > NM_DEVICE_STATE_DISCONNECTED
 		    && priv->state < NM_DEVICE_STATE_DEACTIVATING
-                    && (   (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->flags & IFA_F_DADFAILED)
-		        || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->flags & IFA_F_TENTATIVE))) {
+		    && (   (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED)
+		        || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE))) {
 			priv->dad6_failed_addrs = g_slist_append (priv->dad6_failed_addrs,
 			                                          g_memdup (addr, sizeof (NMPlatformIP6Address)));
 		}
@@ -8690,119 +8974,319 @@ device_ipx_changed (NMPlatform *platform,
 	}
 }
 
+/*****************************************************************************/
+
+NM_UTILS_FLAGS2STR_DEFINE (nm_unmanaged_flags2str, NMUnmanagedFlags,
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_SLEEPING, "sleeping"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_QUITTING, "quitting"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_PARENT, "parent"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_LOOPBACK, "loopback"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_PLATFORM_INIT, "platform-init"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_EXPLICIT, "user-explicit"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_BY_DEFAULT, "by-default"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_SETTINGS, "user-settings"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_UDEV, "user-udev"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_EXTERNAL_DOWN, "external-down"),
+	NM_UTILS_FLAGS2STR (NM_UNMANAGED_IS_SLAVE, "is-slave"),
+);
+
+static const char *
+_unmanaged_flags2str (NMUnmanagedFlags flags, NMUnmanagedFlags mask, char *buf, gsize len)
+{
+	char buf2[512];
+	char *b;
+	char *tmp, *tmp2;
+	gsize l;
+
+	nm_utils_to_string_buffer_init (&buf, &len);
+	if (!len)
+		return buf;
+
+	b = buf;
+
+	mask |= flags;
+
+	nm_unmanaged_flags2str (flags, b, len);
+	l = strlen (b);
+	b += l;
+	len -= l;
+
+	nm_unmanaged_flags2str (mask & ~flags, buf2, sizeof (buf2));
+	if (buf2[0]) {
+		gboolean add_separator = l > 0;
+
+		tmp = buf2;
+		while (TRUE) {
+			if (add_separator)
+				nm_utils_strbuf_append_c (&b, &len, ',');
+			add_separator = TRUE;
+
+			tmp2 = strchr (tmp, ',');
+			if (tmp2)
+				tmp2[0] = '\0';
+
+			nm_utils_strbuf_append_c (&b, &len, '!');
+			nm_utils_strbuf_append_str (&b, &len, tmp);
+			if (!tmp2)
+				break;
+
+			tmp = &tmp2[1];
+		}
+	}
+
+	return buf;
+}
+
+static gboolean
+_get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean for_user_request)
+{
+	/* Evaluate the managed state based on the unmanaged flags.
+	 *
+	 * Some flags are authoritative, meaning they always cause
+	 * the device to be unmanaged (e.g. @NM_UNMANAGED_PLATFORM_INIT).
+	 *
+	 * OTOH, some flags can be overwritten. For example NM_UNMANAGED_USER_SETTINGS
+	 * is ignored once NM_UNMANAGED_USER_EXPLICIT is set. The idea is that
+	 * the flag from the configuration has no effect once the user explicitly
+	 * touches the unmanaged flags. */
+
+	if (for_user_request) {
+
+		/* @for_user_request can make the result only ~more~ managed.
+		 * If the flags already indicate a managed state for a non-user-request,
+		 * then it is also managed for an explict user-request.
+		 *
+		 * Effectively, this check is redundant, as the code below already
+		 * already ensures that. Still, express this invariant explictly here. */
+		if (_get_managed_by_flags (flags, mask, FALSE))
+			return TRUE;
+
+		/* A for-user-request, is effectively the same as pretending
+		 * that user-dbus flag is cleared. */
+		mask |= NM_UNMANAGED_USER_EXPLICIT;
+		flags &= ~NM_UNMANAGED_USER_EXPLICIT;
+	}
+
+	if (   NM_FLAGS_ANY (mask, NM_UNMANAGED_USER_SETTINGS)
+	    && !NM_FLAGS_ANY (flags, NM_UNMANAGED_USER_SETTINGS)) {
+		/* NM_UNMANAGED_USER_SETTINGS can only explicitly unmanage a device. It cannot
+		 * *manage* it. Having NM_UNMANAGED_USER_SETTINGS explicitly not set, is the
+		 * same as having it not set at all. */
+		mask &= ~NM_UNMANAGED_USER_SETTINGS;
+	}
+
+	if (NM_FLAGS_ANY (mask, NM_UNMANAGED_USER_UDEV)) {
+		/* configuration from udev or nm-config overwrites the by-default flag
+		 * which is based on the device type. */
+		flags &= ~NM_UNMANAGED_BY_DEFAULT;
+	}
+
+	if (   NM_FLAGS_HAS (mask, NM_UNMANAGED_IS_SLAVE)
+	    && !NM_FLAGS_HAS (flags, NM_UNMANAGED_IS_SLAVE)) {
+		/* for an enslaved device, by-default doesn't matter */
+		flags &= ~NM_UNMANAGED_BY_DEFAULT;
+	}
+
+	if (NM_FLAGS_HAS (mask, NM_UNMANAGED_USER_EXPLICIT)) {
+		/* if the device is managed by user-decision, certain other flags
+		 * are ignored. */
+
+		flags &= ~(  NM_UNMANAGED_BY_DEFAULT
+		           | NM_UNMANAGED_USER_UDEV
+		           | NM_UNMANAGED_EXTERNAL_DOWN);
+	}
+
+	return flags == NM_UNMANAGED_NONE;
+}
+
 /**
- * nm_device_get_managed():
+ * nm_device_get_managed:
  * @self: the #NMDevice
+ * @for_user_request: whether to check the flags for an explict user-request
+ *
+ * Whether the device is unmanaged according to the unmanaged flags.
  *
- * Returns: %TRUE if the device is managed
+ * Returns: %TRUE if the device is unmanaged because of the flags.
  */
 gboolean
-nm_device_get_managed (NMDevice *self)
+nm_device_get_managed (NMDevice *self, gboolean for_user_request)
 {
 	NMDevicePrivate *priv;
-	gboolean managed;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (!nm_device_is_real (self)) {
+		/* a unrealized device is always considered unmanaged. */
+		return FALSE;
+	}
 
-	/* Return the composite of all managed flags.  However, if the device
-	 * is a default-unmanaged device, and would be managed except for the
-	 * default-unmanaged flag (eg, only NM_UNMANAGED_DEFAULT is set) then
-	 * the device is managed whenever it's not in the UNMANAGED state.
-	 */
-	managed = !NM_FLAGS_ANY (priv->unmanaged_flags, ~NM_UNMANAGED_DEFAULT);
-	if (managed && NM_FLAGS_HAS (priv->unmanaged_flags, NM_UNMANAGED_DEFAULT))
-		managed = (priv->state > NM_DEVICE_STATE_UNMANAGED);
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	return managed;
+	return _get_managed_by_flags (priv->unmanaged_flags, priv->unmanaged_mask, for_user_request);
 }
 
 /**
- * nm_device_get_unmanaged_flags():
+ * nm_device_get_unmanaged_flags:
  * @self: the #NMDevice
- * @flag: return only the selected flags
+ * @flag: the unmanaged flags to check.
+ *
+ * Return the unmanaged flags of the device.
  *
- * Returns: the unmanage flags of the device (filtered with @flag)
+ * Returns: the flags of the device ( & @flag)
  */
 NMUnmanagedFlags
 nm_device_get_unmanaged_flags (NMDevice *self, NMUnmanagedFlags flag)
 {
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (flag != NM_UNMANAGED_NONE, FALSE);
+
 	return NM_DEVICE_GET_PRIVATE (self)->unmanaged_flags & flag;
 }
 
 /**
- * nm_device_get_default_unmanaged():
- * @self: the #NMDevice
+ * _set_unmanaged_flags:
+ * @self: the #NMDevice instance
+ * @flags: which #NMUnmanagedFlags to set.
+ * @set_op: whether to set/clear/forget the flags. You can also pass
+ *   boolean values %TRUE and %FALSE, which mean %NM_UNMAN_FLAG_OP_SET_UNMANAGED
+ *   and %NM_UNMAN_FLAG_OP_SET_MANAGED, respectively.
+ * @allow_state_transition: if %FALSE, setting flags never triggers a device
+ *   state change. If %TRUE, the device can change state, if it is real and
+ *   switches from managed to unmanaged (or vice versa).
+ * @reason: the device state reason passed to nm_device_state_changed() if
+ *   the device becomes managed/unmanaged. This is only relevant if the
+ *   device switches state and if @allow_state_transition is %TRUE.
  *
- * Returns: %TRUE if the device is by default unmanaged
- */
-static gboolean
-nm_device_get_default_unmanaged (NMDevice *self)
-{
-	return !!nm_device_get_unmanaged_flags (self, NM_UNMANAGED_DEFAULT);
-}
-
+ * Set the unmanaged flags of the device.
+ **/
 static void
 _set_unmanaged_flags (NMDevice *self,
                       NMUnmanagedFlags flags,
-                      gboolean unmanaged)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	if (unmanaged) {
-		if (!NM_FLAGS_ALL (priv->unmanaged_flags, flags)) {
-			_LOGD (LOGD_DEVICE, "unmanaged: flags set to 0x%0llx (was 0x%0llx, %s 0x%0llx)",
-			       (long long unsigned) (priv->unmanaged_flags | flags),
-			       (long long unsigned) priv->unmanaged_flags,
-			       "set",
-			       (long long unsigned) flags);
-			priv->unmanaged_flags |= flags;
-		}
-	} else {
-		if (NM_FLAGS_ANY (priv->unmanaged_flags, flags)) {
-			_LOGD (LOGD_DEVICE, "unmanaged: flags set to 0x%0llx (was 0x%0llx, %s 0x%0llx)",
-			       (long long unsigned) (priv->unmanaged_flags & (~flags)),
-			       (long long unsigned) priv->unmanaged_flags,
-			       "clear",
-			       (long long unsigned) flags);
-			priv->unmanaged_flags &= ~flags;
-		}
-	}
-}
-
-void
-nm_device_set_unmanaged_flags (NMDevice *self,
-                               NMUnmanagedFlags flag,
-                               gboolean unmanaged,
-                               NMDeviceStateReason reason)
+                      NMUnmanFlagOp set_op,
+                      gboolean allow_state_transition,
+                      NMDeviceStateReason reason)
 {
 	NMDevicePrivate *priv;
-	gboolean was_managed, now_managed;
+	gboolean was_managed, transition_state;
+	NMUnmanagedFlags old_flags, old_mask;
+	const char *operation = NULL;
+	char str1[512];
+	char str2[512];
 
 	g_return_if_fail (NM_IS_DEVICE (self));
-	g_return_if_fail (flag <= NM_UNMANAGED_LAST);
+	g_return_if_fail (flags);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	was_managed = nm_device_get_managed (self);
-	_set_unmanaged_flags (self, flag, unmanaged);
-	now_managed = nm_device_get_managed (self);
+	if (!priv->real)
+		allow_state_transition = FALSE;
+	was_managed = allow_state_transition && nm_device_get_managed (self, FALSE);
+
+	old_flags = priv->unmanaged_flags;
+	old_mask = priv->unmanaged_mask;
 
-	if (was_managed != now_managed) {
-		_LOGD (LOGD_DEVICE, "now %s", unmanaged ? "unmanaged" : "managed");
+	switch (set_op) {
+	case NM_UNMAN_FLAG_OP_FORGET:
+		priv->unmanaged_mask &= ~flags;
+		priv->unmanaged_flags &= ~flags;
+		operation = "forget";
+		break;
+	case NM_UNMAN_FLAG_OP_SET_UNMANAGED:
+		priv->unmanaged_mask |= flags;
+		priv->unmanaged_flags |= flags;
+		operation = "set-unmanaged";
+		break;
+	case NM_UNMAN_FLAG_OP_SET_MANAGED:
+		priv->unmanaged_mask |= flags;
+		priv->unmanaged_flags &= ~flags;
+		operation = "set-managed";
+		break;
+	default:
+		g_return_if_reached ();
+	}
 
-		g_object_notify (G_OBJECT (self), NM_DEVICE_MANAGED);
+	if (   old_flags == priv->unmanaged_flags
+	    && old_mask == priv->unmanaged_mask)
+		return;
 
-		if (unmanaged)
+	transition_state =    allow_state_transition
+	                   && was_managed != nm_device_get_managed (self, FALSE)
+	                   && (   was_managed
+	                       || (   !was_managed
+	                           && nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED));
+
+#define _FMTX "[%s%s0x%0x/0x%x/%s"
+#define _FMT(flags, mask, str) \
+	_unmanaged_flags2str ((flags), (mask), str, sizeof (str)), \
+	((flags) | (mask)) ? "=" : "", \
+	(flags), \
+	(mask), \
+	(_get_managed_by_flags (flags, mask, FALSE) \
+	     ? "managed" \
+	     : (_get_managed_by_flags (flags, mask, TRUE) \
+	            ? "manageable" \
+	            : "unmanaged"))
+	_LOGD (LOGD_DEVICE, "unmanaged: flags set to "_FMTX"%s, %s [%s=0x%0x]%s%s%s)",
+	       _FMT (priv->unmanaged_flags, priv->unmanaged_mask, str1),
+	       priv->real ? "" : "/unrealized",
+	       operation,
+	       nm_unmanaged_flags2str (flags, str2, sizeof (str2)),
+	       flags,
+	       NM_PRINT_FMT_QUOTED (allow_state_transition,
+	                            ", reason ",
+	                            reason_to_string (reason),
+	                            transition_state ? ", transition-state" : "",
+	                            ""));
+#undef _FMT
+
+	if (transition_state) {
+		if (was_managed)
 			nm_device_state_changed (self, NM_DEVICE_STATE_UNMANAGED, reason);
-		else if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED)
+		else
 			nm_device_state_changed (self, NM_DEVICE_STATE_UNAVAILABLE, reason);
 	}
 }
 
+/**
+ * @self: the #NMDevice instance
+ * @flags: which #NMUnmanagedFlags to set.
+ * @set_op: whether to set/clear/forget the flags. You can also pass
+ *   boolean values %TRUE and %FALSE, which mean %NM_UNMAN_FLAG_OP_SET_UNMANAGED
+ *   and %NM_UNMAN_FLAG_OP_SET_MANAGED, respectively.
+ *
+ * Set the unmanaged flags of the device (does not trigger a state change).
+ **/
+void
+nm_device_set_unmanaged_flags (NMDevice *self,
+                               NMUnmanagedFlags flags,
+                               NMUnmanFlagOp set_op)
+{
+	_set_unmanaged_flags (self, flags, set_op, FALSE, NM_DEVICE_STATE_REASON_NONE);
+}
+
+/**
+ * nm_device_set_unmanaged_by_flags:
+ * @self: the #NMDevice instance
+ * @flags: which #NMUnmanagedFlags to set.
+ * @set_op: whether to set/clear/forget the flags. You can also pass
+ *   boolean values %TRUE and %FALSE, which mean %NM_UNMAN_FLAG_OP_SET_UNMANAGED
+ *   and %NM_UNMAN_FLAG_OP_SET_MANAGED, respectively.
+ * @reason: the device state reason passed to nm_device_state_changed() if
+ *   the device becomes managed/unmanaged.
+ *
+ * Set the unmanaged flags of the device and possibly trigger a state change.
+ **/
+void
+nm_device_set_unmanaged_by_flags (NMDevice *self,
+                                  NMUnmanagedFlags flags,
+                                  NMUnmanFlagOp set_op,
+                                  NMDeviceStateReason reason)
+{
+	_set_unmanaged_flags (self, flags, set_op, TRUE, reason);
+}
+
 void
-nm_device_set_unmanaged_flags_by_device_spec (NMDevice *self, const GSList *unmanaged_specs)
+nm_device_set_unmanaged_by_user_config (NMDevice *self, const GSList *unmanaged_specs)
 {
 	NMDevicePrivate *priv;
 	gboolean unmanaged;
@@ -8811,46 +9295,36 @@ nm_device_set_unmanaged_flags_by_device_spec (NMDevice *self, const GSList *unma
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->managed_touched_by_user)
-		return;
-
 	unmanaged = nm_device_spec_match_list (self, unmanaged_specs);
-	nm_device_set_unmanaged_flags (self,
-	                               NM_UNMANAGED_USER,
-	                               unmanaged,
-	                               unmanaged
-	                                   ? NM_DEVICE_STATE_REASON_NOW_UNMANAGED
-	                                   : NM_DEVICE_STATE_REASON_NOW_MANAGED);
+
+	nm_device_set_unmanaged_by_flags (self,
+	                                  NM_UNMANAGED_USER_SETTINGS,
+	                                  unmanaged,
+	                                  unmanaged
+	                                      ? NM_DEVICE_STATE_REASON_NOW_UNMANAGED
+	                                      : NM_DEVICE_STATE_REASON_NOW_MANAGED);
 }
 
-/**
- * nm_device_set_unmanaged_flags_initial():
- * @self: the #NMDevice
- * @flag: an #NMUnmanagedFlag
- * @unmanaged: %TRUE or %FALSE to set or clear @flag
- *
- * Like nm_device_set_unmanaged_flags(), but must be set before the device is
- * initialized by nm_device_finish_init(), and does not trigger state changes.
- * Should only be used when initializing a device.
- */
 void
-nm_device_set_unmanaged_flags_initial (NMDevice *self,
-                                       NMUnmanagedFlags flag,
-                                       gboolean unmanaged)
+nm_device_set_unmanaged_by_user_udev (NMDevice *self)
 {
-	NMDevicePrivate *priv;
+	int ifindex;
+	gboolean platform_unmanaged = FALSE;
 
-	g_return_if_fail (NM_IS_DEVICE (self));
-	g_return_if_fail (flag <= NM_UNMANAGED_LAST);
+	ifindex = self->priv->ifindex;
 
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->initialized == FALSE);
+	if (   ifindex <= 0
+	    || !nm_platform_link_get_unmanaged (NM_PLATFORM_GET, ifindex, &platform_unmanaged))
+		return;
 
-	_set_unmanaged_flags (self, flag, unmanaged);
+	nm_device_set_unmanaged_by_flags (self,
+	                                  NM_UNMANAGED_USER_UDEV,
+	                                  platform_unmanaged,
+	                                  NM_DEVICE_STATE_REASON_USER_REQUESTED);
 }
 
 void
-nm_device_set_unmanaged_quitting (NMDevice *self)
+nm_device_set_unmanaged_by_quitting (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean need_deactivate = nm_device_is_activating (self) ||
@@ -8860,11 +9334,11 @@ nm_device_set_unmanaged_quitting (NMDevice *self)
 	if (need_deactivate)
 		_set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_NOW_UNMANAGED, TRUE);
 
-	nm_device_set_unmanaged_flags (self,
-	                               NM_UNMANAGED_INTERNAL,
-	                               TRUE,
-	                               need_deactivate ? NM_DEVICE_STATE_REASON_REMOVED
-	                                               : NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+	nm_device_set_unmanaged_by_flags (self,
+	                                  NM_UNMANAGED_QUITTING,
+	                                  TRUE,
+	                                  need_deactivate ? NM_DEVICE_STATE_REASON_REMOVED
+	                                                  : NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
 }
 
 /*****************************************************************************/
@@ -8901,6 +9375,7 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 	NMSettingConnection *s_con_applied;
 	const char *zone;
 	NMMetered metered;
+	guint64 version_id;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
@@ -8923,7 +9398,8 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 	if (g_strcmp0 ((zone = nm_setting_connection_get_zone (s_con_settings)),
 	               nm_setting_connection_get_zone (s_con_applied)) != 0) {
 
-		_LOGD (LOGD_DEVICE, "reapply setting: zone = %s%s%s", NM_PRINT_FMT_QUOTE_STRING (zone));
+		version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->priv->act_request);
+		_LOGD (LOGD_DEVICE, "reapply setting: zone = %s%s%s (version-id %llu)", NM_PRINT_FMT_QUOTE_STRING (zone), (long long unsigned) version_id);
 
 		g_object_set (G_OBJECT (s_con_applied),
 		              NM_SETTING_CONNECTION_ZONE, zone,
@@ -8934,7 +9410,8 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 
 	if ((metered = nm_setting_connection_get_metered (s_con_settings)) != nm_setting_connection_get_metered (s_con_applied)) {
 
-		_LOGD (LOGD_DEVICE, "reapply setting: metered = %d", (int) metered);
+		version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->priv->act_request);
+		_LOGD (LOGD_DEVICE, "reapply setting: metered = %d (version-id %llu)", (int) metered, (long long unsigned) version_id);
 
 		g_object_set (G_OBJECT (s_con_applied),
 		              NM_SETTING_CONNECTION_METERED, metered,
@@ -9017,7 +9494,7 @@ nm_device_update_metered (NMDevice *self)
 	if (value != priv->metered) {
 		_LOGD (LOGD_DEVICE, "set metered value %d", value);
 		priv->metered = value;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_METERED);
+		_notify (self, PROP_METERED);
 	}
 }
 
@@ -9029,11 +9506,21 @@ _nm_device_check_connection_available (NMDevice *self,
 {
 	NMDeviceState state;
 
+	/* an unrealized software device is always available, hardware devices never. */
+	if (!nm_device_is_real (self)) {
+		if (nm_device_is_software (self))
+			return nm_device_check_connection_compatible (self, connection);
+		return FALSE;
+	}
+
 	state = nm_device_get_state (self);
 	if (state < NM_DEVICE_STATE_UNMANAGED)
 		return FALSE;
 	if (   state < NM_DEVICE_STATE_UNAVAILABLE
-	    && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT))
+	    && (   (   !NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)
+	            && !nm_device_get_managed (self, FALSE))
+	        || (    NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)
+	            && !nm_device_get_managed (self, TRUE))))
 		return FALSE;
 	if (   state < NM_DEVICE_STATE_DISCONNECTED
 	    && !nm_device_is_software (self)
@@ -9100,34 +9587,30 @@ nm_device_check_connection_available (NMDevice *self,
 }
 
 static void
-_signal_available_connections_changed (NMDevice *self)
+available_connections_notify (NMDevice *self)
 {
-	g_object_notify (G_OBJECT (self), NM_DEVICE_AVAILABLE_CONNECTIONS);
+	_notify (self, PROP_AVAILABLE_CONNECTIONS);
 }
 
-static void
-_clear_available_connections (NMDevice *self, gboolean do_signal)
+static gboolean
+available_connections_del_all (NMDevice *self)
 {
-	g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (self)->available_connections);
-	if (do_signal == TRUE)
-		_signal_available_connections_changed (self);
+	if (g_hash_table_size (self->priv->available_connections) == 0)
+		return FALSE;
+	g_hash_table_remove_all (self->priv->available_connections);
+	return TRUE;
 }
 
 static gboolean
-_try_add_available_connection (NMDevice *self, NMConnection *connection)
+available_connections_add (NMDevice *self, NMConnection *connection)
 {
-	if (nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_NONE, NULL)) {
-		g_hash_table_add (NM_DEVICE_GET_PRIVATE (self)->available_connections,
-		                  g_object_ref (connection));
-		return TRUE;
-	}
-	return FALSE;
+	return nm_g_hash_table_add (self->priv->available_connections, g_object_ref (connection));
 }
 
 static gboolean
-_del_available_connection (NMDevice *self, NMConnection *connection)
+available_connections_del (NMDevice *self, NMConnection *connection)
 {
-	return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (self)->available_connections, connection);
+	return g_hash_table_remove (self->priv->available_connections, connection);
 }
 
 static gboolean
@@ -9162,22 +9645,55 @@ nm_device_recheck_available_connections (NMDevice *self)
 {
 	NMDevicePrivate *priv;
 	const GSList *connections, *iter;
+	gboolean changed = FALSE;
+	GHashTableIter h_iter;
+	NMConnection *connection;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE(self);
 
 	if (priv->con_provider) {
-		_clear_available_connections (self, FALSE);
+		gs_unref_hashtable GHashTable *prune_list = NULL;
+
+		if (g_hash_table_size (priv->available_connections) > 0) {
+			prune_list = g_hash_table_new (g_direct_hash, g_direct_equal);
+			g_hash_table_iter_init (&h_iter, priv->available_connections);
+			while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL))
+				g_hash_table_add (prune_list, connection);
+		}
 
 		connections = nm_connection_provider_get_connections (priv->con_provider);
-		for (iter = connections; iter; iter = g_slist_next (iter))
-			_try_add_available_connection (self, NM_CONNECTION (iter->data));
+		for (iter = connections; iter; iter = g_slist_next (iter)) {
+			connection = NM_CONNECTION (iter->data);
+
+			if (nm_device_check_connection_available (self,
+				                                  connection,
+				                                  NM_DEVICE_CHECK_CON_AVAILABLE_NONE,
+				                                  NULL)) {
+				if (available_connections_add (self, connection))
+					changed = TRUE;
+			} else {
+				if (prune_list && g_hash_table_remove (prune_list, connection))
+					changed = TRUE;
+			}
+		}
 
-		_signal_available_connections_changed (self);
+		if (prune_list) {
+			g_hash_table_iter_init (&h_iter, prune_list);
+			while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL)) {
+				if (available_connections_del (self, connection))
+					changed = TRUE;
+			}
+		}
+	} else {
+		if (available_connections_del_all (self))
+			changed = TRUE;
 	}
 
-	available_connection_check_delete_unrealized (self);
+	if (changed)
+		available_connections_notify (self);
+	available_connections_check_delete_unrealized (self);
 }
 
 /**
@@ -9209,7 +9725,7 @@ nm_device_get_available_connections (NMDevice *self, const char *specific_object
 			 * compatible with it.
 			 */
 			if (   !specific_object /* << Optimization: we know that the connection is available without @specific_object.  */
-			    || nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_NONE, specific_object))
+			    || nm_device_check_connection_available (self, connection, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, specific_object))
 				g_ptr_array_add (array, connection);
 		}
 	}
@@ -9217,45 +9733,38 @@ nm_device_get_available_connections (NMDevice *self, const char *specific_object
 }
 
 static void
-cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
+cp_connection_added_or_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
 {
+	gboolean changed;
 	NMDevice *self = user_data;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
 
-	if (_try_add_available_connection (self, connection))
-		_signal_available_connections_changed (self);
-}
-
-static void
-cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
-{
-	NMDevice *self = user_data;
-
-	g_return_if_fail (NM_IS_DEVICE (self));
+	if (nm_device_check_connection_available (self,
+	                                          connection,
+	                                          _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST,
+	                                          NULL))
+		changed = available_connections_add (self, connection);
+	else
+		changed = available_connections_del (self, connection);
 
-	if (_del_available_connection (self, connection)) {
-		_signal_available_connections_changed (self);
-		available_connection_check_delete_unrealized (self);
+	if (changed) {
+		available_connections_notify (self);
+		available_connections_check_delete_unrealized (self);
 	}
 }
 
 static void
-cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
+cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
 {
 	NMDevice *self = user_data;
-	gboolean added, deleted;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	/* FIXME: don't remove it from the hash if it's just going to get re-added */
-	deleted = _del_available_connection (self, connection);
-	added = _try_add_available_connection (self, connection);
-
-	/* Only signal if the connection was removed OR added, but not both */
-	if (added != deleted) {
-		_signal_available_connections_changed (self);
-		available_connection_check_delete_unrealized (self);
+	if (available_connections_del (self, connection)) {
+		available_connections_notify (self);
+		available_connections_check_delete_unrealized (self);
 	}
 }
 
@@ -9309,7 +9818,7 @@ nm_device_add_pending_action (NMDevice *self, const char *action, gboolean asser
 	_LOGD (LOGD_DEVICE, "add_pending_action (%d): '%s'", count, action);
 
 	if (count == 1)
-		g_object_notify (G_OBJECT (self), NM_DEVICE_HAS_PENDING_ACTION);
+		_notify (self, PROP_HAS_PENDING_ACTION);
 
 	return TRUE;
 }
@@ -9344,7 +9853,7 @@ nm_device_remove_pending_action (NMDevice *self, const char *action, gboolean as
 			g_free (iter->data);
 			priv->pending_actions = g_slist_delete_link (priv->pending_actions, iter);
 			if (priv->pending_actions == NULL)
-				g_object_notify (G_OBJECT (self), NM_DEVICE_HAS_PENDING_ACTION);
+				_notify (self, PROP_HAS_PENDING_ACTION);
 			return TRUE;
 		}
 		count++;
@@ -9453,6 +9962,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	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->ext_ip6_config_captured);
 	g_clear_object (&priv->wwan_ip6_config);
 	g_clear_object (&priv->ip6_config);
 
@@ -9466,7 +9976,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	/* Clear legacy IPv4 address property */
 	if (priv->ip4_address) {
 		priv->ip4_address = 0;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS);
+		_notify (self, PROP_IP4_ADDRESS);
 	}
 
 	if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
@@ -9733,11 +10243,11 @@ ip_config_valid (NMDeviceState state)
 static void
 notify_ip_properties (NMDevice *self)
 {
-	g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_CONFIG);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+	_notify (self, PROP_IP_IFACE);
+	_notify (self, PROP_IP4_CONFIG);
+	_notify (self, PROP_DHCP4_CONFIG);
+	_notify (self, PROP_IP6_CONFIG);
+	_notify (self, PROP_DHCP6_CONFIG);
 }
 
 static void
@@ -9840,14 +10350,16 @@ _set_state_full (NMDevice *self,
 	 * can retry device initialization.
 	 */
 	if (   (priv->state == state)
-	    && !(state == NM_DEVICE_STATE_UNAVAILABLE && priv->firmware_missing)) {
-		_LOGD (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d] (skip due to missing firmware)",
+	    && (   state != NM_DEVICE_STATE_UNAVAILABLE
+	        || !priv->firmware_missing)) {
+		_LOGD (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d]%s",
 		       state_to_string (old_state),
 		       state_to_string (state),
 		       reason_to_string (reason),
 		       old_state,
 		       state,
-		       reason);
+		       reason,
+		       priv->firmware_missing ? " (missing firmware)" : "");
 		return;
 	}
 
@@ -9875,13 +10387,13 @@ _set_state_full (NMDevice *self,
 	req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
 
 	if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
-		_clear_available_connections (self, TRUE);
+		if (available_connections_del_all (self))
+			available_connections_notify (self);
 		_clear_queued_act_request (priv);
 	}
 
 	/* Update the available connections list when a device first becomes available */
-	if (   (state >= NM_DEVICE_STATE_DISCONNECTED && old_state < NM_DEVICE_STATE_DISCONNECTED)
-	    || nm_device_get_default_unmanaged (self))
+	if (state >= NM_DEVICE_STATE_DISCONNECTED && old_state < NM_DEVICE_STATE_DISCONNECTED)
 		nm_device_recheck_available_connections (self);
 
 	/* Handle the new state here; but anything that could trigger
@@ -9963,8 +10475,8 @@ _set_state_full (NMDevice *self,
 	    && state <= NM_DEVICE_STATE_ACTIVATED)
 		nm_device_set_autoconnect (self, TRUE);
 
-	g_object_notify (G_OBJECT (self), NM_DEVICE_STATE);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_STATE_REASON);
+	_notify (self, PROP_STATE);
+	_notify (self, PROP_STATE_REASON);
 	g_signal_emit_by_name (self, NM_DEVICE_STATE_CHANGED, state, old_state, reason);
 
 	/* Post-process the event after internal notification */
@@ -9982,11 +10494,7 @@ _set_state_full (NMDevice *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");
-			else if (   old_state > NM_DEVICE_STATE_UNAVAILABLE
-			         && nm_device_get_default_unmanaged (self))
-				nm_device_queue_state (self, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
+			_LOGD (LOGD_DEVICE, "device not yet available for transition to DISCONNECTED");
 		}
 		break;
 	case NM_DEVICE_STATE_DEACTIVATING:
@@ -10032,9 +10540,6 @@ _set_state_full (NMDevice *self,
 				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:
 		_LOGI (LOGD_DEVICE, "Activation: successful, device activated.");
@@ -10149,6 +10654,9 @@ _set_state_full (NMDevice *self,
 		g_object_unref (req);
 
 	priv->in_state_changed = FALSE;
+
+	if ((old_state > NM_DEVICE_STATE_UNMANAGED) != (state > NM_DEVICE_STATE_UNMANAGED))
+		_notify (self, PROP_MANAGED);
 }
 
 void
@@ -10297,7 +10805,7 @@ nm_device_update_hw_address (NMDevice *self)
 			priv->hw_addr = nm_utils_hwaddr_ntoa (hwaddr, hwaddrlen);
 
 			_LOGD (LOGD_HW | LOGD_DEVICE, "hardware address now %s", priv->hw_addr);
-			g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS);
+			_notify (self, PROP_HW_ADDRESS);
 		}
 	} else {
 		/* Invalid or no hardware address */
@@ -10306,7 +10814,7 @@ nm_device_update_hw_address (NMDevice *self)
 			priv->hw_addr_len = 0;
 			_LOGD (LOGD_HW | LOGD_DEVICE,
 			       "previous hardware address is no longer valid");
-			g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS);
+			_notify (self, PROP_HW_ADDRESS);
 		}
 	}
 }
@@ -10347,7 +10855,11 @@ nm_device_set_hw_addr (NMDevice *self, const char *addr,
 	const char *cur_addr = nm_device_get_hw_address (self);
 	guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX];
 
-	g_return_val_if_fail (addr != NULL, FALSE);
+	/* Fall back to the permanent address */
+	if (!addr)
+		addr = priv->perm_hw_addr;
+	if (!addr)
+		return FALSE;
 
 	/* Do nothing if current MAC is same */
 	if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) {
@@ -10498,7 +11010,8 @@ nm_device_init (NMDevice *self)
 	priv->dhcp_timeout = 0;
 	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
 	priv->autoconnect = DEFAULT_AUTOCONNECT;
-	priv->unmanaged_flags = NM_UNMANAGED_INTERNAL;
+	priv->unmanaged_flags = NM_UNMANAGED_PLATFORM_INIT;
+	priv->unmanaged_mask = priv->unmanaged_flags;
 	priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
 	priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
 
@@ -10533,7 +11046,7 @@ constructor (GType type,
 
 		if (pllink && link_type_compatible (self, pllink->type, NULL, NULL)) {
 			priv->ifindex = pllink->ifindex;
-			priv->up = NM_FLAGS_HAS (pllink->flags, IFF_UP);
+			priv->up = NM_FLAGS_HAS (pllink->n_ifi_flags, IFF_UP);
 		}
 	}
 
@@ -10563,7 +11076,7 @@ constructed (GObject *object)
 	g_assert (priv->con_provider);
 	g_signal_connect (priv->con_provider,
 	                  NM_CP_SIGNAL_CONNECTION_ADDED,
-	                  G_CALLBACK (cp_connection_added),
+	                  G_CALLBACK (cp_connection_added_or_updated),
 	                  self);
 
 	g_signal_connect (priv->con_provider,
@@ -10573,18 +11086,9 @@ constructed (GObject *object)
 
 	g_signal_connect (priv->con_provider,
 	                  NM_CP_SIGNAL_CONNECTION_UPDATED,
-	                  G_CALLBACK (cp_connection_updated),
+	                  G_CALLBACK (cp_connection_added_or_updated),
 	                  self);
 
-	/* Update default-unmanaged device available connections immediately,
-	 * since they don't transition from UNMANAGED (and thus the state handler
-	 * doesn't run and update them) until something external happens.
-	 */
-	if (nm_device_get_default_unmanaged (self)) {
-		nm_device_set_autoconnect (self, FALSE);
-		nm_device_recheck_available_connections (self);
-	}
-
 	G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
 
 	_LOGD (LOGD_DEVICE, "constructed (%s)", G_OBJECT_TYPE_NAME (self));
@@ -10599,6 +11103,11 @@ dispose (GObject *object)
 
 	_LOGD (LOGD_DEVICE, "disposing");
 
+	g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy);
+	priv->arping.dad_list = NULL;
+
+	arp_cleanup (self);
+
 	g_signal_handlers_disconnect_by_func (nm_config_get (), config_changed_update_ignore_carrier, self);
 
 	dispatcher_cleanup (self);
@@ -10623,13 +11132,12 @@ dispose (GObject *object)
 	link_disconnect_action_cancel (self);
 
 	if (priv->con_provider) {
-		g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_added, self);
+		g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_added_or_updated, self);
 		g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_removed, self);
-		g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_updated, self);
 		priv->con_provider = NULL;
 	}
 
-	g_hash_table_remove_all (priv->available_connections);
+	available_connections_del_all (self);
 
 	nm_clear_g_source (&priv->carrier_wait_id);
 
@@ -10698,7 +11206,6 @@ set_property (GObject *object, guint prop_id,
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const char *hw_addr, *p;
 	guint count;
-	gboolean val_bool;
 
 	switch (prop_id) {
 	case PROP_UDI:
@@ -10732,18 +11239,26 @@ set_property (GObject *object, guint prop_id,
 	case PROP_IP4_ADDRESS:
 		priv->ip4_address = g_value_get_uint (value);
 		break;
-	case PROP_MANAGED:
-		val_bool = g_value_get_boolean (value);
-		priv->managed_touched_by_user = TRUE;
-		nm_device_set_unmanaged_flags (self,
-		                               NM_UNMANAGED_USER | (val_bool ? NM_UNMANAGED_DEFAULT : NM_UNMANAGED_NONE),
-		                               !val_bool,
-		                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
+	case PROP_MANAGED: {
+		gboolean managed;
+		NMDeviceStateReason reason;
+
+		managed = g_value_get_boolean (value);
+		if (managed)
+			reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED;
+		else
+			reason = NM_DEVICE_STATE_REASON_REMOVED;
+		nm_device_set_unmanaged_by_flags (self,
+		                                  NM_UNMANAGED_USER_EXPLICIT,
+		                                  !managed,
+		                                  reason);
 		break;
+	}
 	case PROP_AUTOCONNECT:
 		nm_device_set_autoconnect (self, g_value_get_boolean (value));
 		break;
 	case PROP_FIRMWARE_MISSING:
+		/* construct only */
 		priv->firmware_missing = g_value_get_boolean (value);
 		break;
 	case PROP_NM_PLUGIN_MISSING:
@@ -10878,7 +11393,8 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->link_type);
 		break;
 	case PROP_MANAGED:
-		g_value_set_boolean (value, nm_device_get_managed (self));
+		/* The managed state exposed on D-Bus only depends on the current device state alone. */
+		g_value_set_boolean (value, nm_device_get_state (self) > NM_DEVICE_STATE_UNMANAGED);
 		break;
 	case PROP_AUTOCONNECT:
 		g_value_set_boolean (value, priv->autoconnect);
@@ -11002,232 +11518,169 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->get_ip_iface_identifier = get_ip_iface_identifier;
 
 	/* Properties */
-	g_object_class_install_property
-		(object_class, PROP_UDI,
-		 g_param_spec_string (NM_DEVICE_UDI, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_IFACE,
-		 g_param_spec_string (NM_DEVICE_IFACE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_IP_IFACE,
-		 g_param_spec_string (NM_DEVICE_IP_IFACE, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_DRIVER,
-		 g_param_spec_string (NM_DEVICE_DRIVER, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_DRIVER_VERSION,
-		 g_param_spec_string (NM_DEVICE_DRIVER_VERSION, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_FIRMWARE_VERSION,
-		 g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_CAPABILITIES,
-		 g_param_spec_uint (NM_DEVICE_CAPABILITIES, "", "",
-		                    0, G_MAXUINT32, NM_DEVICE_CAP_NONE,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_CARRIER,
-		 g_param_spec_boolean (NM_DEVICE_CARRIER, "", "",
-		                       FALSE,
-		                       G_PARAM_READABLE |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_DEVICE_MTU, "", "",
-		                    0, G_MAXUINT32, 1500,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_IP4_ADDRESS,
-		 g_param_spec_uint (NM_DEVICE_IP4_ADDRESS, "", "",
-		                    0, G_MAXUINT32, 0, /* FIXME */
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_IP4_CONFIG,
-		 g_param_spec_string (NM_DEVICE_IP4_CONFIG, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_DHCP4_CONFIG,
-		 g_param_spec_string (NM_DEVICE_DHCP4_CONFIG, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_IP6_CONFIG,
-		 g_param_spec_string (NM_DEVICE_IP6_CONFIG, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_DHCP6_CONFIG,
-		 g_param_spec_string (NM_DEVICE_DHCP6_CONFIG, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_STATE,
-		 g_param_spec_uint (NM_DEVICE_STATE, "", "",
-		                    0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_STATE_REASON,
-		 g_param_spec_variant (NM_DEVICE_STATE_REASON, "", "",
-		                       G_VARIANT_TYPE ("(uu)"),
-		                       NULL,
-		                       G_PARAM_READABLE |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_ACTIVE_CONNECTION,
-		 g_param_spec_string (NM_DEVICE_ACTIVE_CONNECTION, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_DEVICE_TYPE,
-		 g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, "", "",
-		                    0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_LINK_TYPE,
-		 g_param_spec_uint (NM_DEVICE_LINK_TYPE, "", "",
-		                    0, G_MAXUINT32, NM_LINK_TYPE_NONE,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_MANAGED,
-		 g_param_spec_boolean (NM_DEVICE_MANAGED, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_AUTOCONNECT,
-		 g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, "", "",
-		                       DEFAULT_AUTOCONNECT,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_FIRMWARE_MISSING,
-		 g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_NM_PLUGIN_MISSING,
-		 g_param_spec_boolean (NM_DEVICE_NM_PLUGIN_MISSING, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_TYPE_DESC,
-		 g_param_spec_string (NM_DEVICE_TYPE_DESC, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_RFKILL_TYPE,
-		 g_param_spec_uint (NM_DEVICE_RFKILL_TYPE, "", "",
-		                    RFKILL_TYPE_WLAN,
-		                    RFKILL_TYPE_MAX,
-		                    RFKILL_TYPE_UNKNOWN,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_IFINDEX,
-		 g_param_spec_int (NM_DEVICE_IFINDEX, "", "",
-		                   0, G_MAXINT, 0,
-		                   G_PARAM_READABLE |
-		                   G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_AVAILABLE_CONNECTIONS,
-		 g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_PHYSICAL_PORT_ID,
-		 g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_IS_MASTER,
-		 g_param_spec_boolean (NM_DEVICE_IS_MASTER, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_MASTER,
-		 g_param_spec_object (NM_DEVICE_MASTER, "", "",
-		                      NM_TYPE_DEVICE,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_HW_ADDRESS,
-		 g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_HAS_PENDING_ACTION,
-		 g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, "", "",
-		                       FALSE,
-		                       G_PARAM_READABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_UDI] =
+	    g_param_spec_string (NM_DEVICE_UDI, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IFACE] =
+	    g_param_spec_string (NM_DEVICE_IFACE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IP_IFACE] =
+	    g_param_spec_string (NM_DEVICE_IP_IFACE, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DRIVER] =
+	    g_param_spec_string (NM_DEVICE_DRIVER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DRIVER_VERSION] =
+	    g_param_spec_string (NM_DEVICE_DRIVER_VERSION, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_FIRMWARE_VERSION] =
+	    g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_CAPABILITIES] =
+	    g_param_spec_uint (NM_DEVICE_CAPABILITIES, "", "",
+	                       0, G_MAXUINT32, NM_DEVICE_CAP_NONE,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_CARRIER] =
+	    g_param_spec_boolean (NM_DEVICE_CARRIER, "", "",
+	                          FALSE,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_DEVICE_MTU, "", "",
+	                       0, G_MAXUINT32, 1500,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IP4_ADDRESS] =
+	    g_param_spec_uint (NM_DEVICE_IP4_ADDRESS, "", "",
+	                       0, G_MAXUINT32, 0, /* FIXME */
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IP4_CONFIG] =
+	    g_param_spec_string (NM_DEVICE_IP4_CONFIG, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DHCP4_CONFIG] =
+	    g_param_spec_string (NM_DEVICE_DHCP4_CONFIG, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IP6_CONFIG] =
+	    g_param_spec_string (NM_DEVICE_IP6_CONFIG, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DHCP6_CONFIG] =
+	    g_param_spec_string (NM_DEVICE_DHCP6_CONFIG, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_STATE] =
+	    g_param_spec_uint (NM_DEVICE_STATE, "", "",
+	                       0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_STATE_REASON] =
+	    g_param_spec_variant (NM_DEVICE_STATE_REASON, "", "",
+	                          G_VARIANT_TYPE ("(uu)"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_ACTIVE_CONNECTION] =
+	    g_param_spec_string (NM_DEVICE_ACTIVE_CONNECTION, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DEVICE_TYPE] =
+	    g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, "", "",
+	                       0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
+	                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_LINK_TYPE] =
+	    g_param_spec_uint (NM_DEVICE_LINK_TYPE, "", "",
+	                       0, G_MAXUINT32, NM_LINK_TYPE_NONE,
+	                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_MANAGED] =
+	    g_param_spec_boolean (NM_DEVICE_MANAGED, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_AUTOCONNECT] =
+	    g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, "", "",
+	                          DEFAULT_AUTOCONNECT,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_FIRMWARE_MISSING] =
+	    g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_NM_PLUGIN_MISSING] =
+	    g_param_spec_boolean (NM_DEVICE_NM_PLUGIN_MISSING, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_TYPE_DESC] =
+	    g_param_spec_string (NM_DEVICE_TYPE_DESC, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_RFKILL_TYPE] =
+	    g_param_spec_uint (NM_DEVICE_RFKILL_TYPE, "", "",
+	                       RFKILL_TYPE_WLAN,
+	                       RFKILL_TYPE_MAX,
+	                       RFKILL_TYPE_UNKNOWN,
+	                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IFINDEX] =
+	    g_param_spec_int (NM_DEVICE_IFINDEX, "", "",
+	                      0, G_MAXINT, 0,
+	                      G_PARAM_READABLE |
+	                      G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_AVAILABLE_CONNECTIONS] =
+	    g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_PHYSICAL_PORT_ID] =
+	    g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IS_MASTER] =
+	    g_param_spec_boolean (NM_DEVICE_IS_MASTER, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_MASTER] =
+	    g_param_spec_object (NM_DEVICE_MASTER, "", "",
+	                         NM_TYPE_DEVICE,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_HW_ADDRESS] =
+	    g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_HAS_PENDING_ACTION] =
+	    g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, "", "",
+	                          FALSE,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMDevice:metered:
@@ -11236,99 +11689,95 @@ nm_device_class_init (NMDeviceClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_METERED,
-		 g_param_spec_uint (NM_DEVICE_METERED, "", "",
-		                    0, G_MAXUINT32, NM_METERED_UNKNOWN,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_LLDP_NEIGHBORS,
-		 g_param_spec_variant (NM_DEVICE_LLDP_NEIGHBORS, "", "",
-		                       G_VARIANT_TYPE ("aa{sv}"),
-		                       NULL,
-		                       G_PARAM_READABLE |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_REAL,
-		 g_param_spec_boolean (NM_DEVICE_REAL, "", "",
-		                       FALSE,
-		                       G_PARAM_READABLE |
-		                       G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_SLAVES,
-	     g_param_spec_boxed (NM_DEVICE_SLAVES, "", "",
-	                         G_TYPE_STRV,
-	                         G_PARAM_READABLE |
-	                         G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_METERED] =
+	    g_param_spec_uint (NM_DEVICE_METERED, "", "",
+	                       0, G_MAXUINT32, NM_METERED_UNKNOWN,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_LLDP_NEIGHBORS] =
+	    g_param_spec_variant (NM_DEVICE_LLDP_NEIGHBORS, "", "",
+	                          G_VARIANT_TYPE ("aa{sv}"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_REAL] =
+	    g_param_spec_boolean (NM_DEVICE_REAL, "", "",
+	                          FALSE,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_SLAVES] =
+	    g_param_spec_boxed (NM_DEVICE_SLAVES, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	/* Signals */
 	signals[STATE_CHANGED] =
-		g_signal_new (NM_DEVICE_STATE_CHANGED,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_LAST,
-		              G_STRUCT_OFFSET (NMDeviceClass, state_changed),
-		              NULL, NULL, NULL,
-		              G_TYPE_NONE, 3,
-		              G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+	    g_signal_new (NM_DEVICE_STATE_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_LAST,
+	                  G_STRUCT_OFFSET (NMDeviceClass, state_changed),
+	                  NULL, NULL, NULL,
+	                  G_TYPE_NONE, 3,
+	                  G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
 
 	signals[AUTOCONNECT_ALLOWED] =
-		g_signal_new ("autoconnect-allowed",
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_LAST,
-		              0,
-		              autoconnect_allowed_accumulator, NULL, NULL,
-		              G_TYPE_BOOLEAN, 0);
+	    g_signal_new ("autoconnect-allowed",
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_LAST,
+	                  0,
+	                  autoconnect_allowed_accumulator, NULL, NULL,
+	                  G_TYPE_BOOLEAN, 0);
 
 	signals[AUTH_REQUEST] =
-		g_signal_new (NM_DEVICE_AUTH_REQUEST,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              /* context, connection, permission, allow_interaction, callback, user_data */
-		              G_TYPE_NONE, 6, G_TYPE_DBUS_METHOD_INVOCATION, NM_TYPE_CONNECTION, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER);
+	    g_signal_new (NM_DEVICE_AUTH_REQUEST,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  /* context, connection, permission, allow_interaction, callback, user_data */
+	                  G_TYPE_NONE, 6, G_TYPE_DBUS_METHOD_INVOCATION, NM_TYPE_CONNECTION, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER);
 
 	signals[IP4_CONFIG_CHANGED] =
-		g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
+	    g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
 
 	signals[IP6_CONFIG_CHANGED] =
-		g_signal_new (NM_DEVICE_IP6_CONFIG_CHANGED,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
+	    g_signal_new (NM_DEVICE_IP6_CONFIG_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
 
 	signals[REMOVED] =
-		g_signal_new (NM_DEVICE_REMOVED,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 0);
+	    g_signal_new (NM_DEVICE_REMOVED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
 
 	signals[RECHECK_AUTO_ACTIVATE] =
-		g_signal_new (NM_DEVICE_RECHECK_AUTO_ACTIVATE,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 0);
+	    g_signal_new (NM_DEVICE_RECHECK_AUTO_ACTIVATE,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
 
 	signals[RECHECK_ASSUME] =
-		g_signal_new (NM_DEVICE_RECHECK_ASSUME,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 0);
+	    g_signal_new (NM_DEVICE_RECHECK_ASSUME,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
 
 	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
 	                                        NMDBUS_TYPE_DEVICE_SKELETON,
 	                                        "Reapply", impl_device_reapply,
+	                                        "GetAppliedConnection", impl_device_get_applied_connection,
 	                                        "Disconnect", impl_device_disconnect,
 	                                        "Delete", impl_device_delete,
 	                                        NULL);